【线性表2】线性表的顺序实现:顺序表

顺序表简介

特点:使用一组地址连续的存储单元依次存储表中的数据元素,常见的就是使用数组去实现。

表中逻辑相邻的数据元素,在物理内存上也相邻。

顺序表中的任意数据元素都可随机访问,是一种支持随机访问,长度自动动态调整的线性表结构。

优点:访问表中的元素很快,时间复杂度为O(1)

缺点:插入,删除元素需要移动大量的元素,时间复杂度为O(n) 。

因此如果我们在编程中需要这样一种线性表数据结构:构造后对元素的访问操作很频繁,而很少进行增,删等元素位置的调整操作,那么就可以考虑使用顺序表。

代码实现

#include<iostream>
#include<stdexcept>
#include<cstdlib>
using namespace std;
class ArrayList
{
private:
     enum{ INCREMENT_SIZE = 20,    //容量不足时,每次增加10个
           INIT_CAPACITY = 10      //初始容量20
         };
     int   size;                  //实际元素个数
     int   capacity;              //容量
     int*  elements;              //存储元素的数组的基地址
    //确保表的容量至少为 reqCapacity 个。否则就增加容量 。
     void ensureCapacity(int reqCapacity)
     {
         int* nelements;
         if(capacity<reqCapacity)
         {
             nelements = (int*)realloc(elements,(capacity+INCREMENT_SIZE)*sizeof(int));

             if(nelements!=0)   //如果对动态数组的大小调整成功
             {
               elements = nelements;
               capacity+=INCREMENT_SIZE;
             }
             /*
              else{            //调整失败
                 exit(-1);
             }
             */
         }
     }

public:
    ArrayList():size(0),capacity(INIT_CAPACITY),elements(0)
    {
        elements = (int*)malloc(sizeof(int)*INIT_CAPACITY);
    }
    ~ArrayList()
    {
        free(elements);
    }
    /*
    * 功能:在表末尾追加元素
    */
    void append(int e)
    {
        ensureCapacity(size+1);
        elements[size++]  = e;
    }

    /*
    * 将新元素e插入为索引为index
    * 合法的index值为  [0,size] ,当index为size时,不需要移动元素,当index为0时
    * 需要移动所有元素。
    *
    **/
    bool insert(int index,int e)
    {
        if(index>size || index < 0) return false;  //索引不合法
        ensureCapacity(size+1);

        for(int i=size-1;i>=index;--i)
        {
            elements[i+1] = elements[i];
        }   

          elements[index] = e;
        size++;
         return true;

    }

    /*
    * 删除索引为index 的元素 。
    * 合法的index值为  [0,size-1] ,当index=0时,删除的是第一个元素。
    * 当index为size-1时,删除的是最后一个元素
    **/
    bool remove(int index)
    {
        if(index>=size || index <0) return false;    //索引不合法
        for(int i=index;i<size-1;++i)
        {
            elements[i] = elements[i+1];
        }
        size --;
        return true;
    }

    /*
    * 功能:查找一个元素在表中的索引,如果不存在此元素,则返回-1
    */
    int indexOf(int e) const
    {
        for(int i=0;i<size;++i)
        {
            if(elements[i] == e)
                return i;
        }
        return -1;
    }

    /*
    * 功能:判断表中是否包含某个元素。
    */
    bool contains(int e) const
    {
        return (-1 != indexOf(e));
    }

    int length() const
    {
        return size;
    }

    void clear()
    {
        size = 0;
    }

    bool isEmpty()const
    {
        return size==0;
    }
    void show()const
    {
        cout<<"[";
        for(int i=0;i<size;++i)
        {
            if(i!=0) cout<<‘,‘;
            cout<<elements[i];
        }
        cout<<"]";
    }
    //重载索引运算符[],元素只读版本
    int operator[](int index) const
    {
        if(index >=size || index < 0)          //如果索引不合法 ,则抛异常
            throw std::out_of_range(0);
        return elements[index];
    }
    //重载索引运算符[],元素可读可写版本
    int& operator[](int index)
    {
        if(index >=size || index < 0)
            throw std::out_of_range(0);
        return elements[index];
    }
}; //end class

