ios -鏈表的簡單認(rèn)識

作為一個資深的新手程序員??,鏈表這些既基礎(chǔ)又深奧的東西是日常工作中并不常見,但是卻非常重要,所以就總結(jié)一下鏈表的簡單認(rèn)識!

數(shù)組作為存放同類數(shù)據(jù)的集合,給我們在程序設(shè)計時帶來很多的方便,增加了靈活性。但數(shù)組也同樣存在一些弊病。如數(shù)組的大小在定義時要事先規(guī)定,不能在程序中進(jìn)行調(diào)整,這樣一來,在程序設(shè)計中針對不同問題有時需要3 0個大小的數(shù)組,有時需要5 0個數(shù)組的大小,難于統(tǒng)一。我們只能夠根據(jù)可能的最大需求來定義數(shù)組,常常會造成一定存儲空間的浪費(fèi)。

我們希望構(gòu)造動態(tài)的數(shù)組,隨時可以調(diào)整數(shù)組的大小,以滿足不同問題的需要。鏈表就是我們需要的動態(tài)數(shù)組。它是在程序的執(zhí)行過程中根據(jù)需要有數(shù)據(jù)存儲就向系統(tǒng)要求申請存儲空間,決不構(gòu)成對存儲區(qū)的浪費(fèi)。

鏈表(Linkedlist)是一種常見的基礎(chǔ)數(shù)據(jù)結(jié)構(gòu),是一種線性表,是一種物理存儲單元上非連續(xù)、非順序的存儲結(jié)構(gòu)。數(shù)據(jù)元素的邏輯順序是通過鏈表中的指針鏈接次序?qū)崿F(xiàn)的,但是并不會按線性的順序存儲數(shù)據(jù),鏈表通常由一連串節(jié)點(diǎn)組成,每個節(jié)點(diǎn)包含任意的實例數(shù)據(jù)(datafields)和一或兩個用來指向上一個/或下一個節(jié)點(diǎn)的位置的鏈接("links")。在計算機(jī)科學(xué)中,鏈表作為一種基礎(chǔ)的數(shù)據(jù)結(jié)構(gòu)可以用來生成其它類型的數(shù)據(jù)結(jié)構(gòu),其數(shù)據(jù)之間的相互關(guān)系使鏈表分成三種:單鏈表、循環(huán)鏈表、雙向鏈表。


鏈表最基本的結(jié)構(gòu)是在每個節(jié)點(diǎn)保存數(shù)據(jù)和到下一個節(jié)點(diǎn)的地址,在最后一個節(jié)點(diǎn)保存一個特殊的結(jié)束標(biāo)記。另外在一個固定的位置保存指向第一個節(jié)點(diǎn)的指針,有的時候也會同時儲存指向最后一個節(jié)點(diǎn)的指針。但是也可以提前把一個節(jié)點(diǎn)的位置另外保存起來,然后直接訪問。當(dāng)然如果只是訪問數(shù)據(jù)就沒必要了,不如在鏈表上儲存指向?qū)嶋H數(shù)據(jù)的指針。這樣一般是為了訪問鏈表中的下一個或者前一個節(jié)點(diǎn)。

優(yōu)勢:可以克服數(shù)組鏈表需要預(yù)先知道數(shù)據(jù)大小的缺點(diǎn),鏈表結(jié)構(gòu)可以充分利用計算機(jī)內(nèi)存空間,實現(xiàn)靈活的內(nèi)存動態(tài)管理。鏈表最明顯的好處就是,常規(guī)數(shù)組排列關(guān)聯(lián)項目的方式可能不同于這些數(shù)據(jù)項目在記憶體或磁盤上順序,數(shù)據(jù)的存取往往要在不同的排列順序中轉(zhuǎn)換。而鏈表是一種自我指示數(shù)據(jù)類型,因為它包含指向另一個相同類型的數(shù)據(jù)的指針(鏈接),同時,鏈表允許插入和移除表上任意位置上的節(jié)點(diǎn)。

