(一)線性表----順序表、連結串列
阿新 • • 發佈:2020-09-20
一、線性表的基本概念與實現
1.1.線性表的邏輯特性
- 線性表都只有一個表頭元素和一個表尾元素;
- 表頭沒有前驅,表尾沒有後繼;
- 除表頭和表尾外,其他元素只有一個直接前驅和一個直接後繼。
1.2.線性表的儲存結構
a、順序表
- 順序表是把線性表中的所有元素按照其邏輯順序,依次儲存到從指定的儲存位置開始的一塊連續的儲存空間中,這樣只要知道第一個元素的位置就能立即知道每一元素的位置
b、連結串列
- 在連結串列儲存中,每個節點不僅包含所存元素本身的資訊,還包含元素之間邏輯關係的資訊,即前驅節點包含後繼節點的地址資訊,這樣就能通過前驅節點中的地址資訊找到後繼節點的位置
c、兩種線性表的比較
- 順序表只要知道元素0的位置就能根據與該節點的距離馬上找到其他元素的位置,即支援隨機訪問特性,
- 順序表要求佔用連續的記憶體空間;儲存分配只能預先進行,即靜態分配,一旦分配好了,在其操作過程中始終不變;
- 順序表的插入需要移動多個元素,而連結串列不需要;
- 連結串列不支援隨機訪問;
- 因為連結串列每一個節點需要劃出一部分空間儲存指向下一個節點位置的指標,因此節點的儲存空間利用率比順序列表稍低;
- 因為連結串列中節點是散落地分佈在儲存器中,所以連結串列支援儲存空間額動態分配。
1.3、連結串列的形式
a、單鏈表
1. 每一個節點中除了包含資料域外,還包含一個指標域,用以指向後繼節點,圖為帶頭節點的單鏈表,帶頭節點的單鏈表中的頭指標head指向頭節點,頭節點的值域不包含任何資訊,從頭節點的後繼節點開始儲存資訊,頭指標head始終不等於NULL,head->next等於NULL的時候連結串列為空;不帶頭節點的單鏈表中的頭指標head直接指向開始節點,當head等於NULL的時候連結串列為空,
b、雙鏈表
1. 單鏈表只能由開始節點走到終端節點,而不能由終端節點走向開始節點,而雙鏈表可以實現,雙鏈表就是在單鏈表節點上增添了一個指標域,指向當前節點的前驅
c、迴圈單鏈表
1. 只要將單鏈表的最後一個指標域指向連結串列中的第一個節點即可,可以實現從任何一個節點出發訪問連結串列中的任何節點。
d、迴圈雙鏈表
e、靜態連結串列
二、線性表的基本操作
a、順序表的結構定義
1 #define maxSize 100 2 typedef struct 3 { 4 int data[maxSize]; //存放順序表元素的陣列 5 int length; //存放順序表的長度 6 }Sqlist; //順序表型別的定義
b、單鏈表的節點定義
typedef struct LNode { int data; //data中存放節點資料域 struct LNode * next; //指向後繼節點的指標 }LNode; //定義單鏈表節點型別
c、雙鏈表的節點定義
typedef struct DLNode { int data; struct DLNode *prior; //前驅 struct DLNode *next; //後繼 }DLNode;
例題1
已知一個順序表L,其中的元素已經按照遞增排序,設計一個演算法,插入一個元素x後保持該順序表仍然遞增有序排列。
分析:1. 要找出可以讓順序表保持有序的插入位置
2.將第一步找出的位置上及其後的元素往後移動一個位置,然後將x放至騰出的位置上
3.為了方便操作,陣列元素的存放從下標1開始
int LocateElem(Sqlist L, int x) //找到要插入的位置 { int i; for(i=1; i<L.length;++i) if(x<L.data[i]) { return i; } return i; }
void insert(Sqlist &L, int x) //因為L本身要發生變化,所以使用引用型
{
int p, i;
p=LocateElem(L, x);
for(i=L.length; i>=p; --i)
{
L.data[i+1] = L.data[i];
L.data[p] = x;
++(L.length);
}
}
例題2
A和B是兩個單鏈表(帶表頭結點),其中元素遞增有序。設計一個演算法將A和B歸併成一個按照元素值非遞減有序的連結串列C,C由A,B中的節點組成
typedef struct LNode { int data; //data中存放節點資料域 struct LNode * next; //指向後繼節點的指標 }LNode; //定義單鏈表節點型別 void merge(LNode *A, LNode *B, LNode *C) { LNode *p = A->next; //p來跟蹤A的最小節點 LNode *q = B->next; //q來跟蹤B的最小節點 LNode *r; //r始終指向C的終端節點 C=A; //A的頭節點來做C的頭節點 C->next = NULL; free(B); r=C; //r指向C,因為此時頭節點也是終端節點 while(p != NULL && q != NULL) //qp都不為空,選取較小的節點插入C的尾部 { if(p->data <= q->data) { r->next = p;p = p->next; r = r->next; } else { r->next = q;q = q->next; r = r->next; } } r->next = NULL; if(p != NULL) r->next = p; if(q != NULL) r->next = q; }
例題3
假設有n個元素已經儲存在陣列a中,用尾插法建立連結串列C
void CreatelistR(LNode *&C, int a[], int n) { LNode *s, *r; //s用來指向新申請的節點,r始終指向c的終端節點 int i; C=(LNode *)malloc(size(LNode)); //申請C的頭節點空間 C->next = NULL; r=C; //r指向頭節點,因為此時頭節點就是終端節點 for(i=1; i<=n; ++i) //迴圈申請n個節點來接受陣列a中的元素 { s = (LNode*)malloc(sizeof(LNode)); //s指向新申請的節點 s->data = a[i]; //用新申請的節點來接收a中的一個元素 r->next = s; //用r來接納新節點 r = r->next; //r指向終端節點,以便於接納下一個到來的節點 } r->next = NULL; //a中所有的元素都已經裝入連結串列中,C的終端節點的指標域置為NULL,C建立完成 }
頭插法
typedef struct LNode { int data; //data中存放節點資料域 struct LNode * next; //指向後繼節點的指標 }LNode; void CreatelistF(LNode *&C, int a[], int n) { LNode *s; int i; C=(LNode *)malloc(size(LNode)); //申請C的頭節點空間 C->next = NULL; for(i=1; i<=n; ++i) { s = (LNode*)malloc(sizeof(LNode)); //s指向新申請的節點 s->data = a[i]; s->next = C->next; //s所指新節點的指標域next指向C的開始節點 C->next = s; //頭節點的指標域next指向s節點,使得s成為了新的開始節點 } }