int main()
{
    ArrayList list1;

    list1.append(1);
    list1.append(2);
    list1.append(5);

    list1.insert(0,100);

    list1.remove(2);

    list1.show();
    return 0;
}

顺序表的短板

插入元素,时间复杂度 O(n)

插入为 第 i  个 元素,则需要移动  n - i +1  个数据元素. 需要移动 第 n  到第 i 个 元素。

均值的计算:  一共为   (n+1)(n+0)  / 2 ,因为一共计算插了 n+1 个位置。则均值为 :  n / 2


插为第 i 个 元素

1 2 ... n+1
移动 元素的个数       n n-1 ... 0

删除元素,时间复杂度 O(n)

删除 第 i  个 元素,则需要移动 n - i  个数据元素 。 需要移动 第   i+ 1  到 第  n 个 元素。

均值的计算:一共为   (n)(n-1+0)  / 2 ,因为一共计算删除 n  个位置。则均值为 :  (n-1) / 2


删除第 i 个 元素

1 2 ... n
移动 元素的个数     n-1 n-2 ... 0

小提示

1、一般在实际开发时,为了尽量避免移动元素的开销,都会使用贴近硬件的API去完成内存数据的移动,而不是使用循环。例如使用memmove函数。

2、当内部数组的容量不够时,需要重新调整数组的大小,上面的例子我们使用了realloc函数去实现,且每次增加20。然而我们必须认识到,调整大小是很销耗资源的一个操作,因此在实际开发时,我们必须做出明智的容量增长策略。例如:Java中的ArrayList每次将容量扩展为原来的1.5倍。

编程语言中的实现类 增长因子
Java ArrayList 1.5 (3/2)
Python PyListObject ~1.125 (n + n >> 3)
VC++ 2013 1.5 (3/2)
G++ 5.2.0 2
Clang 3.6 2

编程练习

将2个非递减排序的顺序表合并为1个表,且新表也保持非递减排序。

如  [1,56,88 ]  和 [ 2,75] 合并后为 [ 1,2,56,75,88  ]

#include<iostream>
#include<stdexcept>
#include<cstdlib>
using namespace std;
class ArrayList
{
private:
     enum{ INCREMENT_SIZE = 20,    //容量不足时,每次增加10个
           INIT_CAPACITY = 10      //初始容量20
         };
     int   size;                  //实际元素个数
     int   capacity;              //容量
     int*  elements;              //存储元素的数组的基地址
    //确保表的容量至少为 reqCapacity 个。否则就增加容量 。
     void ensureCapacity(int reqCapacity)
     {
         int* nelements;
         if(capacity<reqCapacity)
         {
             nelements = (int*)realloc(elements,(capacity+INCREMENT_SIZE)*sizeof(int));

             if(nelements!=0)   //如果对动态数组的大小调整成功
             {
               elements = nelements;
               capacity+=INCREMENT_SIZE;
             }
             /*
              else{            //调整失败
                 exit(-1);
             }
             */
         }
     }

public:
    ArrayList():size(0),capacity(INIT_CAPACITY),elements(0)
    {
        elements = (int*)malloc(sizeof(int)*INIT_CAPACITY);
    }
    ~ArrayList()
    {
        free(elements);
    }
    /*
    * 功能:在表末尾追加元素
    */
    void append(int e)
    {
        ensureCapacity(size+1);
        elements[size++]  = e;
    }

    /*
    * 将新元素e插入为索引为index
    * 合法的index值为  [0,size] ,当index为size时,不需要移动元素,当index为0时
    * 需要移动所有元素。
    *
    **/
    bool insert(int index,int e)
    {
        if(index>size || index < 0) return false;  //索引不合法
        ensureCapacity(size+1);

        for(int i=size-1;i>=index;--i)
        {
            elements[i+1] = elements[i];
        }   

          elements[index] = e;
        size++;
         return true;

    }

    /*
    * 删除索引为index 的元素 。
    * 合法的index值为  [0,size-1] ,当index=0时,删除的是第一个元素。
    * 当index为size-1时,删除的是最后一个元素
    **/
    bool remove(int index)
    {
        if(index>=size || index <0) return false;    //索引不合法
        for(int i=index;i<size-1;++i)
        {
            elements[i] = elements[i+1];
        }
        size --;
        return true;
    }

