作為一個資深的新手程序員??,鏈表這些既基礎又深奧的東西是日常工作中并不常見,但是卻非常重要,所以就總結一下鏈表的簡單認識!
數組作為存放同類數據的集合,給我們在程序設計時帶來很多的方便,增加了靈活性。但數組也同樣存在一些弊病。如數組的大小在定義時要事先規定,不能在程序中進行調整,這樣一來,在程序設計中針對不同問題有時需要3 0個大小的數組,有時需要5 0個數組的大小,難于統一。我們只能夠根據可能的最大需求來定義數組,常常會造成一定存儲空間的浪費。
我們希望構造動態的數組,隨時可以調整數組的大小,以滿足不同問題的需要。鏈表就是我們需要的動態數組。它是在程序的執行過程中根據需要有數據存儲就向系統要求申請存儲空間,決不構成對存儲區的浪費。
鏈表(Linkedlist)是一種常見的基礎數據結構,是一種線性表,是一種物理存儲單元上非連續、非順序的存儲結構。數據元素的邏輯順序是通過鏈表中的指針鏈接次序實現的,但是并不會按線性的順序存儲數據,鏈表通常由一連串節點組成,每個節點包含任意的實例數據(datafields)和一或兩個用來指向上一個/或下一個節點的位置的鏈接("links")。在計算機科學中,鏈表作為一種基礎的數據結構可以用來生成其它類型的數據結構,其數據之間的相互關系使鏈表分成三種:單鏈表、循環鏈表、雙向鏈表。
鏈表最基本的結構是在每個節點保存數據和到下一個節點的地址,在最后一個節點保存一個特殊的結束標記。另外在一個固定的位置保存指向第一個節點的指針,有的時候也會同時儲存指向最后一個節點的指針。但是也可以提前把一個節點的位置另外保存起來,然后直接訪問。當然如果只是訪問數據就沒必要了,不如在鏈表上儲存指向實際數據的指針。這樣一般是為了訪問鏈表中的下一個或者前一個節點。
優勢:可以克服數組鏈表需要預先知道數據大小的缺點,鏈表結構可以充分利用計算機內存空間,實現靈活的內存動態管理。鏈表最明顯的好處就是,常規數組排列關聯項目的方式可能不同于這些數據項目在記憶體或磁盤上順序,數據的存取往往要在不同的排列順序中轉換。而鏈表是一種自我指示數據類型,因為它包含指向另一個相同類型的數據的指針(鏈接),同時,鏈表允許插入和移除表上任意位置上的節點。
劣勢:鏈表由于增加了結點的指針域,空間開銷比較大;另外,鏈表失去了數組隨機讀取的優點,一般查找一個節點的時候需要從第一個節點開始每次訪問下一個節點,一直訪問到需要的位置。
單向鏈表
鏈表中最簡單的一種是單向鏈表,
一個單向鏈表的節點被分成兩個部分。它包含兩個域,一個信息域和一個指針域。第一個部分保存或者顯示關于節點的信息,第二個部分存儲下一個節點的地址,而最后一個節點則指向一個空值。單向鏈表只可向一個方向遍歷。
單鏈表有一個頭節點head,指向鏈表在內存的首地址。鏈表中的每一個節點的數據類型為結構體類型,節點有兩個成員:整型成員(實際需要保存的數據)和指向下一個結構體類型節點的指針即下一個節點的地址(事實上,此單鏈表是用于存放整型數據的動態數組)。鏈表按此結構對各節點的訪問需從鏈表的頭找起,后續節點的地址由當前節點給出。無論在表中訪問那一個節點,都需要從鏈表的頭開始,順序向后查找。鏈表的尾節點由于無后續節點,其指針域為空,寫作為NULL。
上圖還給出這樣一層含義,鏈表中的各節點在內存的存儲地址不是連續的,其各節點的地址是在需要時向系統申請分配的,系統根據內存的當前情況,既可以連續分配地址,也可以跳躍式分配地址。
單向鏈表程序的實現
(1),鏈表節點的數據結構定義
struct node
{
int num;
struct node?*p;
}?;
在鏈表節點的定義中,除一個整型的成員外,成員p是指向與節點類型完全相同的指針。
在鏈表節點的數據結構中,非常特殊的一點就是結構體內的指針域的數據類型使用了未定義成功的數據類型。這是在C中唯一規定可以先使用后定義的數據結構。
(2),鏈表的創建、輸出步驟
單鏈表的創建過程有以下幾步:
1 ) 定義鏈表的數據結構;
2 ) 創建一個空表;
3 ) 利用malloc ( )函數向系統申請分配一個節點;
4 ) 將新節點的指針成員賦值為空。若是空表,將新節點連接到表頭;若是非空表,將新
節點接到表尾;
5 ) 判斷一下是否有后續節點要接入鏈表,若有轉到3 ),否則結束;
單鏈表的輸出過程有以下幾步
1) 找到表頭;
2) 若是非空表,輸出節點的值成員,是空表則退出;
3 ) 跟蹤鏈表的增長,即找到下一個節點的地址;
4) 轉到2 ).
(3),程序代碼例子:
創建一個存放正整數單鏈表,輸入0或小于0的數,結束創建鏈表,并打印出鏈表中的值,程序如下:
#include ?<stdlib.h>/*含ma l l o c ( ) 的頭文件*/
#include <stdio.h>
struct node//①定義鏈表數據結構
{
int num;
struct node?*next;
};
main(?)
{
struct node?*creat();
void print();
struct node?*head;
head=NULL;//②建一個空表
head=creat(head);/*創建單鏈表*/
print(head);/*打印單鏈表*/
}
/******************************************/
struct node*creat(struct node?*head)/*返回的是與節點相同類型的指針*/
{
struct node*p1,*p2;
//③利用malloc?(?)函數向系統申請分配一個節點
p1=p2=(structnode*)malloc(sizeof(struct node));/*新節點*/
printf("p1=?%d\n",p1);
scanf("%d",&p1->num);/*輸入節點的值*/
p1->next=NULL;/*將新節點的指針置為空*/
while(p1->num>0)/*輸入節點的數值大于0*/
{
//④將新節點的指針成員賦值為空。若是空表,將新節點連接到表頭;若是非空表,將新
//節點接到表尾;
if(head==NULL)
head=p1;/*空表,接入表頭*/
else
p2->next=p1;/*非空表,接到表尾*/
p2=p1;
p1=(struct node*)malloc(sizeof(struct node));/*下一個新節點*/
printf("p2=?%d\n",p2);
scanf("%d",&p1->num);/*輸入節點的值*/
//⑤判斷一下是否有后續節點要接入鏈表,若有轉到3?),否則結束;
}
printf("p2->next=%d\n",p2->next);
return head;/*返回鏈表的頭指針*/
}
/*******************************************/
void print(struct node*head)/*出以head為頭的鏈表各節點的值*/
{
struct node?*temp;
temp=head;/*取得鏈表的頭指針*/
while(temp!=NULL)/*只要是非空表*/
{
printf("%6d",temp->num);/*輸出鏈表節點的值*/
temp=temp->next;/*跟蹤鏈表增長*/
}
}
編譯后執行:
p1= 140660744 ?//鏈表頭
1
p2= 140660760
45
p2= 140660776
6
p2= 140660792
77
p2= 140660808
88
p2= 140660824
0 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? //輸入0結束增加鏈表
p2->next=0 ? ? ? //鏈表結尾
1 ? ? ? 45????6??? 77??? 88 ? ? ? ? ?//輸出鏈表的值。
在鏈表的創建過程中,鏈表的頭指針是非常重要的參數。因為對鏈表的輸出和查找都要從鏈表的頭開始,所以鏈表創建成功后,要返回一個鏈表頭節點的地址,即頭指針。
程序執行流程:
雙向鏈表
雙向鏈表其實是單鏈表的改進,當我們對單鏈表進行操作時,有時你要對某個結點的直接前驅進行操作時,又必須從表頭開始查找。這是由單鏈表結點的結構所限制的。因為單鏈表每個結點只有一個存儲直接后繼結點地址的鏈域,那么能不能定義一個既有存儲直接后繼結點地址的鏈域,又有存儲直接前驅結點地址的鏈域的這樣一個雙鏈域結點結構呢?這就是雙向鏈表。
在雙向鏈表中,結點除含有數據域外,還有兩個鏈域,一個存儲直接后繼結點地址,一般稱之為右鏈域(當此“連接”為最后一個“連接”時,指向空值或者空列表);一個存儲直接前驅結點地址,一般稱之為左鏈域(當此“連接”為第一個“連接”時,指向空值或者空列表)。
typedef struct node
{
int data; /*數據域*/
struct node *llink,*rlink; /*鏈域,*llink是左鏈域指針,*rlink是右鏈域指針*/
}JD;
當然,也可以把一個雙向鏈表構建成一個雙向循環鏈表。
雙向鏈表與單向鏈表一樣,也有三種基本運算:查找、插入和刪除, ?后續我會寫出來分享給大家!
循環鏈表
循環鏈表是與單向鏈表一樣,是一種鏈式的存儲結構,所不同的是,循環鏈表的最后一個結點的指針是指向該循環鏈表的第一個結點或者表頭結點,從而構成一個環形的鏈。
循環鏈表的運算與單鏈表的運算基本一致。所不同的有以下幾點:
1、在建立一個循環鏈表時,必須使其最后一個結點的指針指向表頭結點,而不是象單鏈表那樣置為NULL。此種情況還使用于在最后一個結點后插入一個新的結點。
2、在判斷是否到表尾時,是判斷該結點鏈域的值是否是表頭結點,當鏈域值等于表頭指針時,說明已到表尾。而非象單鏈表那樣判斷鏈域值是否為NULL。
塊狀鏈表
塊狀鏈表本身是一個鏈表,但是鏈表儲存的并不是一般的數據,而是由這些數據組成的順序表。每一個塊狀鏈表的節點,也就是順序表,可以被叫做一個塊。
塊狀鏈表另一個特點是相對于普通鏈表來說節省內存,因為不用保存指向每一個數據節點的指針。
鏈表的提出主要在于:順序存儲中的插入和刪除的時間復雜度是線性時間的,而鏈表的操作則可以是常數時間的復雜度。
鏈表的插入與刪除操作順序:
插入操作處理順序:中間節點的邏輯,后節點邏輯,前節點邏輯。
刪除操作的處理順序:前節點邏輯,后節點邏輯,中間節點邏輯。