劣勢:鏈表由于增加了結(jié)點(diǎn)的指針域,空間開銷比較大;另外,鏈表失去了數(shù)組隨機(jī)讀取的優(yōu)點(diǎn),一般查找一個節(jié)點(diǎn)的時候需要從第一個節(jié)點(diǎn)開始每次訪問下一個節(jié)點(diǎn),一直訪問到需要的位置。

單向鏈表

鏈表中最簡單的一種是單向鏈表,

一個單向鏈表的節(jié)點(diǎn)被分成兩個部分。它包含兩個域,一個信息域和一個指針域。第一個部分保存或者顯示關(guān)于節(jié)點(diǎn)的信息,第二個部分存儲下一個節(jié)點(diǎn)的地址,而最后一個節(jié)點(diǎn)則指向一個空值。單向鏈表只可向一個方向遍歷。

單鏈表有一個頭節(jié)點(diǎn)head,指向鏈表在內(nèi)存的首地址。鏈表中的每一個節(jié)點(diǎn)的數(shù)據(jù)類型為結(jié)構(gòu)體類型,節(jié)點(diǎn)有兩個成員:整型成員(實際需要保存的數(shù)據(jù))和指向下一個結(jié)構(gòu)體類型節(jié)點(diǎn)的指針即下一個節(jié)點(diǎn)的地址(事實上,此單鏈表是用于存放整型數(shù)據(jù)的動態(tài)數(shù)組)。鏈表按此結(jié)構(gòu)對各節(jié)點(diǎn)的訪問需從鏈表的頭找起,后續(xù)節(jié)點(diǎn)的地址由當(dāng)前節(jié)點(diǎn)給出。無論在表中訪問那一個節(jié)點(diǎn),都需要從鏈表的頭開始,順序向后查找。鏈表的尾節(jié)點(diǎn)由于無后續(xù)節(jié)點(diǎn),其指針域為空,寫作為NULL


單向鏈表

上圖還給出這樣一層含義,鏈表中的各節(jié)點(diǎn)在內(nèi)存的存儲地址不是連續(xù)的,其各節(jié)點(diǎn)的地址是在需要時向系統(tǒng)申請分配的,系統(tǒng)根據(jù)內(nèi)存的當(dāng)前情況,既可以連續(xù)分配地址,也可以跳躍式分配地址。

單向鏈表程序的實現(xiàn)

(1),鏈表節(jié)點(diǎn)的數(shù)據(jù)結(jié)構(gòu)定義

struct node

{

int num;

struct node?*p;

}?;

在鏈表節(jié)點(diǎn)的定義中,除一個整型的成員外,成員p是指向與節(jié)點(diǎn)類型完全相同的指針。

在鏈表節(jié)點(diǎn)的數(shù)據(jù)結(jié)構(gòu)中,非常特殊的一點(diǎn)就是結(jié)構(gòu)體內(nèi)的指針域的數(shù)據(jù)類型使用了未定義成功的數(shù)據(jù)類型。這是在C中唯一規(guī)定可以先使用后定義的數(shù)據(jù)結(jié)構(gòu)。

(2),鏈表的創(chuàng)建、輸出步驟

單鏈表的創(chuàng)建過程有以下幾步:

1 ) 定義鏈表的數(shù)據(jù)結(jié)構(gòu);

2 ) 創(chuàng)建一個空表;

3 ) 利用malloc ( )函數(shù)向系統(tǒng)申請分配一個節(jié)點(diǎn);

4 ) 將新節(jié)點(diǎn)的指針成員賦值為空。若是空表,將新節(jié)點(diǎn)連接到表頭;若是非空表,將新

節(jié)點(diǎn)接到表尾;

5 ) 判斷一下是否有后續(xù)節(jié)點(diǎn)要接入鏈表,若有轉(zhuǎn)到3 ),否則結(jié)束;

