一、認識鏈表
鏈表是一種物理存儲單元上非連續、非順序的存儲結構,數據元素的邏輯順序是通過鏈表中的指針鏈接次序實現的。鏈表由一系列結點(鏈表中每一個元素稱為結點)組成,結點可以在運行時動態生成。每個結點包括兩個部分:一個是存儲數據元素的數據域,另一個是存儲下一個結點地址的指針域。——百度百科
如圖,上面是一個標准的單鏈表,並且有一個頭指針head指向鏈表的第一個元素。
二、建立鏈表
現在來建立一個最簡單的鏈表。
1.創建存儲結點的結構體
首先,我們需要建立適當的結構體來存儲結點。
typedef struct node
{
int val;//一個節點的值
struct node *next;//自引用,實現鏈表“隨用隨開”的動態特點
} myLink;
為什么在struct
之前加typedef
?
解釋:typedef
為后面的這個復雜的類型
struct node
{
int val;
struct node *next;
}
起了個名字myLink
,此后若想創造一個結點a,只需用
myLink* a;
即可,這和定義整型變量a
int a;
是一樣的道理。
寫好存儲結點的結構體之后,我們來創建這個鏈表的第一個結點。
2.創建節點
我們要建立的鏈表為動態鏈表,“動態”是其一大特點,也就是要實現按需自動開辟新的節點。
malloc()
函數可以分配一塊內存空間,並返回一個指向這塊內存空間的指針。
如果我們用malloc()
函數分配一塊大小等於結點所占空間大小的內存空間,然后想辦法把malloc()
函數返回的指針賦給一個我們自己定義的指針,那豈不是實現了開辟結點的操作?
myLink *temp=malloc(sizeof(myLink));
這里,我們用sizeof()
函數獲取一個結點的大小,然后用malloc()
函數在內存中開辟一塊這么大的空間來存放結點,最后malloc()
函數返回一個指向這個節點的指針便於我們對這個節點進行各種操作。在此,我們用temp指針來進行對這個鏈表的操作。
為了以后方便遍歷這個鏈表,我們還需要一個指針head
指向鏈表的最開頭。而temp
指針現在正好指向鏈表頭部,那么我們再加上這句話。
myLink *temp=malloc(sizeof(myLink));
myLink* head=temp;
現在,我們得到了鏈表的第一個節點。
由於現在只有這一個結點,該結點的next
指針無法指向任何一個結點,所以我們讓他指向NULL
得了。
myLink *temp=malloc(sizeof(myLink));
myLink* head=temp;
temp->next=NULL;//temp->指針
有些情況下,在開始正式創建鏈表的第一個結點之前,需要在第一個結點之前再創造一個結點以方便進行后續的某些處理,這個結點就叫頭結點。我們可以把現在得到的這個結點初始化為頭節點,當然也可以跳過這一步,直接正式建立鏈表。
myLink *temp=malloc(sizeof(myLink));
myLink* head=temp;
temp->next=NULL;//temp->指針
temp->val=-1; //把頭節點的值設為-1
3.正式創建鏈表
假設我們在正式創建鏈表的時候,需要加入n個元素。我們可以寫一個1到n的for循環來操作。
首先,我們讀入新元素的值,並且開辟一塊內存空間存放結點,把這個值賦給該新節點,再把這個結點的next指針指向NULL
。
int val;
scanf("%d",&val);
myLink* newNode=malloc(sizeof(myLink));
newNode->val=val;
newNode->next=NULL;
(假設新結點的元素值為3)
然后,我們讓頭結點的指針temp指向新的節點。
int val;
scanf("%d",&val);
myLink* newNode=malloc(sizeof(myLink));
newNode->val=val;
newNode->next=NULL;
temp->next=newNode;
這樣,兩個結點便連接在了一起。新結點宣告添加成功。
為了給下一個鏈表進行操作,temp指針需要指向剛剛添加的新結點。
int val;
scanf("%d",&val);
myLink* newNode=malloc(sizeof(myLink));
newNode->next=NULL;
newNode->val=val;
temp->next=newNode;
temp=newNode;
這樣,我們完成了添加一個結點的所有操作。把這些代碼寫進循環,就可以一直給這條鏈表增加元素了。
for(int i=1;i<=n;i++)
{
int val;
scanf("%d",&val);
myLink* newNode=malloc(sizeof(myLink));
newNode->next=NULL;
newNode->val=val;
temp->next=newNode;
temp=newNode;
}
我們把所有的代碼都寫進函數
myLink* createList()
{
myLink* temp=malloc(sizeof(myLink));
temp->next=NULL;
myLink* head=temp;
temp->val=-1;
for(int i=1;i<=n;i++)
{
int val;
scanf("%d",&val);
myLink* newNode=malloc(sizeof(myLink));
newNode->next=NULL;
newNode->val=val;
temp->next=newNode;
temp=newNode;
}
return head;
}
最后一步,我們返回指向鏈表頭部的指針,以便對鏈表進行各種操作。
三、鏈表的基本操作
鏈表的基本操作包括結點的增加、刪除、查詢、修改、鏈表的打印,以及獲取鏈表長度等。
由於許多操作其實都是一個道理,在此,我只介紹三種基本操作:遍歷鏈表、插入結點、刪除結點。
1.遍歷鏈表
我們現在建立好了鏈表,並且得到了指向它開頭的指針
myLink* p=createList();
我們來寫一個函數遍歷這個鏈表。假設我們遍歷鏈表的目的是按順序打印所有元素
void printLink(myLink* p);