线性表---List,零个或多个数据元素的有限序列.

      首先它是一个序列.也就是说,元素之间是有顺序的,若元素存在多个,则第一个元素无前驱,最后一个元素无后继,其他每个元素都有且只有一个前驱和后继.

     当线性表元素的个数n(n>=0)定义为线性表的长度时,如果n=0,则说明该线性表是个空表.

     线性表的抽象数据类型定义如下:

 

View Code
 1 ADT 线性表(List)
2 Data
3 线性表的数据对象集合为{a1,a2,....an},每个元素的类型均为DataType.其中,除第一个元素a1外,每一个元素有且只有一个直接前驱元素,除了最后一个元素an外,每一个元素有且只有一个直接后继元素。数据元素之间的关系是一对一的关系.
4
5 Operation
6 InitList(*L):初始化操作,建立一个空的线性表
7 ListEmpty(L):若线性表为空,返回true,否则返回false.
8 ClearList(*L):将线性表清空.
9 GetElem(L,i,*e):将线性表L中的第i个位置元素值返回给e
10 LocateElem(L,e):在线性表L中查找给定值e相等的元素,若查找成功,返回该元素在表中的序号表示成功,否则,返回0表示失败.
11 ListInsert(*L,i,e):在线性表L中的第i个位置插入元素e.
12 ListDelete(*L,i,*e):删除线性表L中第i个位置元素,并用e返回其值.
13 ListLength(L):返回线性表L的元素个数.
14 EndADT

      对于不同的应用,线性表的基本操作是不同的,但是上述操作是最基本的,对于实际问题中涉及的关于线性表的更复杂的操作,完全可以用这些基本操作的组合来实现.

     假设La表示集合A,Lb表示集合B,现在要得到一个集合A使得A=AUB:

     C语言代码如下:

View Code
 1 void unionL(SqList *La,SqList Lb)
2 {
3 int La_len,Lb_len,i;
4 ElemType e;
5 La_len=ListLength(*La);
6 Lb_len=ListLength(Lb);
7 for (i=1;i<=Lb_len;i++)
8 {
9 GetElem(Lb,i,&e);
10 if (!LocateElem(*La,e))
11 ListInsert(La,++La_len,e);
12 }
13 }

        这里,union的操作用到了ListLength,GetElem,LocateElem,ListInsert等基本方法.

        线性表的顺序存储结构,指的是用一段地址连续的存储单元依次存储线性表的数据元素.

        线性表的顺序存储结构的代码如下:

View Code
1 #define MAXSIZE 20 /* 存储空间初始分配量 */
2 typedef int ElemType; /* ElemType类型根据实际情况而定,这里假设为int */
3 typedef struct
4 {
5 ElemType data[MAXSIZE]; /* 数组,存储数据元素,最大值为MAXSIZE */
6 int length; /* 线性表当前长度 */
7 }SqList;

       这里,我们发现描述顺序存储结构需要三个属性:

      1,存储空间的起始位置:数组data,它的存储位置就是存储空间的存储位置.

      2,线性表的最大存储容量:数组长度:MAXSIZE

      3,线性表的当前长度length.

    注意:线性表的长度应该小于等于数组的长度.

       接着,我们来看下一段代码:

View Code
 1 #define OK 1
2 #define ERROR 0
3 #define TRUE 1
4 #define FALSE 0
5 typedef int Status; /* Status是函数的类型,其值是函数结果状态代码,如OK等 */
6 /* 初始条件:顺序线性表L已存在,1≤i≤ListLength(L) */
7 /* 操作结果:用e返回L中第i个数据元素的值,注意i是指位置,第1个位置的数组是从0开始 */
8 Status GetElem(SqList L,int i,ElemType *e)
9 {
10 if(L.length==0 || i<1 || i>L.length)
11 return ERROR;
12 *e=L.data[i-1];
13
14 return OK;
15 }

    以上代码就是用来获得某一元素的操作。有了以上的基础,我们就能对顺序存储结构的插入与删除做一个讲解了。首先,我们来了解下插入操作.

    首先,我们一起看看插入算法的思路:

    1,如果插入的位置不合理,那么就抛出异常

    2,如果线性表长度大于等于数组长度,则抛出异常或者动态增加容量

    3,从最后一个元素开始向前遍历到第i个位置,分别将它们都向后移动一个位置

    4,将要插入的元素填入位置i处

    5,表长加1.

    C语言实现代码如下:

View Code
 1 /* 初始条件:顺序线性表L已存在,1≤i≤ListLength(L), */
2 /* 操作结果:在L中第i个位置之前插入新的数据元素e,L的长度加1 */
3 Status ListInsert(SqList *L,int i,ElemType e)
4 {
5 int k;
6 if (L->length==MAXSIZE) /* 顺序线性表已经满 */
7 return ERROR;
8 if (i<1 || i>L->length+1)/* 当i比第一位置小或者比最后一位置后一位置还要大时 */
9 return ERROR;
10
11 if (i<=L->length) /* 若插入数据位置不在表尾 */
12 {
13 for(k=L->length-1;k>=i-1;k--) /* 将要插入位置之后的数据元素向后移动一位 */
14 L->data[k+1]=L->data[k];
15 }
16 L->data[i-1]=e; /* 将新元素插入 */
17 L->length++;
18
19 return OK;
20 }

       然后,我们一起看看删除操作.还是从思路开始说:

      1,如果删除位置不合理,抛出异常

      2,取出删除元素

      3,从删除元素位置开始遍历到最后一个元素位置,分别将它们向前移动一个位置

      4,表长减1.

     C语言实现代码如下:

View Code
 1 /* 初始条件:顺序线性表L已存在,1≤i≤ListLength(L) */
2 /* 操作结果:删除L的第i个数据元素,并用e返回其值,L的长度减1 */
3 Status ListDelete(SqList *L,int i,ElemType *e)
4 {
5 int k;
6 if (L->length==0) /* 线性表为空 */
7 return ERROR;
8 if (i<1 || i>L->length) /* 删除位置不正确 */
9 return ERROR;
10 *e=L->data[i-1];
11 if (i<L->length) /* 如果删除不是最后位置 */
12 {
13 for(k=i;k<L->length;k++)/* 将删除位置后继元素前移 */
14 L->data[k-1]=L->data[k];
15 }
16 L->length--;
17 return OK;
18 }

      分析上述插入和删除两段代码和更早的获取元素代码,我们可以发现,线性表的顺序存储结构,在存,读数据时,不管是哪个位置,时间复杂度O(1),而插入或删除时,时间复杂度都是O(n).

     说完了顺序存储结构,我们可以总结下它的优缺点了,喜欢先听优点还是先听缺点呢?

     好吧,从优点开始说:当我们在使用线性表的时候,我们不需要为表中元素之间的逻辑关系而增加额外的存储空间,而且可以快速的存取表中任意位置的元素.接下来谈谈缺点:如我们所见,如果我们要插入或者删除的元素是在第一个位置,那么无疑的,我们需要移动大量的元素来完成这样的操作,而且限于线性表长度必须小于数组长度,如果我们需要插入大量数据,那么很难保证空间是否充足,而如果我们要删除大量数据的时候,无疑又会造成空间的浪费。

     好吧,更优的方式,让我们看看接下来的讲解。

posted on 2011-08-31 17:09  Jeallyn  阅读(295)  评论(0编辑  收藏  举报