單鏈表的輸出過程有以下幾步

1) 找到表頭;

2) 若是非空表,輸出節(jié)點(diǎn)的值成員,是空表則退出;

3 ) 跟蹤鏈表的增長,即找到下一個節(jié)點(diǎn)的地址;

4) 轉(zhuǎn)到2 ).

(3),程序代碼例子:

創(chuàng)建一個存放正整數(shù)單鏈表,輸入0或小于0的數(shù),結(jié)束創(chuàng)建鏈表,并打印出鏈表中的值,程序如下:

#include ?<stdlib.h>/*含ma l l o c ( ) 的頭文件*/

#include <stdio.h>

struct node//①定義鏈表數(shù)據(jù)結(jié)構(gòu)

{

int num;

struct node?*next;

};

main(?)

{

struct node?*creat();

void print();

struct node?*head;

head=NULL;//②建一個空表

head=creat(head);/*創(chuàng)建單鏈表*/

print(head);/*打印單鏈表*/

}

/******************************************/

struct node*creat(struct node?*head)/*返回的是與節(jié)點(diǎn)相同類型的指針*/

{

struct node*p1,*p2;

//③利用malloc?(?)函數(shù)向系統(tǒng)申請分配一個節(jié)點(diǎn)

p1=p2=(structnode*)malloc(sizeof(struct node));/*新節(jié)點(diǎn)*/

printf("p1=?%d\n",p1);

scanf("%d",&p1->num);/*輸入節(jié)點(diǎn)的值*/

p1->next=NULL;/*將新節(jié)點(diǎn)的指針置為空*/

while(p1->num>0)/*輸入節(jié)點(diǎn)的數(shù)值大于0*/

{

//④將新節(jié)點(diǎn)的指針成員賦值為空。若是空表,將新節(jié)點(diǎn)連接到表頭;若是非空表,將新

//節(jié)點(diǎn)接到表尾;

if(head==NULL)

head=p1;/*空表,接入表頭*/

else

p2->next=p1;/*非空表,接到表尾*/

p2=p1;

p1=(struct node*)malloc(sizeof(struct node));/*下一個新節(jié)點(diǎn)*/

printf("p2=?%d\n",p2);

scanf("%d",&p1->num);/*輸入節(jié)點(diǎn)的值*/

//⑤判斷一下是否有后續(xù)節(jié)點(diǎn)要接入鏈表,若有轉(zhuǎn)到3?),否則結(jié)束;

}

printf("p2->next=%d\n",p2->next);

return head;/*返回鏈表的頭指針*/

}

/*******************************************/

void print(struct node*head)/*出以head為頭的鏈表各節(jié)點(diǎn)的值*/

{

struct node?*temp;

temp=head;/*取得鏈表的頭指針*/

while(temp!=NULL)/*只要是非空表*/

{

printf("%6d",temp->num);/*輸出鏈表節(jié)點(diǎn)的值*/

temp=temp->next;/*跟蹤鏈表增長*/

}

}


編譯后執(zhí)行:

p1= 140660744 ?//鏈表頭

1

p2= 140660760

45

p2= 140660776

6

p2= 140660792

77

p2= 140660808

88

p2= 140660824

0 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? //輸入0結(jié)束增加鏈表

p2->next=0 ? ? ? //鏈表結(jié)尾

1 ? ? ? 45????6??? 77??? 88 ? ? ? ? ?//輸出鏈表的值。

在鏈表的創(chuàng)建過程中,鏈表的頭指針是非常重要的參數(shù)。因為對鏈表的輸出和查找都要從鏈表的頭開始,所以鏈表創(chuàng)建成功后,要返回一個鏈表頭節(jié)點(diǎn)的地址,即頭指針。

程序執(zhí)行流程:

程序執(zhí)行流程

雙向鏈表

