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