    /*
    * 功能:查找一个元素在表中的索引,如果不存在此元素,则返回-1
    */
    int indexOf(int e) const
    {
        for(int i=0;i<size;++i)
        {
            if(elements[i] == e)
                return i;
        }
        return -1;
    }

    /*
    * 功能:判断表中是否包含某个元素。
    */
    bool contains(int e) const
    {
        return (-1 != indexOf(e));
    }

    int length() const
    {
        return size;
    }

    void clear()
    {
        size = 0;
    }

    bool isEmpty()const
    {
        return size==0;
    }
    void show()const
    {
        cout<<"[";
        for(int i=0;i<size;++i)
        {
            if(i!=0) cout<<‘,‘;
            cout<<elements[i];
        }
        cout<<"]";
    }
    //重载索引运算符[],元素只读版本
    int operator[](int index) const
    {
        if(index >=size || index < 0)          //如果索引不合法 ,则抛异常
            throw std::out_of_range(0);
        return elements[index];
    }
    //重载索引运算符[],元素可读可写版本
    int& operator[](int index)
    {
        if(index >=size || index < 0)
            throw std::out_of_range(0);
        return elements[index];
    }
}; //end class
/*
* 功能:将2个非递减排序的顺序表合并为1个表re,且re表也保持非递减排序
*/
void MergeList(const ArrayList& list1,const ArrayList& list2,ArrayList&re)
{
    int i=0,j=0;    //用于访问list1和list2的索引
    int k=0;        //访问re的索引
    int e1,e2;      //保存从list1和list2中提出的元素

    while(i<list1.length() && j<list2.length())
    {
        e1 = list1[i];
        e2 = list2[j];
        if(e1 <= e2)      //比较大小,将小的元素加入到re表中。
        {
            re.append(e1);
            i++;
        }
        else
        {
            re.append(e2);
            j++;
        }
        k++;
    }    

    //如果list1还没访问完
    while(i<list1.length())
    {
        re.append( list1[i] );
        i++;
        k++;
    }
    //如果list2还没访问完
    while(j<list2.length())
    {
        re.append( list2[j] );
        j++;
        k++;
    }

}
int main()
{
    ArrayList list1;
    ArrayList list2;
    ArrayList re;
    list1.append(12);
    list1.append(35);
    list1.append(88);

    list2.append(1);
    list2.append(2);
    list2.append(7);
    list2.append(82);
    list2.append(101);

    MergeList(list1,list2,re);

    re.show();

    return 0;
}

时间: 2024-12-14 18:06:58

【线性表2】线性表的顺序实现:顺序表的相关文章

数据结构-02 _用顺序表解决线性表的编程问题

看到这个标题,相必最先应该只到什么是顺序表,什么是线性表. 线性表(linear list):由n(n>=0)个相同的数据类型的数据元素(结点)a0,a1,a2,...an-1 组成的有限序列. 顺序表:把线性表的结构按照逻辑顺序存放在一组地址连续的存储单元里,用这种方式存储的线性表简称顺序表. 线性表的基本操作: 1.初始化操作 2.插入操作:InsertNode(T a,int i) 在线性表的第i个位置插入一个值为a的新元素,使得原序号为i,i+1,...,n 的数据元素的序号变成i+1,

广义表(线性表的推广)

广义表的定义 广义表是线性表的推广. 广义表一般记作LS=(d0,d1,...dn-1) 其中,LS是广义表(d1,d2,...dn)的名称,n是它的长度.在线性表的定义中,ai(1<=i<=n)只限于是单个元素.而在广义表的定义中,di可以是单个元素.也可以是广义表,分别称为广义表LS的单元素和子表.习惯上,用大写字母表示广义表的名称,用小写字母表示单元素.当广义表LS非空时,称第一个元素d0为表头(Head),称其余元素组成的表(d1,d2,...dn-1)是LS的表尾(Tail). 显然

考研数据结构-顺序表(综合应用3)-合并顺序表

