数据结构之:链表详解
鏈表是 數據結構中很重要的基礎 部分,下面 我通過簡單的故事來將鏈表的內容串起來解釋一下,同時也是總結一下自己的學習內容:
故事:
某一天,樂樂,豐豐,呆子,星星,領領,小韋6位小朋友帶領著8個小朋友一起去山上玩耍。當玩耍過后,天下起了大雨 !!于是 14位小朋友趕緊返回,不幸的是山口處山洪暴發。如果想要 過去,14位 小朋友需要連在一起,單個過河的小朋友會被山洪沖走(因為經過試驗證明了這一點,而且小韋在試驗過程中被洪水沖走了) 。
我們將每位小朋友看做是一個節點。
typedef struct Lnode {int data; //小朋友數據struct Lnode *next; //指向下一個小朋友的指針 };建立單鏈表:
那么要從洪水中過去的話,14位小朋友需要建立一個長隊。可以想到,建立長隊的方法有兩種:
首先我們需要空出來一塊場地用來建立我們的長隊(所說的建立 一個空鏈表)
code:
void InitList(LinkList*&L) {L = (LinkList *)malloc(sizeof(LinkList));L->next = NULL; }第一種:樂樂站在第一個,星星站在樂樂 的前面,呆子站在星星的前面……依次排列,這樣樂樂會最終站在隊尾(這就是頭插法建立單鏈表)。
? ? ? ? ? ? ? ?1. 2 .3.……豐豐,呆子,星星,樂樂。
code:
void CreateListH(LinkList *&L, int a[], int n)? ?//頭插法建立單鏈表 {LinkList *s;? ? ? ? ? ? ? ? ? ? ? ? ? ? //s是要插入的小朋友int i;L = (LinkList *)malloc(sizeof(LinkList)); //申請空間,L->next = NULL;? ? ? ? ? ? ? ? ? ? ? ? ? //剛開始的時候為空,因為還沒有排隊。for (i = 0; i < n; i++)? ? ? ? ? ? ? ? ?//我們將后來的小朋友插入前面,并讓這個的小朋友的手拉著站在他后面的小朋友的衣服{s = (LinkList *)malloc(sizeof(LinkList));s->data = a[i];s->next = L->next;?L->next = s;} }第二種:樂樂站在第一個,星星 站在樂樂的后面,呆子站在星星的后面,豐豐站在呆子的后面,……其他人依次后排(這就是尾插法建立單鏈表)。
? ? ? ? ? ? ? ?樂樂,星星,呆子,豐豐……。
Code:
void? CreateListR(LinkList *&L, int a[], int n) {int i;LinkList *s, *r;L = (LinkList *)malloc(sizeof(LinkList));? //申請空間r = L;for (i = 0; i < n; i++)? ? ? // s是要插入的小朋友,r只是一個臨時標記,為了知道現在誰在最后的位置{s = (LinkList *)malloc(sizeof(LinkList));s->data = a[i];r->next = s;? ? ? ? ? ? //將 s小朋友插入r的后面,r = s;? ? ? //插入后,那么s在最后的位置,讓其成為標記。因為我們需要知道誰在最后面,方面下次的插入操作}r->next = L->next;? ? ? ? ? ? ?//隊伍建立完成后,最后? 隊尾節點為 NULL; }那么現在隊伍建立完成了,可以過河了,小朋友們都很高興(其實 一點都不高興)。
這個時候小韋竟然回來了,小朋友們都很高興他還 或活著,選舉他當了隊長 。由于小韋剛回來 ,對隊伍的情況 不是 很了解,他想要知道隊伍有多少人,于是他從隊伍的頭到尾進行了查數:
(單鏈表中數據 節點 的個數)
code:
int ListLength(LinkList *L) {int ans = 0;LinkList *p = L; //小韋學長找到了隊伍頭部 while (p->next != NULL) //直到尾部,看到一個小朋友 就ans++;{ans++;p = p->next;}return ans; }但是 悲劇的是,小韋由于智商有限,只能數到10,就不會數了。于是他想了一個方法,讓隊伍中的小朋友從頭到尾 說出自己的名字、信息:
(輸出節點存儲的信息):
code:
void CoutList(LinkList *L) {LinkList *p = L->next; //從 有效節點開始while (p != NULL){printf("%d", p->data); //小朋友喊出自己的信息p = p->next; //換下一個小朋友} }由于小韋只能數到10,造成隊伍中的星星的嘲笑,并給他 起了個外號:小白。站在隊伍頭部的小韋很是氣憤,氣憤中的小韋突然就知道怎么數10以后的數字了。于是他查了一下嘲笑他的小朋友的位置,給他起外號報復:
修改某個節點的數據信息 :
code:
bool ChangeInfo(LinkList *&L, int i, int &e) //小韋查到了星星的位置,想好了外號 {int j = 0;LinkList *p = L;while (j < i && p != NULL) //如果是不在數據范圍內,說明小韋的數學真的很菜{j++;p = p->next;}if (p == NULL)return false;else{p->data = e; //如果找到了i位置上的星星,小韋就把他想好的外號賦予星星return true;} }由于小韋的行為,使得某個位置上的樂樂表現的很氣憤。于是小韋查了樂樂的位置,并行使了隊長權利將其踢出了隊伍(因為 小韋知道樂樂未來會是一位IT大神,要讓樂樂掛掉先)。
刪除某個節點:
bool ListDelete(LinkList *&L, int i) //找到樂樂的位置 {int j = 0;LinkList *p = L, *q;while (j < i - 1 && p != NULL) {j++;p = p->next;}if (p == NULL) //如果找錯了,不存在i節點,說明小韋的 數學是體育老師教的,return false;else{q = p->next; //如果找到了樂樂,把樂樂一覺踹出隊伍,再讓樂樂前面的小朋友的手拉著樂樂后面 的小朋友的衣服if (q == NULL){return false;}p->next = q->next; free(q);return true;} }雜七雜八的事情終于弄完了,于是 小韋也歸隊。(因為他 不想再單獨被沖走了)
插入數據元素:
code:
bool ListInsert(LinkList *&L, int i, int e) //小韋在某個位置上插入隊伍 {int j = 0;LinkList *p = L, *s;while (j < i - 1 && p != NULL){j++;p = p->next;}if (p == NULL)return false;else{s = (LinkList *)malloc(sizeof(LinkList));s->data = e;s->next = p->next;p->next = s;return true;} }那么 現在開始過河了,由于河水 ?突然猛漲,小韋學長竟然又被沖走了。小伙伴們需要抓的更緊點,于是它們退回來重新 建隊。并將抓衣服的方式 更改了一下:讓前一個小朋友的手抓住后一個小朋友的衣服,后一個小朋友的手抓住他前面的小朋友的衣服。即(雙鏈表)
相應的此時建雙鏈表的方法也有兩種,與建立單鏈表過程相似,只需要加一個前驅結點即可:
code:
typedef struct Lnode {int data;struct Lnode *prior; //前驅節點struct Lnode *next; //后繼節點 }LinkList;第一種建立方式頭插法(與單鏈表頭插法相似):
code:
void CreateList_F(LinkList *&L, int a[], int n) {LinkList *s;int i;L = (LinkList*)malloc(sizeof(LinkList));L->prior = L->next = NULL;for (i = 0; i < n; i++){s = (LinkList *)malloc(sizeof(LinkList));s->data = a[i];s->next = L->next;if (L->next != NULL){L->next->prior = s;}L->next = s;s->prior = L;} }code:
void CreatList_R(LinkList *&L, int a[], int n) {int i;LinkList *s, *r;r = L;for (i = 0; i < n; i++){s = (LinkList*)malloc(sizeof(LinkList));s->data = a[i];r->next = s;s->prior = r;}r->next = L->next; }循環鏈表:
循環鏈表 只是將鏈表 的尾部節點的 next指向了鏈表 的開頭L;
下面我總結下代碼:
單鏈表:
typedef struct Lnode //數據節點 {int data;struct Lnode *next; }LinkList;void InitList(LinkList *&L) //創建空的單鏈表 {L = (LinkList *)malloc(sizeof(LinkList));L->next = NULL; }void CreateListH(LinkList *&L, int a[], int n) //頭插法建立單鏈表 {LinkList *s; int i;L = (LinkList *)malloc(sizeof(LinkList)); L是 鏈表的頭L->next = NULL; for (i = 0; i < n; i++) //將數據插入鏈表頭(L)的后面{s = (LinkList *)malloc(sizeof(LinkList));s->data = a[i];s->next = L->next; //即:s指向 L的 指向,L指向s;L->next = s;} }void CreateListR(LinkList *&L, int a[], int n) //尾插法建立單鏈表 {int i;LinkList *s, *r;L = (LinkList *)malloc(sizeof(LinkList)); //申請空間。開始時L雖然是表頭 ,同時也是尾r = L;for (i = 0; i < n; i++) // //將下一個 數據插入隊尾的后面,再讓該數據成為新的隊尾{s = (LinkList *)malloc(sizeof(LinkList));s->data = a[i];r->next = s; //r就是隊尾部,將 s插入r的后面,r = s; //插入后,那么s在最后的位置,讓其成為標記?,F在s就是新的隊尾}r->next = L->next; //隊伍建立完成后,最后 隊尾節點為 NULL; } </pre><pre code_snippet_id="1630697" snippet_file_name="blog_20160331_15_6131477" name="code" class="cpp"> int ListLength(LinkList *L) //返回單鏈表的長度 {int ans = 0;LinkList *p = L; //(要注意是LinkList * p = L)while (p->next != NULL) //如果 下一個節點不為空,肯定是有數據存儲的,于是ans++;{ans++;p = p->next;}return ans; }void CoutList(LinkList *L) //輸出每個節點的信息 {LinkList *p = L->next; //從頭開始(要注意是LinkList *p = L->next)while (p != NULL) //如果當前節點不為空,則 輸出信息{printf("%d", p->data);p = p->next;} }bool GetElem(LinkList *&L, int i, int &e) //修改某個位置上的數據信息 {int j = 0;LinkList *p = L;while (j < i && p != NULL) //如果沒有遍歷到i-1并且鏈表沒有結束{j++; //接著找p = p->next;}if (p == NULL) //如果結束時是因為鏈表結束了,則說明 i超出了范圍return false;else //否則,修改信息{p->data = e;return true;} }bool ListDelete(LinkList *&L, int i) //刪除某個節點 {int j = 0;LinkList *p = L, *q;while (j < i - 1 && p != NULL){j++;p = p->next;}if (p == NULL)return false;else //如果找到了該節點,{q = p->next; //那么讓當前節點的后記節點指向它后面 的后面,就相當于把 這個節點隔出來了if (q == NULL){return false;}p->next = q->next;free(q);return true;} }bool ListInsert(LinkList *&L, int i, int e) // 插入節點 {int j = 0;LinkList *p = L, *s;while (j < i - 1 && p != NULL){j++;p = p->next;}if (p == NULL)return false;else //如果 找到位置,先讓要插入的s節點指向當前節點的后繼,并讓當前節點的后繼指向s。{s = (LinkList *)malloc(sizeof(LinkList));s->data = e;s->next = p->next;p->next = s;return true;} }雙鏈表:
循環鏈表 ?的就不寫了,因為只需要 讓尾節點和頭結點關聯上就行了,不過要注意雙鏈表循環和單鏈表循環是有一定區別的。但是本質不變。
希望 ?這篇文章 可以 幫助你更好的理解或者復習鏈表操作?。
如有錯,請留言。
總結
以上是生活随笔為你收集整理的数据结构之:链表详解的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: C语言链表实现
- 下一篇: 单向链表的C语言实现与基本操作