一、單向鏈表
在鏈表中,我們爲了表示前一個數據和後一個數據的邏輯對於關係,比如說兩個數據,ai,ai+1。那麼在ai中除了存儲其本身的數據,還需要存儲指示其後繼信息,舉例爲:ai+1的地址。我們把存儲數據元素信息的域稱爲數據域,把存儲直接後繼位置的域稱爲指針域。指針域中存儲的信息稱做指針或鏈。這兩部分信息組成數據元素ai的存儲映像,稱爲結點(Node)。
第一個節點的存儲位置叫做頭指針。最後一個節點的的指針指向NULL。
結點的存儲結構:
struct node {
//data,這裏用int類型代替
int data;
struct node* next;
};
node* listhead;//定義了頭指針
鏈表的創建:
創建表的過程,這個操作是動態的,那麼我們需要以此建立並插入。需要聲明一指針,p,計數器變量。每創建一個結點,就把上一個結點的指針指向新創建的結點,然後在此節點輸入數據,以此類推。個人傾向於創建時檢查指針是非爲空防止程序崩潰。
void creat(node* head,int n)
{
node* p;
node* r;
int i;
head=(struct node *)malloc(sizeof(node));
//讓link指向新的空間,但頭結點無數據!
r=head; //r爲指向尾部的結點
listhead = head;
//listhead爲頭指針,head爲當前頭指針,listhead是全局變量
for (i = 0; i < n; i++)
{
if(p = (struct node*)malloc(sizeof(node)))
//檢查是否創建成功,也可省略,不建議省略
{
p->data = rand() % 100;//隨機插入一個數作爲data
r->next = p;
r = p;
}
else
{
return;
}
}
if (r == NULL) //防止爲空
return;
r->next = NULL;
}
單鏈表的插入
這步驟很簡單,假設存儲單元爲e,只需創建一個結點,新節點的指針等於上一個結點的指針指向(就是新節點的下一個結點的位置),上一個結點的指針等於新結點的地址。
//插入,假設在位置i插入一個結點,數據爲e
void insert( int i, int e)
{
int j = 1;
node* p;
p = listhead;
while (p && j < i)//尋找第i個結點
{
p = p->next;
j++;
}
if (!p || j>i)
return;
node* s;
s = (struct node*)malloc(sizeof(node)); //分配空間,創造一個結點s,則結點p在s的上一位
if (s == NULL)
return;
s->data = e;
s->next = p->next;
p->next = s;
return;
}
單鏈表的刪除操作
遍歷鏈表,找到符合條件的,若未找到,則說明不存在,找到則要刪除的結點的指針,先賦值,q=p->next,p->next=q->next,意思爲p的後繼的後繼結點改爲p的後繼結點。
void Delete (int i)//傳入參數隨條件可以改變
{
int j = 1;
node* p;
node* q;
p = listhead;
while (p && j < i)//尋找第i個結點,j==i時,p爲第i-1個結點
{
p = p->next;
j++;
}
if (p->next == NULL||p==NULL)//該結點不存在
return;
q = p->next; //q爲要刪除的結點
cout << q->data << endl;
p->next = q->next;
free(q);
return;
}
鏈表的循環刪除
這個很簡單,找到頭指針,定義一個p,q 。q=p->next,free§,然後p,q,互換即可,注意最後的頭指針一定要定義爲NULL!
在程序運行期間,用動態內存分配函數來申請的內存都是從堆上分配的,動態內存的生存期自己來決定,使用非常靈活,但也易出現內存泄漏的問題,爲了防止內存泄漏的發生,必須及時調用free()釋放已不再使用的內存
void clearlist()
{
node* p;
node* q;
p = listhead;
while (p != NULL)
{
q = p->next;
free(p);
p = q;
}
listhead = NULL;
return;
}
二、循環鏈表
對於單鏈表,由於每個結點只存儲了向後的指針,到了尾標誌就停止了向後鏈的操作,這樣,當中某一結點就無法找到它的前驅結點了,就像我們剛纔說的,不能回到頭部。
將單鏈表中終端結點的指針端由空指針改爲指向頭結點,就使整個單鏈表形成一個環,這種頭尾相接的單鏈表稱爲單循環鏈表,簡稱循環鏈表。其從剛纔的例子,可以總結出,循環鏈表解決了一一個很麻煩的問題。如何從當中一個結點出發,訪問到鏈表的全部結點。
其實循環鏈表和單鏈表的主要差異就在於循環的判斷條件上,原來是判斷p->next是否爲空,現在則是p -> next不等於頭結點,則循環未結束。
但我們要是訪問最後一個結點,仍然需要O(N)的複雜度,那麼可以定義一個尾指針,rear,則rear->next,則爲開始結點(具體看如何創建鏈表)。
三、雙向鏈表
爲了克服單向性這一缺點,我們的老科學家們,設計出了雙向鏈表。雙向鏈表(double linked list) 是在單鏈表的每個結點中,再設置一個指向其前驅結點的指針域。所以在雙向鏈表中的結點都有兩個指針域,一個指向直接後繼,另一個指向直接前驅。
雙向鏈表是單鏈表中擴展出來的結構,所以它的很多操作是和單鏈表相同的。比如求長度,遍歷,但刪除單個結點,插入之類的操作就需要麻煩一些了,順序不能錯。
插入
s->prior=p; //前繼指針指向P
s->next=p->next; //先別改p,s的後繼指向原來p的後繼
p->next->prior=s;
//p此時未改,p的後繼的前繼等於ai+1的地址,那麼改爲s的地址
p->next=s;
刪除
p->prior->next=p->next;
p->next->prior=p->prior;