線性結構的特點:
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