鏈表不僅作爲鏈式存儲的一種實現方式,還表達了計算機不連續(離散)的存儲思想。在初學階段,鏈表的實現類型有單鏈表(帶/不帶頭結點)、循環單鏈表、雙鏈表、循環雙鏈表四種。
上文已經講解了單鏈表,接下來將講解其他三類。
Table of Contents
循環單鏈表
定義:
將單鏈表中終端結點的指針端由 NULL 改爲 指向頭結點 ,就使整個單鏈表形成一個環,這種頭尾相接的單鏈表稱爲單循環鏈表,簡稱循環鏈表。
兩種情形:
- 爲使空鏈表與非空鏈表處理一致,通常設置一個頭結點。但並非必須設置頭結點。
循環單鏈表特徵:
- 對於單鏈表而言,最後一個結點指向NULL;把最後一個結點的不指向NULL而指向頭,就是循環單鏈表;
- 在單循環鏈表上的操作基本上與非循環鏈表相同。循環鏈表和單鏈表的主要差異就在於循環的條件判斷上,原來是 p->next == NULL,現在是 p->next != 頭結點 ,則循環未結束。
- 單循環鏈表可以從表中任意結點開始遍歷整個鏈表,不僅如此,有時對鏈表常做的操作是在表尾、表頭進行。如果用頭指針表示循環鏈表,則需O(n) 時間找到最後一個結點。若改用尾指針表示循環鏈表,此時查找開始結點和終端結點都很方便了。查找終端結點時間是O(1),而開始結點,其實就是 rear->next->next ,其時間複雜也爲O(1)。
第一個循環單鏈表
#include <stdio.h>
#include <malloc.h>
#include <assert.h>
typedef struct node
{
int data;
struct node* next;
}Node; //struct node 完全等於 Node(結構體變量)
typedef Node* LinkList; //struct node * 完全等於 LinkList(結構體指針)
int main()
{
LinkList head = (LinkList)malloc(sizeof(Node));
assert(head != NULL); //檢查malloc之後是不是空間不夠,返回了空指針NULL(WarningC6011:取消對NULL指針的引用)
LinkList NodeAa = (LinkList)malloc(sizeof(Node));
assert(NodeAa != NULL);
LinkList NodeBb = (LinkList)malloc(sizeof(Node));
assert(NodeBb != NULL);
LinkList NodeCc = (LinkList)malloc(sizeof(Node));
assert(NodeCc != NULL);
head->data = NULL; //頭結點,不保存數據
head->next = NodeAa;
NodeAa->data = 202;
NodeAa->next = NodeBb;
NodeBb->data = 303;
NodeBb->next = NodeCc;
NodeCc->data = 404;
NodeCc->next = head; //單鏈表中:NodeCc->next = NULL;
LinkList p = head->next; //把鏈表頭結點的下一個節點,交給指針p,去遍歷
while (p != head)
{
printf("%d ", p->data);
p = p->next;
}
return 0;
}
雙鏈表
定義:
雙向鏈表也叫雙鏈表,它的每個數據結點中都有兩個指針(保存兩個節點的地址),分別指向直接後繼和直接前驅。
雙鏈表的代碼定義:
typedef struct node
{
int data;
struct node* pre; //前驅
struct node* next; //後繼
}Node;
特性:
- 從雙向鏈表中的任意一個結點開始,都可以很方便地訪問它的前驅結點和後繼結點。
- 循環鏈表的最後一個結點指向頭結點,循環鏈表的操作和單鏈表的操作基本一致,差別僅僅在於算法中的循環條件有所不同。
- 雙向鏈表使單鏈表中擴展出來的結構,因此有一部分操作與單鏈表是相同的,如求長度函數、查找元素位置、打印函數、銷燬函數等,這些函數操作都只要涉及一個方向的指針即可。
第一個雙鏈表
第一個雙鏈表
#include <stdio.h>
#include <malloc.h>
#include <assert.h>
typedef struct node
{
int data;
struct node* pre;
struct node* next;
}Node; //struct node 完全等於 Node(結構體變量)
typedef Node* LinkList; //struct node * 完全等於 LinkList(結構體指針)
int main()
{
LinkList head = (LinkList)malloc(sizeof(Node));
assert(head != NULL); //檢查malloc之後是不是空間不夠,返回了空指針NULL(WarningC6011:取消對NULL指針的引用)
LinkList NodeAa = (LinkList)malloc(sizeof(Node));
assert(NodeAa != NULL);
LinkList NodeBb = (LinkList)malloc(sizeof(Node));
assert(NodeBb != NULL);
LinkList NodeCc = (LinkList)malloc(sizeof(Node));
assert(NodeCc != NULL);
head->data = 101;
head->pre = NULL;
head->next = NodeAa;
NodeAa->data = 202;
NodeAa->pre = head;
NodeAa->next = NodeBb;
NodeBb->data = 303;
NodeBb->pre = NodeAa;
NodeBb->next = NodeCc;
NodeCc->data = 404;
NodeCc->pre = NodeBb;
NodeCc->next = NULL; //單鏈表中:NodeCc->next = NULL;
LinkList p = head; //把鏈表頭結點的下一個交給指針p,去遍歷
printf("順序遍歷:");
while (p != NULL)
{
printf("%d ", p->data);
p = p->next;
}
printf("\n逆序遍歷:");
LinkList tail = NodeCc;
p = tail;
while (p != NULL)
{
printf("%d ", p->data);
p = p->pre;
}
return 0;
}
循環雙鏈表
定義:
雙向鏈表也叫雙鏈表,它的每個數據結點中都有兩個指針(保存兩個節點的地址),分別指向直接後繼和直接前驅。頭指針的前驅指向最後一個節點,最後一個節點的後繼指向頭指針。
雙鏈表的代碼定義:
typedef struct node
{
int data;
struct node* pre; //前驅
struct node* next; //後繼
}Node;
特性:
- 從雙向鏈表中的任意一個結點開始,都可以很方便地訪問它的前驅結點和後繼結點。
- 循環鏈表的最後一個結點指向頭結點,循環鏈表的操作和單鏈表的操作基本一致,差別僅僅在於算法中的循環條件有所不同。
- 雙向鏈表使單鏈表中擴展出來的結構,因此有一部分操作與單鏈表是相同的,如求長度函數、查找元素位置、打印函數、銷燬函數等,這些函數操作都只要涉及一個方向的指針即可。
兩種情形:
第一個循環雙鏈表
#include <stdio.h>
#include <malloc.h>
#include <assert.h>
typedef struct node
{
int data;
struct node* pre;
struct node* next;
}Node; //struct node 完全等於 Node(結構體變量)
typedef Node* LinkList; //struct node * 完全等於 LinkList(結構體指針)
int main()
{
LinkList head = (LinkList)malloc(sizeof(Node));
assert(head != NULL); //檢查malloc之後是不是空間不夠,返回了空指針NULL(WarningC6011:取消對NULL指針的引用)
LinkList NodeAa = (LinkList)malloc(sizeof(Node));
assert(NodeAa != NULL);
LinkList NodeBb = (LinkList)malloc(sizeof(Node));
assert(NodeBb != NULL);
LinkList NodeCc = (LinkList)malloc(sizeof(Node));
assert(NodeCc != NULL);
head->data = NULL; //頭結點,不保存數據
head->pre = NodeCc;
head->next = NodeAa;
NodeAa->data = 202;
NodeAa->pre = head;
NodeAa->next = NodeBb;
NodeBb->data = 303;
NodeBb->pre = NodeAa;
NodeBb->next = NodeCc;
NodeCc->data = 404;
NodeCc->pre = NodeBb;
NodeCc->next = head; //單鏈表中:NodeCc->next = NULL;
LinkList p = head->next; //把鏈表頭結點的下一個交給指針p,去遍歷
printf("順序遍歷:");
while (p != head)
{
printf("%d ", p->data);
p = p->next;
}
printf("\n逆序遍歷:");
p = p->pre;
while (p != head)
{
printf("%d ", p->data);
p = p->pre;
}
return 0;
}