数据结构:单链表
文章目錄
- 鏈表:
- 單鏈表的實現(xiàn)及操作:
- 1.指針描述的單鏈表L存儲結構
- 2.查找L的第i個元素,將其值賦值給e
- 3.在L的第i個位置之前插入元素e
- 4.在L中,刪除第i個元素,并返回其值e
- 5.輸入n個元素,建立帶頭節(jié)點的單鏈表L
- 6.歸并La和Lb的到新單鏈表Lc,按值非遞減排列
- 鏈表操作實例及代碼:
- 代碼:
- 運行結果:
鏈表:
1.鏈式存儲與順序存儲的不同:
順序存儲:邏輯關系上相鄰的兩元素,物理位置上也相鄰。優(yōu)點:可隨機存取表中任一元素,存儲位置可以用簡單公式表示。缺點:插入刪除需要移動大量元素。
鏈式存儲:不要求邏輯關系上相鄰的兩元素,物理位置上也相鄰。優(yōu)缺點剛好和順序存儲的相反。
2.線性表的鏈式存儲結構的特點:
用一組任意的存儲單元存儲線性表的數(shù)據(jù)元素,這組存儲單元可以是連續(xù)的也可以是不連續(xù)的。
我們用結點存儲一個數(shù)據(jù)元素,結點包含兩個域,數(shù)據(jù)域存儲數(shù)據(jù)元素的值,指針域存儲直接后繼。
鏈表每個結點中只包含一個指針域的,稱為線性鏈表或單鏈表。
使用鏈表時,只需關心數(shù)據(jù)元素之間的邏輯順序,無需關心每個數(shù)據(jù)元素在存儲器中的實際位置。
單鏈表的實現(xiàn)及操作:
1.指針描述的單鏈表L存儲結構
我們這里單鏈表是帶有頭結點的單鏈表,頭結點是單鏈表第一個元素(首元結點)之前的結點,他的數(shù)據(jù)域可以不存任何信息,也可存鏈表長度等信息,他的指針域的指針指向第一個元素的結點。
單鏈表的頭指針指向頭結點,單鏈表可由頭指針唯一確定,所以接下來我們都用結構指針描述。
如果線性表是空表,頭結點的指針域為空,也就是指針指向null。
線性表最后一個結點的指針域里面的指針指向null。
LNode是一個結點類型,它里面有數(shù)據(jù)域還有指針域。
LinkList是結點指針類型,它可以通過提取結點的next成員指向我們的結點類型。
調用其結構體里面的成員,這里面就涉及到.和->操作符的區(qū)別了,A->B則A為指針,->是成員提取,A->B是提取A中的成員B,A只能是指向類、結構、聯(lián)合的指針。A.B則A為對象或者結構體。
接下來我們參數(shù)都是結構體指針LinkList,所以一律用->提取結構體里面的成員。
next指針明顯指向下一個LNode類型的結點,那么它就是個結構體指針。
頭指針L指向頭結點,L->next指向首元結點。
如果說L是頭指針,那么L指向的是頭結點,L->data是頭結點的數(shù)據(jù)域,L->next是頭結點的指針域。L->next指向頭結點的下一個LNode類型的結點,也就是首元結點,L->next->data是首元結點的數(shù)據(jù)域,存的是第一個元素的值。
a指針指向b結點,用->提取a指針的成員,提取的是b這個結點的成員。可以看出我們根本沒有考慮a指針所在結構體里面的數(shù)據(jù)域,也就是與a指針相對應的a的數(shù)據(jù)域里面的元素。也就是說,頭指針僅僅充當了指針的作用,通過它,我們能夠訪問首元結點的成員。
typedef struct LNode {LElemType_L data;struct LNode* next; }LNode; typedef LNode* LinkList;2.查找L的第i個元素,將其值賦值給e
L是頭指針,也就是指向頭結點的指針,我們從第一個元素開始查找,所以要先通過頭指針L找到指向第一個元素的指針,也就是L->next。方便起見我們用p表示指向第一個元素的指針。然后用一個j表示當前我們找到了第幾個元素。p一開始指向第一個元素,所以j初始值賦為1。然后如果p指向null,意味著我們已經(jīng)找完了鏈表里面全部元素,此時退出循環(huán),如果i是不符合條件的,i<=0,那么也要退出循環(huán)。
退出循環(huán)之后我們判斷一下,如果p指向null或者j>i,那么就error。反之,意味著是因為j=i而退出的循環(huán),由于之前我們說p一開始指向第一個元素,所以j初始值賦為1,而且p指向下一個元素的同時j也加1,那么p可以認為始終指的是第j個元素。現(xiàn)在j指的i,也就是說p現(xiàn)在指的是第i個元素,那么把e賦給p->data即可。
Status GetElem_L(LinkList L, int i, LElemType_L &e) {int j;LinkList p ;j = 1;p = L->next;while(p && j<i) //滿足了才循環(huán) {j++;p = p->next;}if(!p || j>i) return ERROR;e = p->data;return OK; }3.在L的第i個位置之前插入元素e
在第i個位置插入元素的話我們需要找到第i-1個元素的位置,然后將新結點放到第i-1個元素的位置的下一個位置,并將原來的第i個位置放到新結點的下一個位置。這里面插入可以插入到第一個元素之前,所以我們找的第i-1個元素的位置有可能是頭結點的位置,那么我們遍歷查找的時候就要從頭結點開始,上一個函數(shù)我們說,p一開始指向第一個元素,所以j初始值賦為1。這里p指向頭結點,所以j初值賦為0。然后繼續(xù)循環(huán)。循環(huán)結束后判斷退出的原因,是因為沒找到還是i的值有問題,還是當j=i-1時退出得,滿足j=i-1的話再進行插入。
插入一個結點,首先要給這個結點開一個空間,s指針指向這個新的空間,也就是s指針指向新的結點,這里插入的時候如果先把這個結點放到第i-1個結點的后面,那么我們就找不到原來的第i個結點了,因為原來的第i個結點是通過p->next來找到的,然鵝現(xiàn)在p->next指的是新的結點s。
所以我們就不能先更改p->next,而是先把p->next給連接到s的后面,然后再把p->next更新為s。
Status ListInsert_L(LinkList &L, int i, LElemType_L e) {LinkList p, s;int j;p = L;j = 0; while(p && j<i-1) //尋找第i-1個結點 {p = p->next;++j;}if(!p || j>i-1)return ERROR;s = (LinkList)malloc(sizeof(LNode));if(!s)exit(OVERFLOW);s->data = e;s->next = p->next;p->next = s;return OK; }4.在L中,刪除第i個元素,并返回其值e
刪除第i個元素,意味著第i-1個元素連到第i+1個元素,我們知道第i-1個元素的位置,也就知道了后面的元素的位置,因此這里還是找第i-1個元素。
這里可以刪除第一個元素,因此還是從頭結點開始找。這里我們刪除第i個元素的話,第i個元素一定要存在,所以判斷里面要先判斷第i-1個元素的下一個元素是否存在,本來我們是要找第i-1個元素,但是現(xiàn)在我們先查看了第i個元素,而且我們還存了第i-1個元素的位置,那么我們找第i個元素的話,即使退出循環(huán),我們仍然能找到第i-1個元素的位置,這就轉化成了,找第i個元素。當然你也可以找第i-1個元素,只不過你要判斷,如果它后面沒元素,就退出了,而并不是說只要第i-1個元素不為空,就能刪。
最后退出了,但是pre存的是第i-1個元素的位置,那么pre->next=pre->next->next即可,這里刪去之后還要把被刪的結點的空間給釋放了,所以先把pre->next存到p里面,然后釋放p即可。
Status ListDelete_L(LinkList &L, int i, LElemType_L *e) {LinkList pre, p; int j;pre = L;j = 1; while(pre->next && j<i) {pre = pre->next;++j;}if(!pre->next || j>i) return ERROR;p = pre->next;pre->next = p->next;*e = p->data;free(p);return OK; }5.輸入n個元素,建立帶頭節(jié)點的單鏈表L
首先看頭插法,也就是說先插入的在后面,每次插入是插到鏈表的第一個元素的位置。如果你輸入9 8 7 6 5,鏈表從表頭到表尾依次存放5 6 7 8 9。
頭插法先要建立一個頭結點,L指針指向頭結點,L->next是首元結點,一開始是個空鏈表,所以L->next指向null。輸入一個數(shù),放到一個新結點里面,然后把這個結點插到原來的首元結點前面,也就是L->next前面,讓他成為新的首元結點,然后由于L->next指向首元結點,所以我們更新L->next,讓他指向這個新結點。
再看尾插法,也就是說先插入的在前面。
先建立頭結點L,L->next就是第一個元素的位置,起初為null,我們需要引入一個標記q,讓他一開始指向頭結點,每當插入一個元素p,q的下一個位置就是p的位置,然后讓它指向p的位置。這樣每次新結點都放到了p后面,而且q又指向最后的那個元素的位置,那么也就說明我們每次都放到了最后的位置。
最終還需讓q->next指向null,因為已經(jīng)插入完了,它沒有下一個元素了。
Status CreateList_HL(LinkList &L, int n) {int i;LinkList p;LElemType_L tmp;L = (LinkList)malloc(sizeof(LNode));if(!L)exit(OVERFLOW);L->next = NULL; printf("請輸入%d個數(shù):",n);for(i=1; i<=n; ++i){if(scanf("%d", &tmp)==1){p = (LinkList)malloc(sizeof(LNode));if(!p)exit(OVERFLOW);p->data = tmp; p->next = L->next;L->next = p; }elsereturn ERROR;}return OK; } Status CreateList_TL(LinkList &L, int n) {int i;LinkList p, q;LElemType_L tmp; L = (LinkList)malloc(sizeof(LNode));if(!L)exit(OVERFLOW);L->next = NULL;printf("請輸入%d個數(shù):",n);for(i=1,q=L; i<=n; ++i){if(scanf("%d", &tmp)==1){p = (LinkList)malloc(sizeof(LNode));if(!p)exit(OVERFLOW);p->data = tmp; q->next = p;q = q->next; }elsereturn ERROR; }q->next = NULL;return OK; }6.歸并La和Lb的到新單鏈表Lc,按值非遞減排列
這個就是把La也當成Lc的頭結點,然后從第一個元素開始,依次比較La,Lb的當前元素大小。如果小的,那么放到Lc的后面,然后把放過的那個元素所在的鏈表的指針后移一位。最后如果有一個列表已經(jīng)遍歷完了,那么就把那沒被遍歷完的列表放到Lc的后面,我們可以看出,Lb的元素全放到了La里面,最后只剩了一個Lb的頭結點,所以我們把頭結點free掉即可。
void MergeList_L(LinkList &La, LinkList &Lb, LinkList &Lc) { LinkList pa, pb, pc;pa = La->next;pb = Lb->next;pc = Lc = La; while(pa && pb){if(pa->data <= pb->data){pc->next = pa;pc = pa;pa = pa->next;}else{pc->next = pb;pc = pb;pb = pb->next;} }pc->next = pa ? pa : pb; free(*Lb); *Lb = NULL; }鏈表操作實例及代碼:
代碼:
#include<cstdio> #include<cstdlib>#define OK 1 #define ERROR 0 #define OVERFLOW -2 #define UNDERFLOW -3 #define TRUE 1 #define FALSE 0typedef int LElemType_L; typedef int Status;typedef struct LNode {LElemType_L data;struct LNode* next; }LNode; typedef LNode* LinkList;Status GetElem_L(LinkList L, int i, LElemType_L &e) {int j;LinkList p = L->next;j = 1;p = L->next;while(p && j<i) {j++;p = p->next;}if(!p || j>i) return ERROR;e = p->data;return OK; } Status ListInsert_L(LinkList &L, int i, LElemType_L e) {LinkList p, s;int j;p = L;j = 0; while(p && j<i-1) //尋找第i-1個結點 {p = p->next;++j;}if(!p || j>i-1)return ERROR;s = (LinkList)malloc(sizeof(LNode));if(!s)exit(OVERFLOW);s->data = e;s->next = p->next;p->next = s;return OK; } Status ListDelete_L(LinkList &L, int i, LElemType_L &e) {LinkList pre, p; int j;pre = L;j = 1; while(pre->next && j<i) {pre = pre->next;++j;}if(!pre->next || j>i) return ERROR;p = pre->next;pre->next = p->next;e = p->data;free(p);return OK; }Status CreateList_HL(LinkList &L, int n) {int i;LinkList p;LElemType_L tmp;L = (LinkList)malloc(sizeof(LNode));if(!L)exit(OVERFLOW);L->next = NULL; printf("(頭插法)請輸入%d個數(shù):",n);for(i=1; i<=n; ++i){if(scanf("%d", &tmp)==1){p = (LinkList)malloc(sizeof(LNode));if(!p)exit(OVERFLOW);p->data = tmp; p->next = L->next;L->next = p; }elsereturn ERROR;}return OK; } Status CreateList_TL(LinkList &L, int n) {int i;LinkList p, q;LElemType_L tmp; L = (LinkList)malloc(sizeof(LNode));if(!L)exit(OVERFLOW);L->next = NULL;printf("(尾插法)請輸入%d個數(shù):",n);for(i=1,q=L; i<=n; ++i){if(scanf("%d", &tmp)==1){p = (LinkList)malloc(sizeof(LNode));if(!p)exit(OVERFLOW);p->data = tmp; q->next = p;q = q->next; }elsereturn ERROR; }q->next = NULL;return OK; } Status ListTraverse_L(LinkList L, void(Visit)(LElemType_L)) {LinkList p;if(!L)return ERROR;elsep = L->next; while(p){Visit(p->data);p = p->next;}printf("\n");return OK; } void PrintElem(LElemType_L e) {printf("%d ", e); } void MergeList_L(LinkList &La, LinkList &Lb, LinkList &Lc) { LinkList pa, pb, pc;pa = La->next;pb = Lb->next;pc = Lc = La; while(pa && pb){if(pa->data <= pb->data){pc->next = pa;pc = pa;pa = pa->next;}else{pc->next = pb;pc = pb;pb = pb->next;} }pc->next = pa ? pa : pb; free(Lb); Lb = NULL; } int main() {LinkList La, Lb, Lc ;int m,i; LElemType_L e;m = 5;printf("作為示例,La長度設定為 %d ,Lb設定為 %d ,創(chuàng)建La和Lb...\n", m, m);CreateList_HL(La, m);CreateList_TL(Lb, m); printf("La = ");ListTraverse_L(La, PrintElem);printf("Lb = ");ListTraverse_L(Lb, PrintElem);printf("\n");MergeList_L(La, Lb, Lc);printf("合并La和Lb為Lc = ");ListTraverse_L(Lc, PrintElem);printf("\n");ListDelete_L(Lc, 6, e);printf("刪除 Lc 中第 6 個元素 \"%d\" ...\n", e);printf(" Lc 中的元素為:Lc = "); ListTraverse_L(Lc, PrintElem);printf("\n");GetElem_L(Lc, 4, e);printf(" Lc 中第 4 個位置的元素為 \"%d\" \n", e);printf("\n");for(i=1; i<=6; i++) {printf("在 Lc 第 %d 個位置插入 \"%d\" ...\n", i, 2*i);ListInsert_L(Lc, i, 2*i);}printf(" Lc 中的元素為:Lc = "); ListTraverse_L(Lc, PrintElem);printf("\n");return 0; }運行結果:
輸入:
9 7 5 3 1
2 4 6 8 10
輸出:
作為示例,La長度設定為 5 ,Lb設定為 5 ,創(chuàng)建La和Lb…
(頭插法)請輸入5個數(shù):9 7 5 3 1
(尾插法)請輸入5個數(shù):2 4 6 8 10
La = 1 3 5 7 9
Lb = 2 4 6 8 10
合并La和Lb為Lc = 1 2 3 4 5 6 7 8 9 10
刪除 Lc 中第 6 個元素 “6” …
Lc 中的元素為:Lc = 1 2 3 4 5 7 8 9 10
Lc 中第 4 個位置的元素為 “4”
在 Lc 第 1 個位置插入 “2” …
在 Lc 第 2 個位置插入 “4” …
在 Lc 第 3 個位置插入 “6” …
在 Lc 第 4 個位置插入 “8” …
在 Lc 第 5 個位置插入 “10” …
在 Lc 第 6 個位置插入 “12” …
Lc 中的元素為:Lc = 2 4 6 8 10 12 1 2 3 4 5 7 8 9 10
總結
- 上一篇: 前端实现图片悬浮_悬浮图片之上效果实现
- 下一篇: ic读卡器设置工具_从Matlab被禁来