本节代码主要来自王道单科18页的综合应用题. 七.将两个有序顺序表合并成一个新的有序顺序表,并由函数返回结果顺序表 易忘点:合并以前需要先判断一下是否大于C的最大长度. 核心代码: bool merge(Sqlist A,Sqlist B,Sqlist &C){ if(A.length+B.length>MaxSize) return false; //容易忘记 int i=0,j=0,k=0; while(i<=A.length-1&&j<=B.length-1

WEB 页面 控制表单内tab键切换的顺序

在Html代码中有一个键盘属性——tabindex,它可以设置访问者在页面中按tab键的顺序.如下: <input type="button" id="b1" tabIndex="1" value="Button1" /> <input type="button" id="b2" tabIndex="2" value="Button2&qu

Oracle 执行顺序 及 驱动表和被驱动表

oracle驱动表以及如何确定驱动表 驱动表普遍认为是由SQL语句的写法决定的,简单的说,就是FROM语句后面的表列表中的最后一个.由于SQL语句是从后向前进行分析,Oracle会根据FROM语句从后到前将各个表依次连接起来. 首先理解执行顺序 先从最开头一直往右看,直到看到最右边的并列的地方,对于不并列的,靠右的先执行:对于并列的,靠上的先执行.  即并列的缩进,从上往下执行,非并列的缩进块,从下往上执行. 1.    如果所连接的表A和B,A表长度远远大于B表,建议从较大的A表上驱动.(简言

SWAP_JOIN_INPUTS Oracle Hint(处理hash join强制大表(segment_size大)作为被驱动表)

swap_join_inputs是针对哈希连接的hint,它的含义是让优化器交换原哈希连接的驱动表和被驱动表的顺序,即在依然走哈希连接的情况下让原哈希连接的驱动表变被驱动表,让原哈希连接的被驱动表变为驱动表. 注意,在swap_join_inputs hint中指定的目标表应该是原哈希连接中的被驱动表,否则oracle会忽略该hint. /*+ swap_join_inputs(原哈希连接的被驱动表) */ 其使用范例如下: 1 2 select /*+ leading(dept) use_ha

]sap透明表、结构、簇介绍以及查找表方法

一些人在写开发功能说明书的时候不知道如何去找屏幕字段对应的透明表,下面我来介绍一个比较有效的方法:首先简单介绍一下概念:在SAP中的表的种类有以下三种:Tranparent table,Pools,Cluster table. 透明表每个透明表在数据库中有一个相应的物理表.物理表的名称和数据字典中的逻辑表定义的名称一致.事物中处理的数据存贮在透明表中.可以通过数据库直接查询,abap人员也希望能了解这个表的名字. 存贮表存储表可以用来存贮控制数据(例如:屏幕顺序,程序参数或临时数据).几个存储表

OrmLite动态创建表,一个实体类创建多张表的的偏招

在做一个Android的项目,因为使用数据库频繁,实体字段也比较多,于是打算采用ORM框架,发现OrmLite还不错,于是下了下来,打算使用. 没想到还没正式开工,就遇到问题了.我现在的一个需求如下, 我有一个实体类如下,代表聊天消息,现在要做的是针对每一个当前用户(userId)对应一个朋友(friendId)都要创建一个表.需求比较蛋疼,我本来想的是直接在加两个字段就搞定的,但是我们老大说要分表.没办法只能分表. public class ChatMessage{ public ChatMe

SqlServer将表中数据复制到另一张表

insert into phone2(ph,attr,type,carrier) select top 1000 ph,attr,type,carrier from phone 将表phone的字段和前1000条数据复制到Phone2表 数据库中的某个表删除重复数据(phone2表不能存在) select distinct  * into phone2 from phone 表phone的数据放到phone2中(phone2表可以存在) insert into phone2(ph,attr,ty

angular js 实现表单提交时下面的table获取到表单里面的数据

angular js 实现表单提交时下面的table获取到表单里面的数据<!DOCTYPE html><html ><head lang="en"> <meta charset="UTF-8"> <title></title> <link rel="stylesheet" href="css/bootstrap.min.css"/> <s