线性结构的特点:
1 在唯一的“第一个”元素和唯一的“最后一个”元素 2.除第一个元素没有前驱元素外,其余均有唯一的前驱元素,除最后一个元素外,其余均有唯一的后继元素。
一句话就是线性表就是顺序结构,不存在一对多,多对一的想象。
抽象数据类型线性表的定义如下:
ADT List{
数据对象: D= { ai | ai ∈ElemSet,i = 1,2……n,n>= 0}
数据关系:R1={<ai-1,ai | ai-1,ai ∈D,i = 2,……,n}
基本操作:
InitList(&L)
操作结果:构造一个空的线性表L.
DestroyList( &L)
初始条件:线性表L已存在。
操作结果:销毁线性表L。
ClearList(&L)
初始条件:线性表L已存在
操作结果:将L重置为空表(清空元素)
ListEmpty(L)
初始条件:线性表L已存在
操作结果:若L为空表。返回TRUE,否则返回FALSE。判断线性表是否为空
ListLength(L)
初始条件:线性表L已存在
操作结果:返回L中元素的 个数 用以统计线性表的元素个数
Getlem(L,i,&e)
初始条件:线性表已存在,1 <= i <= ListLength(L)
操作结果:用e返回L中第i个元素的值
LocateElem(L,e, compare())
初始条件:线性表L已存在,compare()是数据元素判定函数。
操作结果:返回L中第1个与e满足关系compare()的数据元素的位序。若不存在,返回0
PriorElem(L, cur_e, &pre_e)
初始条件:线性表L已存在
操作结果:若cur_e是L的数据元素,且不是第一个,用pre_e返回它的前驱,否则操作失败,pre_e无定义)
NextElem(L,cur_e, &next_e)
初始条件:线性表L已存在
操作结果:若cur_e是L的数据元素,且不是最后 next_e返回它的后继,作失败,pre_e无定义)
ListInsert(&L ,i , e)
初始条件:L已存在, 1 <= i <=Listlength()+1.
操作结果:在L中的第i位置插入新的元素e,L的长度+1.
ListDelete(&l,i,e)
初始条件:L已存在, 1 <= i <=Listlength()。
操作结果:删除L中的第i位个数据元素,并用e返回删除的元素的值,L的长度-1.
ListTraverse(L,visit())
初始条件:线性表L已存在
操作结果:依次对L的每个元素调用函数visit()。一旦visit()失败,则操作失败}ADT List
一 线性表的基本操作
3 表可以进行其他复杂操作如合并线性表、拆开线性表、重新复杂线性表等
(1)合并线性表:
分析 将LB中元素依次的值依次在LA中进行查访,若不存在,则插入到LA中。
算法:
void union(List &La,List Lb){
//将所有在线性表Lb中且不再La中数据元素插入到La中
la_len = ListLength(La); Lb_len = ListLength(Lb);
//分别求出LA LB的长度
for(i=1;i <= lb_len; i++){
GetElm(Lb, i , e); //取Lb中的第i个数据元素赋给e
if(! LocateElem(La, e, equal)) ListInsert(La, ++la_len,e)
//La不存在和e相同的数据元素,则插入
}
} // union
(2) LA和LB中的数据元素按值非递减有序排列,将LA与LB合并新的表LC,且LC的数据元素仍按值非递减有序排列。
分析:先构造一个空表LC,然后将LA或LB中元素逐个插入到LC。因为LC中按值非递减排列。所以可以设两个指针,分别指向LA和LB中的元素
若设i当前指向a,j当前指向b,当前插入LC中元素应该为c(c为a.b中数值小的数据)。
void MergeList(List La, List Lb, List &Lc){
//已知线性表La和Lb中的数据元素按值非递减排列。
//归并La和Lb得到新的线性表Lc,Lc的数据元素也按值非递减排列。
InitList(Lc);
i = j = 1; k = 0;
la_len = Listlength(la); lb_len = ListLength(Lb);
while(( i <= La-len) && (j<= Lb_len)) { // LA 和LB均非空
GetElem(La, i, ai); //用ai返回第i元素的值
GetElem(Lb,j bj) ; // 用 bj返回第j个元素的值
if( ai <= bj) { ListInster(Lc, ++k,ai); ++i;} // 比较ai和bj的值,将小的先插入Lc,注意++k,相当于先开辟空间,然后放入元素。然后i++指向下一个
else{ ListInsert( Lc, ++k, bj);++j;} { ListInsert(Lc, ++k, bj); ++j;}
}
while(i <= la_len){ //lb为空
GetElem(La,i++,ai);
ListInsert(Lc, ++k, ai ); //将La中元素依次插入LC中
}
while(j <= Lb_len) {
GetElem( lb, j++,bi);
ListInsert(Lc, ++k, bj);
}
}//MergeList
两个例子的算法的时间复杂度取决于抽象数据类型List定义中基本操作的执行时间。假如GetElem和ListInsert 这两个操作的执行时间与表长无关
LocateElem的执行时间和表长成正比。第一子的时间复杂度(最差情况)O(ListLength(LA)* ListLength(LB)),LA中的每个元素都要遍历LB,所以为积。
第二个例子的时间复杂度O(ListLength(LA)+ Listlength(LB)).
二 线性表的顺序表示和实现
用一组地址连续的存储单元依次存储线性表的数据元素
以表中元素的物理存储位置来表示线性关系。每一个数据元素的存储位置都和第一个元素的存储位置相差一个和数据元素在线性表中
的位序成正比的常数。可以通过这种方法随即存取线性表中的任意数据。
//线性表的动态分配顺序存储结构//
#define LIST_INIT_SIZE 100 // 线性表存储空间的初始分配量
#define LISTINCREMENT 10 //线性表存储空间的分配增量即每个元素占用的空间大小
typedef struct{
ElemType *elem; //存储空间基址
int length; //当前长度
int listsize; //当前分配的存储容量(以sizeof(ElemType)为单位
} SqList;
status InitList_aq(Sqlist &L){
// 构造一个空的线性表L
L.elem = ( ElemType *) malloc( LIST_INIT_SIZE * sizeif(ElemType));
if(! L.elem) exit (OVERFLOW); // 存储分配失败
L.length = 0; //空表长度为0
L.listsize = LIST_INIT_SIZE; //初始存储容量
return OK;
} //InitList_Sq
在第i( 1<= i <= n)个元素之前插入一个元素时,需将第n至第i(n-i+1)个元素向后移动一个位置。
算法实现:
Status ListInsert_Sq(SqList &L, int i, ElemType e) {
//在线性表L中第i个位置之前插入新元素e
if(i < 1 || i > L.length+1) return ERROR; // i的值不合法 超出了L的范围
if( L.length >= L.listsize) { //当前存储空间已满,增加分配
newbase = ( ElemType *) realloc(L.elem,
(L.listsize + LISTINCREMENT) *SIZEOF(ElemType));
//通过realloc函数重新分配内存空间
if(!newbase)exit(OVERFLOW); //存储分配失败
L.elem = newbase; //新基址
L.listsize += LISTINCREMENT; //增加存储容量
}
q = &(L.elem[i -1 ]; //q为插入位置
for( p = & (L.elem[l.length - 1]); p >= q; --p)
*p(p + 1) = *p;
//出入位置后及之后的元素右移
*q = e; //插入e
++ L.length; //表长增加1
return OK;
} // ListInsert _sq