雙向鏈表其實是單鏈表的改進(jìn),當(dāng)我們對單鏈表進(jìn)行操作時,有時你要對某個結(jié)點(diǎn)的直接前驅(qū)進(jìn)行操作時,又必須從表頭開始查找。這是由單鏈表結(jié)點(diǎn)的結(jié)構(gòu)所限制的。因為單鏈表每個結(jié)點(diǎn)只有一個存儲直接后繼結(jié)點(diǎn)地址的鏈域,那么能不能定義一個既有存儲直接后繼結(jié)點(diǎn)地址的鏈域,又有存儲直接前驅(qū)結(jié)點(diǎn)地址的鏈域的這樣一個雙鏈域結(jié)點(diǎn)結(jié)構(gòu)呢?這就是雙向鏈表。

在雙向鏈表中,結(jié)點(diǎn)除含有數(shù)據(jù)域外,還有兩個鏈域,一個存儲直接后繼結(jié)點(diǎn)地址,一般稱之為右鏈域(當(dāng)此“連接”為最后一個“連接”時,指向空值或者空列表);一個存儲直接前驅(qū)結(jié)點(diǎn)地址,一般稱之為左鏈域(當(dāng)此“連接”為第一個“連接”時,指向空值或者空列表)。

typedef struct node

{

int data; /*數(shù)據(jù)域*/

struct node *llink,*rlink; /*鏈域,*llink是左鏈域指針,*rlink是右鏈域指針*/

}JD;

當(dāng)然,也可以把一個雙向鏈表構(gòu)建成一個雙向循環(huán)鏈表。

雙向鏈表與單向鏈表一樣,也有三種基本運(yùn)算:查找、插入和刪除, ?后續(xù)我會寫出來分享給大家!

循環(huán)鏈表

循環(huán)鏈表是與單向鏈表一樣,是一種鏈?zhǔn)降拇鎯Y(jié)構(gòu),所不同的是,循環(huán)鏈表的最后一個結(jié)點(diǎn)的指針是指向該循環(huán)鏈表的第一個結(jié)點(diǎn)或者表頭結(jié)點(diǎn),從而構(gòu)成一個環(huán)形的鏈。

循環(huán)鏈表的運(yùn)算與單鏈表的運(yùn)算基本一致。所不同的有以下幾點(diǎn):

1、在建立一個循環(huán)鏈表時,必須使其最后一個結(jié)點(diǎn)的指針指向表頭結(jié)點(diǎn),而不是象單鏈表那樣置為NULL。此種情況還使用于在最后一個結(jié)點(diǎn)后插入一個新的結(jié)點(diǎn)。

2、在判斷是否到表尾時,是判斷該結(jié)點(diǎn)鏈域的值是否是表頭結(jié)點(diǎn),當(dāng)鏈域值等于表頭指針時,說明已到表尾。而非象單鏈表那樣判斷鏈域值是否為NULL。

塊狀鏈表

塊狀鏈表本身是一個鏈表,但是鏈表儲存的并不是一般的數(shù)據(jù),而是由這些數(shù)據(jù)組成的順序表。每一個塊狀鏈表的節(jié)點(diǎn),也就是順序表,可以被叫做一個塊。

塊狀鏈表另一個特點(diǎn)是相對于普通鏈表來說節(jié)省內(nèi)存,因為不用保存指向每一個數(shù)據(jù)節(jié)點(diǎn)的指針。

鏈表的提出主要在于:順序存儲中的插入和刪除的時間復(fù)雜度是線性時間的,而鏈表的操作則可以是常數(shù)時間的復(fù)雜度。

鏈表的插入與刪除操作順序:

插入操作處理順序:中間節(jié)點(diǎn)的邏輯,后節(jié)點(diǎn)邏輯,前節(jié)點(diǎn)邏輯。

刪除操作的處理順序:前節(jié)點(diǎn)邏輯,后節(jié)點(diǎn)邏輯,中間節(jié)點(diǎn)邏輯。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容