目錄
1. 什么是單鏈表?
2. 單鏈表的代碼表示
3. 單鏈表的基本操作
3.1 初始化鏈表
3.2 插入結點(頭插法)
3.3 插入結點(尾插法)
3.4 遍歷鏈表
4. 單鏈表的優缺點
代碼:*L=(LinkList)malloc(sizeof(LNode))
1.?malloc?的作用
2.?sizeof(LNode)?的作用
3.?類型轉換?(LinkList)
4.?*L?的含義
5.?整體流程
6.?實際效果
7.?常見問題解答
Q1:為什么用?malloc?而不是直接聲明變量?
Q2:頭結點的?data?字段有意義嗎?
Q3:為什么要用二級指針?LinkList* L?
8.?圖解過程
DestoryLinkList?函數原理詳解
1. 函數參數?LinkList* L(二級指針)
2.?while (*L != NULL)?循環
3. 銷毀過程圖解
循環步驟:
4. 最終效果
5. 為什么需要這樣實現?
6. 對比?ClearLinkList(清空鏈表)
7. 常見問題
Q1:為什么用?while (*L)?而不是?while ((*L)->next)?
Q2:如果鏈表為空(只有頭結點),會發生什么?
Q3:為什么不用遞歸實現?
8. 代碼驗證
總結
CreateLinkList_H?函數原理詳解(頭插法創建單鏈表)
1. 函數參數
2. 創建頭結點
3. 頭插法循環(for (i = n; i > 0; i--))
步驟拆解:
插入過程圖示:
4. 輸入順序與鏈表順序的關系
5. 與尾插法(CreateLinkList_R)的區別
6. 關鍵代碼解析
7. 內存管理注意事項
8. 示例輸入輸出
輸入:
鏈表結構:
9. 常見問題
Q1:為什么頭插法會導致順序相反?
Q2:如果?n?為負數會發生什么?
Q3:頭插法的時間復雜度是多少?
總結
ShowLinkList?函數原理詳解(顯示單鏈表內容)
1. 函數參數?const LinkList* L
2. 初始化指針?p
3. 檢查鏈表是否為空
4. 遍歷鏈表并打印數據
5. 示例輸出
6. 遍歷過程圖解
7. 為什么用?while (p)?而不是?while (p->next)?
8. 時間復雜度
9. 安全性注意事項
總結
CreateLinkList_R?函數原理詳解(尾插法創建單鏈表)
1. 函數參數
2. 創建頭結點
3. 尾指針?p?的初始化
4. 尾插法循環(for (i = n; i > 0; i--))
步驟拆解:
插入過程圖示:
5. 輸入順序與鏈表順序的關系
6. 與頭插法(CreateLinkList_H)的區別
7. 關鍵代碼解析
8. 內存管理注意事項
9. 示例輸入輸出
輸入:
鏈表結構:
10. 常見問題
Q1:為什么需要尾指針?p?
Q2:如果?p?不更新會怎樣?
Q3:尾插法的時間復雜度是多少?
總結
#include <stdio.h>
#include <stdlib.h>//函數結果狀態代碼
#define OK 1
#define ERROR 0typedef int Status;//函數返回狀態,ok,error
typedef int Elemtype;//鏈表元素為整形
typedef struct Lnode//定義結構體
{Elemtype data;//數據域struct Lnode* next;//指針域
}Lnode,*LinkList;//單個結點,整個鏈表(指向結點的指針)//初始化鏈表(建立一個頭結點)
Status InitLinkList(LinkList* L){*L=(LinkList)malloc(sizeof(Lnode));//分配頭結點內存if(*L==NULL){return ERROR;//判斷是否分配成功}(*L)->next=NULL;//頭結點的指針域為空return OK;
}//判斷鏈表是否為空
Status IsEmptyLinkList(const LinkList* L){if((*L)->next==NULL){return ERROR;}else{return OK;}
}//銷毀鏈表
Status DestoryLinkList(LinkList* L){LinkList p;//定義一個臨時的指向結點的指針while (*L!=NULL){p=*L;//儲存原來的指針(結點)*L=(*L)->next;//往后移動結點free(p);//釋放原來的指針}return OK;
}//鏈表的插入,頭插法
Status CreateLinkList_h(LinkList* L,int n){InitLinkList(L);//創建頭結點for(int i=0;i<n;i++){LinkList newlnode;//創建一個新結點newlnode=(Lnode*)malloc(sizeof(Lnode));//為新節點分配內存if(newlnode==NULL){return ERROR;//判斷是否分配成功}printf("請輸入數據:\n");scanf("%d",&newlnode->data);newlnode->next=(*L)->next;//使新結點指向原指針(*L)->next=newlnode;//使頭指針指向新結點}return OK;
}//鏈表的插入,尾插入
Status CreateLinkList_r(LinkList* L,int n){InitLinkList(L);//創建頭結點LinkList p=*L;//定義臨時尾結點for(int i=0;i<n;i++){LinkList newlnode;newlnode=(Lnode*)malloc(sizeof(Lnode));//給新結點分配內存if(newlnode==NULL){return ERROR;//判斷是否分配成功}printf("請輸入數據:\n");scanf("%d",&newlnode->data);newlnode->next=NULL;//使新結點指向空p->next=newlnode;//使原結點指向新結點p=p->next;//后移一次,定義新的尾結點}return OK;
}//查看鏈表
Status ShowLinkList(const LinkList* L){Lnode* p=(*L)->next;//定義個臨時結點if(p==NULL){printf("鏈表為空!\n");return OK;}int i=1;while (p!=NULL){printf("%d : %d\n", i, p->data); // 打印序號和數據i++; // 序號遞增p = p->next; // p 移動到下一個結點}return OK;
}//查看第i個元素
Status LocatElem(const LinkList* L,int i){int j=i;//賦值給ji=1;//初始化iLinkList p=(*L)->next;//創建臨時結點表示第一個結點if(p==NULL){printf("鏈表為空!\n");//判斷鏈表是否為空return OK;}//逐步后移,直到i和j相等while (i!=j){i++;p=p->next;}printf("第%d個 : %d\n", j, p->data); // 打印第i個序號,和數據return OK;
}//主函數
int main(){LinkList mylist;mylist=NULL;//CreateLinkList_h(&mylist,3);//頭插CreateLinkList_r(&mylist,3);//尾插ShowLinkList(&mylist);LocatElem(&mylist,2);
}
下面來解釋相關知識點和部分代碼:
單鏈表是數據結構中最基礎的一種鏈式存儲結構,非常適合新手學習指針和動態內存管理的概念。下面我會用最易懂的方式講解單鏈表的核心知識。
1. 什么是單鏈表?
單鏈表就像一列火車:
每節車廂(結點)包含兩部分:貨物(數據)和連接鉤(指針)
車頭(頭結點)不裝貨物,只負責帶領整列火車
最后一節車廂的連接鉤是空的(NULL)
2. 單鏈表的代碼表示
typedef struct Node {int data; // 數據域(可以是任意類型)struct Node* next; // 指針域(指向下一個結點) } Node, *LinkedList; // Node表示單個結點,LinkedList表示整個鏈表3. 單鏈表的基本操作
3.1 初始化鏈表
LinkedList initList() {LinkedList L = (LinkedList)malloc(sizeof(Node)); // 創建頭結點L->next = NULL; // 初始為空鏈表return L; }3.2 插入結點(頭插法)
void headInsert(LinkedList L, int data) {Node* newNode = (Node*)malloc(sizeof(Node)); // 創建新結點newNode->data = data;newNode->next = L->next; // 新結點指向原第一個結點L->next = newNode; // 頭結點指向新結點 }特點:新結點總是插在最前面,鏈表順序與插入順序相反
3.3 插入結點(尾插法)
void tailInsert(LinkedList L, int data) {Node* p = L;while(p->next != NULL) { // 找到最后一個結點p = p->next;}Node* newNode = (Node*)malloc(sizeof(Node));newNode->data = data;newNode->next = NULL;p->next = newNode; // 最后一個結點指向新結點 }特點:新結點總是插在最后面,鏈表順序與插入順序相同
3.4 遍歷鏈表
void printList(LinkedList L) {Node* p = L->next; // 跳過頭結點while(p != NULL) {printf("%d ", p->data);p = p->next; // 移動到下一個結點}printf("\n"); }4. 單鏈表的優缺點
優點:
插入/刪除速度快(O(1)時間復雜度)
不需要預先知道數據規模
不需要連續的內存空間
缺點:
查找速度慢(O(n)時間復雜度)
需要額外的空間存儲指針
下面來解釋代碼,更加了解單鏈表:
代碼:*L=(LinkList)malloc(sizeof(LNode))
這行代碼是單鏈表初始化中的核心操作,涉及指針、動態內存分配和類型轉換。讓我們用新手能理解的方式逐步拆解:
1.?
malloc
?的作用
malloc
?是內存分配函數(memory allocation),它的作用是從堆(Heap)內存中申請一塊指定大小的內存空間。
比喻:就像在倉庫里預訂一個儲物柜,告訴管理員你需要多大的空間。
語法:
void* malloc(size_t size);
參數?
size
:需要申請的字節數返回值:成功時返回指向分配內存的指針,失敗返回?
NULL
2.?
sizeof(LNode)
?的作用
sizeof
?是一個運算符,用來計算數據類型或變量占用的字節數。
sizeof(LNode)
:計算結構體?LNode
?的大小。
假設?
LNode
?包含一個?int
(4字節)和一個指針(8字節,64位系統),則?sizeof(LNode) = 12
?字節。意義:告訴?
malloc
?要申請一個足夠存放?LNode
?的內存塊。
3.?類型轉換?
(LinkList)
malloc
?返回的是?void*
(通用指針),需要強制轉換為?LinkList
?類型。
LinkList
?是什么?
根據代碼中的定義:typedef struct Signle_Link_List LNode, *LinkList;
LNode
?是結構體類型(結點)
LinkList
?是?LNode*
?的別名(指向結點的指針)轉換目的:明確告訴編譯器,這塊內存將被當作?
LinkList
(即指向?LNode
?的指針)使用。
4.?
*L
?的含義函數的參數是?
LinkList* L
(二級指針):
LinkList* L
?可以理解為:指向頭指針的指針。
*L
?解引用后得到的是頭指針(LinkList
?類型)。操作目的:通過?
*L = ...
?修改外部的頭指針,使其指向新分配的內存。
5.?整體流程
這行代碼的完整意義是:
申請內存:在堆內存中申請一塊大小為?
sizeof(LNode)
?的內存。類型轉換:將返回的?
void*
?轉換為?LinkList
?類型(即?LNode*
)。賦值給頭指針:讓外部的頭指針?
*L
?指向這塊內存。
6.?實際效果
這行代碼執行后:
創建了一個頭結點:頭結點的?
data
?字段未初始化(可能是垃圾值),但?next
?指針會被初始化為?NULL
(見后續代碼?(*L)->next = NULL;
)。鏈表結構:
*L(頭指針)│▼ [頭結點] → next = NULL
7.?常見問題解答
Q1:為什么用?
malloc
?而不是直接聲明變量?
答:鏈表結點需要動態增減,
malloc
?允許在運行時按需申請內存。對比:
LNode node; // 棧內存,函數結束后自動釋放 LNode* p = malloc(...); // 堆內存,需要手動釋放(用 free)Q2:頭結點的?
data
?字段有意義嗎?
答:在標準實現中,頭結點的?
data
?通常不存儲有效數據(僅作為鏈表入口),但代碼中可能用它存儲元信息(如長度)。Q3:為什么要用二級指針?
LinkList* L
?
答:需要修改外部傳入的頭指針的值。C語言中,若想通過函數修改指針的值,必須傳遞指針的地址(即二級指針)。
示例:
void Init(LinkList* L) {*L = malloc(...); // 修改外部的頭指針 } int main() {LinkList myList; // 此時 myList 是野指針Init(&myList); // 傳遞頭指針的地址 }
8.?圖解過程
Before malloc: +------+ | L | --> 隨機值(野指針) +------+After malloc: +------+ +---------------------+ | L | --> | data(未初始化) | +------+ | next = NULL |+---------------------+
DestoryLinkList
?函數原理詳解這個函數的作用是?銷毀整個單鏈表,包括?頭結點?和所有?數據結點,并釋放它們占用的內存。讓我們一步步解析它的工作原理:
1. 函數參數?
LinkList* L
(二級指針)
LinkList
?是?LNode*
?的別名(指向結點的指針)。
LinkList* L
?是一個?指向頭指針的指針(二級指針),目的是?修改外部的頭指針,使其最終變為?NULL
。
如果只傳?
LinkList L
(一級指針),函數內部修改?L
?不會影響外部的頭指針,導致內存泄漏。
2.?
while (*L != NULL)
?循環
循環條件:只要?
*L
(當前頭指針)不為?NULL
,就繼續釋放內存。循環過程:
p = *L
:臨時指針?p
?保存當前要釋放的結點(頭結點或數據結點)。
*L = (*L)->next
:讓頭指針?*L
?指向下一個結點(相當于鏈表“跳過”當前結點)。
free(p)
:釋放?p
?指向的結點內存。
3. 銷毀過程圖解
假設鏈表結構如下:
頭指針 *L│▼ [頭結點] → [結點1] → [結點2] → NULL循環步驟:
第一次循環:
p = *L
(p
?指向頭結點)
*L = (*L)->next
(頭指針?*L
?指向結點1)
free(p)
(釋放頭結點)*L → [結點1] → [結點2] → NULL第二次循環:
p = *L
(p
?指向結點1)
*L = (*L)->next
(頭指針?*L
?指向結點2)
free(p)
(釋放結點1)*L → [結點2] → NULL第三次循環:
p = *L
(p
?指向結點2)
*L = (*L)->next
(頭指針?*L
?指向?NULL
)
free(p)
(釋放結點2)*L → NULL循環結束:
*L
?為?NULL
,退出循環。
4. 最終效果
鏈表被完全銷毀:所有結點(包括頭結點)的內存被釋放。
頭指針?
*L
?被置為?NULL
:防止外部代碼誤用已釋放的內存(避免野指針)。
5. 為什么需要這樣實現?
防止內存泄漏:必須逐個釋放所有結點,否則未釋放的內存會一直占用堆空間。
安全性:將頭指針置為?
NULL
,避免后續代碼誤操作已釋放的內存。
6. 對比?
ClearLinkList
(清空鏈表)
ClearLinkList
?只釋放數據結點,保留頭結點(鏈表可復用):Status ClearLinkList(LinkList* L) {LNode* p, *q;q = (*L)->next; // q 指向第一個數據結點while (q != NULL) {p = q;q = q->next;free(p);}(*L)->next = NULL; // 頭結點的 next 置空return OK; }
區別:
DestoryLinkList
:銷毀整個鏈表(頭結點+數據結點)。
ClearLinkList
:只清空數據結點,保留頭結點。
7. 常見問題
Q1:為什么用?
while (*L)
?而不是?while ((*L)->next)
?
答:
*L
?是當前頭指針,需要釋放所有結點(包括頭結點)。如果檢查?(*L)->next
,會漏掉頭結點。Q2:如果鏈表為空(只有頭結點),會發生什么?
答:
*L
?指向頭結點,第一次循環釋放頭結點后,*L
?被置為?NULL
,循環結束。Q3:為什么不用遞歸實現?
答:遞歸實現可能因鏈表過長導致棧溢出。迭代(循環)更安全高效。
8. 代碼驗證
可以通過打印結點地址驗證釋放過程:
Status DestoryLinkList(LinkList* L) {LNode* p;while (*L != NULL) {p = *L;printf("Freeing node at address: %p\n", p); // 打印釋放的結點地址*L = (*L)->next;free(p);}return OK; }
總結
核心操作:循環遍歷鏈表,逐個釋放結點,并更新頭指針。
關鍵點:二級指針修改頭指針、
free
?釋放內存、防止野指針。適用場景:當確定鏈表不再使用時調用,避免內存泄漏。
CreateLinkList_H
?函數原理詳解(頭插法創建單鏈表)這個函數的作用是?用頭插法創建一個包含?
n
?個結點的單鏈表。特點是?新結點總是插入在頭結點之后,因此鏈表的順序與輸入順序?相反。下面逐步解析其工作原理:
1. 函數參數
LinkList* L
:二級指針,用于修改外部的頭指針。
int n
:要創建的結點數量。
2. 創建頭結點
*L = (LinkList)malloc(sizeof(LNode)); // 分配頭結點內存 (*L)->next = NULL; // 頭結點的 next 初始化為 NULL
作用:初始化一個空鏈表,只有頭結點(不存儲實際數據)。
圖示:
*L(頭指針)│▼ [頭結點] → NULL
3. 頭插法循環(
for (i = n; i > 0; i--)
)循環?
n
?次,每次創建一個新結點并插入到?頭結點之后。步驟拆解:
申請新結點內存:
LNode* newlnode = (LNode*)malloc(sizeof(LNode));
為新結點分配內存,并通過?
scanf
?輸入數據。插入新結點:
newlnode->next = (*L)->next; // 新結點的 next 指向原第一個結點 (*L)->next = newlnode; // 頭結點的 next 指向新結點
關鍵點:新結點插入后成為鏈表的?第一個數據結點。
插入過程圖示:
初始狀態(只有頭結點):
[頭結點] → NULL插入第一個結點(值為 1):
[頭結點] → [1] → NULL插入第二個結點(值為 2):
[頭結點] → [2] → [1] → NULL插入第三個結點(值為 3):
[頭結點] → [3] → [2] → [1] → NULL
4. 輸入順序與鏈表順序的關系
輸入順序:假設依次輸入?
1, 2, 3
。鏈表順序:
3 → 2 → 1
(與輸入相反)。原因:每次新結點都插入在鏈表頭部。
5. 與尾插法(
CreateLinkList_R
)的區別
頭插法 尾插法 新結點插入頭結點之后 新結點追加到鏈表末尾 鏈表順序與輸入順序相反 鏈表順序與輸入順序相同 無需維護尾指針 需要維護尾指針? tail
時間復雜度:O(n)(每次插入為 O(1)) 時間復雜度:O(n)
6. 關鍵代碼解析
newlnode->next = (*L)->next; // 新結點的 next 指向原第一個結點 (*L)->next = newlnode; // 頭結點的 next 指向新結點
類比:像排隊時每次都讓新來的人站到隊伍最前面。
操作順序:必須先設置?
newlnode->next
,再修改?(*L)->next
,否則會丟失原鏈表的引用。
7. 內存管理注意事項
每個?
malloc
?分配的內存必須在鏈表銷毀時通過?free
?釋放(如調用?DestoryLinkList
)。如果輸入?
n
?為 0,函數會創建一個只有頭結點的空鏈表。
8. 示例輸入輸出
輸入:
CreateLinkList_H(&myList, 3); // 依次輸入:10, 20, 30鏈表結構:
頭結點 → [30] → [20] → [10] → NULL
9. 常見問題
Q1:為什么頭插法會導致順序相反?
答:每次新結點都插入在鏈表頭部,類似“后來居上”。
Q2:如果?
n
?為負數會發生什么?
答:循環不會執行,鏈表只有頭結點(需在函數開頭添加參數檢查)。
Q3:頭插法的時間復雜度是多少?
答:O(n),因為每個結點的插入操作是 O(1),共?
n
?次。
總結
核心思想:通過每次在頭部插入新結點構建鏈表。
特點:簡單高效,但順序與輸入相反。
適用場景:不需要保持輸入順序,或需要頻繁在頭部插入的場景(如棧的實現)。
ShowLinkList
?函數原理詳解(顯示單鏈表內容)這個函數的作用是?遍歷并打印單鏈表中的所有數據結點,同時顯示每個結點的序號。如果鏈表為空,會提示用戶。以下是逐步解析:
1. 函數參數?
const LinkList* L
const
?修飾符:表示不會修改鏈表內容(安全保護)。
LinkList* L
:二級指針,用于訪問頭結點(但這里只讀不修改)。
2. 初始化指針?
p
LNode* p = (*L)->next; // p 指向第一個數據結點(跳過頭結點)
為什么從?
(*L)->next
?開始?
頭結點(*L
)不存儲實際數據,它的?next
?才指向第一個有效結點。
3. 檢查鏈表是否為空
if (!p) { // 等價于 if (p == NULL)puts("The LinkList is empty");return; }
邏輯:如果?
p
?為?NULL
,說明頭結點的?next
?為空,鏈表無數據結點。
4. 遍歷鏈表并打印數據
int i = 1; // 結點序號從1開始 while (p != NULL) { // 遍歷直到鏈表末尾printf("%d : %d\n", i, p->data); // 打印序號和數據i++; // 序號遞增p = p->next; // p 移動到下一個結點 }
關鍵點:
p->data
:當前結點的數據。
p = p->next
:指針后移,實現遍歷。
5. 示例輸出
假設鏈表結構:
頭結點 → [10] → [20] → [30] → NULL調用?
ShowLinkList(&list)
?輸出:1 : 10 2 : 20 3 : 30如果鏈表為空,輸出:
The LinkList is empty
6. 遍歷過程圖解
初始狀態: p = 頭結點->next → [10] → [20] → [30] → NULL第一次循環: 打印 1:10,p 移動到 [20]第二次循環: 打印 2:20,p 移動到 [30]第三次循環: 打印 3:30,p 移動到 NULL(循環結束)
7. 為什么用?
while (p)
?而不是?while (p->next)
?
while (p)
:確保當前結點?p
?有效時才打印數據(包括最后一個結點)。如果寫成?
while (p->next)
,會漏掉最后一個結點的數據!
8. 時間復雜度
O(n):需要遍歷所有?
n
?個數據結點,每個結點訪問一次。
9. 安全性注意事項
const
?保護:防止函數內意外修改鏈表。空指針檢查:避免訪問?
NULL->next
(已通過?if (!p)
?處理)。
總結
功能:按順序顯示鏈表所有結點的數據和序號。
關鍵操作:指針遍歷 (
p = p->next
)、空鏈表檢查。適用場景:調試、查看鏈表內容、交互式程序輸出。
CreateLinkList_R
?函數原理詳解(尾插法創建單鏈表)這個函數的作用是?用尾插法創建一個包含?
n
?個結點的單鏈表。特點是?新結點總是插入在鏈表末尾,因此鏈表的順序與輸入順序?一致。下面逐步解析其工作原理:
1. 函數參數
LinkList* L
:二級指針,用于修改外部的頭指針。
int n
:要創建的結點數量。
2. 創建頭結點
*L = (LinkList)malloc(sizeof(LNode)); // 分配頭結點內存 (*L)->next = NULL; // 頭結點的 next 初始化為 NULL
作用:初始化一個空鏈表,只有頭結點(不存儲實際數據)。
圖示:
*L(頭指針)│▼ [頭結點] → NULL
3. 尾指針?
p
?的初始化LNode* p = *L; // p 初始指向頭結點
p
?的作用:始終指向當前鏈表的?最后一個結點(尾結點)。初始時:鏈表只有頭結點,所以?
p
?指向頭結點。
4. 尾插法循環(
for (i = n; i > 0; i--)
)循環?
n
?次,每次創建一個新結點并插入到?鏈表末尾。步驟拆解:
申請新結點內存:
LNode* newlnode = (LNode*)malloc(sizeof(LNode));
為新結點分配內存,并通過?
scanf
?輸入數據。初始化新結點:
newlnode->next = NULL; // 新結點的 next 置空(因為它將是新的尾結點)插入新結點到末尾:
p->next = newlnode; // 原尾結點的 next 指向新結點 p = newlnode; // p 移動到新結點(更新尾指針)
關鍵點:通過?
p
?直接找到鏈表末尾,實現 O(1) 時間復雜度的插入。插入過程圖示:
初始狀態(只有頭結點):
[頭結點] → NULL p → [頭結點]插入第一個結點(值為 1):
[頭結點] → [1] → NULL p → [1]插入第二個結點(值為 2):
[頭結點] → [1] → [2] → NULL p → [2]插入第三個結點(值為 3):
[頭結點] → [1] → [2] → [3] → NULL p → [3]
5. 輸入順序與鏈表順序的關系
輸入順序:假設依次輸入?
1, 2, 3
。鏈表順序:
1 → 2 → 3
(與輸入一致)。原因:每次新結點都追加到鏈表尾部。
6. 與頭插法(
CreateLinkList_H
)的區別
尾插法 頭插法 新結點插入鏈表末尾 新結點插入頭結點之后 鏈表順序與輸入順序相同 鏈表順序與輸入順序相反 需要維護尾指針? p
無需維護尾指針 時間復雜度:O(n) 時間復雜度:O(n)
7. 關鍵代碼解析
p->next = newlnode; // 將新結點鏈接到末尾 p = newlnode; // 更新尾指針
類比:像排隊時每次都讓新來的人站到隊伍最后面。
必要性:必須更新?
p
,否則下次插入無法找到新的末尾。
8. 內存管理注意事項
每個?
malloc
?分配的內存必須在鏈表銷毀時通過?free
?釋放(如調用?DestoryLinkList
)。如果輸入?
n
?為 0,函數會創建一個只有頭結點的空鏈表。
9. 示例輸入輸出
輸入:
CreateLinkList_R(&myList, 3); // 依次輸入:10, 20, 30鏈表結構:
頭結點 → [10] → [20] → [30] → NULL
10. 常見問題
Q1:為什么需要尾指針?
p
?
答:直接通過頭指針找到鏈表末尾需要 O(n) 時間,而維護?
p
?可以在 O(1) 時間內訪問末尾。Q2:如果?
p
?不更新會怎樣?
答:所有新結點都會插入到原尾結點之后,但原尾結點不會更新,導致鏈表斷裂。
Q3:尾插法的時間復雜度是多少?
答:O(n),因為每個結點的插入操作是 O(1),共?
n
?次。
總結
核心思想:通過維護尾指針,每次在鏈表末尾插入新結點。
特點:保持輸入順序,適合需要順序一致的場景。
關鍵操作:尾指針更新 (
p = newlnode
)、內存分配與釋放。
運行結果如下:
請輸入數據:
90
請輸入數據:
60
請輸入數據:
45
1 : 90
2 : 60
3 : 45
第2個 : 60請按任意鍵繼續. . .
注:該代碼是本人自己所寫,可能不夠好,不夠簡便,歡迎大家指出我的不足之處。如果遇見看不懂的地方,可以在評論區打出來,進行討論,或者聯系我。上述內容全是我自己理解的,如果你有別的想法,或者認為我的理解不對,歡迎指出!!!如果可以,可以點一個免費的贊支持一下嗎?謝謝各位彥祖亦菲!!!!!?