2019資料結構考研(二)-----線性表
知識框架
線性表的基本概念
線性表的定義
線性表是具有相同資料型別的n(n>=0)個數據元素的有限序列,n為線性表的表長,當n=0時,則線性表為空表,線性表的第一個元素稱為表頭,最後一個元素稱為表尾
線性表的基本特點
- 線性表的元素個數有上限
- 線性表裡的元素具有順序性
- 表裡的元素型別都相同
- 線性表表示一種邏輯上一對一的關係,是邏輯結構,而順序表和連結串列則表示一種儲存結構,不要混淆
- 表裡的元素具有抽象性,即僅僅討論表裡元素之間的邏輯關係,而不討論表裡每個元素的具體內容
線性表的基本操作
鄰接表:儲存方法跟樹的孩子連結串列示法相類似,是一種順序分配和鏈式分配相結合的儲存結構。如這個表頭結點所對應的頂點存在相鄰頂點,則把相鄰頂點依次存放於表頭結點所指向的單向連結串列中。
線性表的順序表示
線性表的順序儲存稱為順序表,順序表的特點:表中的元素的邏輯順序和物理順序是一致的 線性表中元素的位序從1開始,而陣列中元素的下標從0開始的
//c的初始動態分配
L.data=(ElemType*)malloc(sizeof(ElemType)*InitSize)
//c++初始動態分配
L.data=new ElemType[InitSize]
特點:隨機訪問,儲存密度高,只儲存資料元素,邏輯上相鄰的元素在物理上也相鄰
順序表的插入操作
bool ListInsert(Sqlist &L,int i,ElemType e){ if(i<1||i>L.length+1)//判斷i的範圍是否有效 return false; if(L.length>=MaxSize)//判斷表的儲存空間是否已滿 return false; for(int j=L.length;j>=i;j--){ //將i後的元素往後移動 L.data[j]=L.data[j-1]; } L.data[i-1]=e;//將元素e插入i位置 L.length++; return true; }
判斷i是否有效時,i>length+1,i可以是插在表尾 最好的情況:在表尾插入,不移動元素,時間複雜度為O(1) 最壞的情況:在表頭插入.都要移動,時間複雜度為O(n) 平均情況:pi為概率pi=1/(1+n),有n+1個位置可以插入
順序表的刪除操作
bool ListDelete(Sqlist &L,int i,ElemType e){ //本演算法實現刪除順序表中第i個元素 if(i<1||i>L.length+1)//判斷i的範圍是否有效 return false; e=L.data[i-1];//將被刪除的元素賦值給e for(int j=i;j<length;j++){ //將i後的元素往後移動 L.data[j-1]=L.data[j]; } L.length--; return true; }
順序表的按值查詢
int LocateElem(Sqlist L,ElemType e){
//本演算法實現查詢順序表裡值為e的元素,成功返回位值,否則返回0
int i;
for(i=0;i<L.length;i++){
if(L.data[i]==e)//下標為i的的元素為e,返回其位序i+1
return i+1;
}
return 0;
}
單鏈表
typedef struct LNode{
ElemType data;//資料域存放資料元素
struct LNode *next; //指標域 ,存放下一個結點的地址
}LNode,*LinkList;
頭結點和頭指標的區別
- 無論有沒有頭結點,頭指標都指向連結串列的第一個結點,通常用頭指標來表示一個連結串列L
- 頭結點,結點內通常不儲存元素,有頭結點的話,頭指標都指向一個非空連結串列 ##單鏈表的基本操作實現 ###頭插法建立單鏈表
typedef struct LNode{
ElemType data;//資料域
struct LNode *next; //指標域
}LNode,*LinkList;
LinkList CreateList1(LinkList &L){
LNode *s;
int x;
L->next=null;//初始為空連結串列
scanf("%d",&x);
while(x!=9999)//輸入9999表示結束
{
s=(LNode*)malloc(sizeof(LNode));//建立新結點
s-data=x;
s->next=L->next;
L->next=s;//新結點插入到連結串列中,L為頭指標
scanf("%d",&x);
}
}
LNode,*LinkListl,都是匿名結構體別名,Lnode是實體,而LiskList是這種ElemType型別的指標 malloc是動態開闢記憶體,函式返回為void型指標(指向開闢的記憶體空間) 前面那個括號是開闢記憶體的型別,如L=(linklist*)malloc(sizeof(lnode)),就是將原來malloc返回的void型指標強制定義為 linklist型(也就是你一開始定義的指標L的型別),這樣才可以賦值給L. sizeof(Inode)是指malloc開闢的記憶體空間的大小,這裡就是指,這個大小為Inode型所佔的容量.(例如sizeof(int),就是開闢一個整形的空間(4位元組).分配兩個int的空間就是2*sizeof(int))
尾插法建立單鏈表
LinkList CreateList2(LinkList &L){
L=(LinkList)malloc(sizeof(LNode));
LNode *s,*r=L;//r為表尾指標
int x;
scanf("%d",&x);
while(x!=9999)//輸入9999表示結束
{
s=(LNode*)malloc(sizeof(LNode));//建立新結點
s-data=x;
r->next=s->next;
r=s;//r指向新的表尾結點
scanf("%d",&x);
}
r->next=null;//尾結點置空
return L;
}
按序號查詢結點
LNode *GetElemt(LinkList L,int i){
//該演算法取出連結串列中i位置的結點指標
int j=1;
LNode *p=L->next;
if(i==0)
return L;
if(i<1)
return null;
while(p&&j<i){
p=p->next;
j++;
}
return p;//如果i大於表長,p=null
}
時間複雜度為O(n)
單鏈表的插入操作
前插法
p=GetElemt(L,i-1)//獲得i的前驅結點
s->next=p->next;
p->next=s;
時間複雜度為O(1) 順序不能顛倒 也可以轉化成後插法,先進行前插,再將兩個的資料進行交換
刪除結點操作
p=GetElemt(L,i-1)//獲得i的前驅結點
q=p->next;//q指向被刪除結點
p->next=q->next;
free(q);//釋放q 結點
時間複雜度為O(1)
雙鏈表
雙鏈表在單鏈表的基礎上增加了一個結點,有一個前驅結點prior,有一個後繼結點next
typedef struct DNode{
ElemType data;//資料域
struct DNode *next,*prior;//前驅和後繼結點
}DNode,*DLinkList;
插入和刪除的時間複雜度為O(1)
雙鏈表的插入操作
在雙鏈表指標p後面插入結點s
s->next = p->next;//1
p->next->prior=s;//2
p->next=s;//3
s->prior=p;//4
1,2兩步必須在3之前
雙鏈表的刪除操作
//刪除q結點
p->next=q->next;
q->next->proir=p;
迴圈連結串列
迴圈連結串列與單鏈表的區別在於最後一個結點不為空,而改成指向頭結點
順序表和連結串列的比較
- 存取方式:順序表為隨機存取,而連結串列為順序存取
- 邏輯結構和物理結構:順序儲存時,邏輯上相鄰的元素在物理結構上也相鄰,而連結串列儲存時,物理結構上不一定相鄰
- 查詢,刪除和插入操作:對於按值查詢,在順序表無序時,兩者的時間複雜度都為O(n),當順序表有序時可以採用折半查詢,順序表的時間複雜度為O(log2n),對於按序號查詢,順序表的時間複雜度為O(1),而連結串列時間複雜度為O(n)
- 空間分配:連結串列可以在需要時申請分配,只要記憶體有空間就可以進行分配,高效靈活