學過鏈表的應該都知道向鏈表中插入元素存在兩種插入方式:
頭插法:數據插入鏈表后,作為鏈表的第一個元素;
尾插法:數據插入鏈表后,作為鏈表的最后一個元素;
本篇博客的重點在於為什么要有頭結點
關於頭結點和頭指針的概念,請參考關於鏈表中頭指針和頭結點的理解
為什么要有頭結點,在網上找了半天,解釋是"為了統一插入和刪除對第一個結點和對其他結點的操作"
but how,這一點沒有完整且直觀的代碼解釋,在這一點上,我之前的理解並不是很清晰,這里通過代碼來驗證一下:
talk is cheap,show me the code
#include<stdio.h>
#include<stdlib.h>
struct Node
{
int data;
struct Node *next;
}Node;
typedef struct LinkedList
{
struct Node *head; // 頭指針
}LinkedList;
// 無頭結點初始化
void Init_List(LinkedList *list)
{
list->head = NULL;
}
// 有頭結點初始化
void Init_List_With_Head_Node(LinkedList *list)
{
struct Node *node = (struct Node*)malloc(sizeof(struct Node));
node->next = NULL;
list->head = node;
}
// 有頭結點頭插法插入數據
void Head_Insert_List(LinkedList *list,int data)
{
struct Node *node = (struct Node*)malloc(sizeof(struct Node));
node->data = data;
node->next = list->head;
list->head = node;
}
// 有頭結點尾插法插入數據
void Tail_Insert_List(LinkedList *list,int data)
{
struct Node *node = (struct Node*)malloc(sizeof(struct Node));
struct Node *tmp = list->head;
node->data = data;
node->next = NULL;
if(tmp)
{
// 走到尾部
while(tmp->next)
{
tmp = tmp->next;
}
tmp->next = node;
}
else
{
// head為 NULL
list->head = node;
}
}
// 有頭結點頭插法插入數據
void Head_Insert_List_With_Head_Node(LinkedList *list,int data)
{
struct Node *node = (struct Node*)malloc(sizeof(struct Node));
node->data = data;
node->next = list->head->next;
list->head->next = node;
}
// 有頭結點尾插法插入數據
void Tail_Insert_List_With_Head_Node(LinkedList *list,int data)
{
struct Node *node = (struct Node*)malloc(sizeof(struct Node));
struct Node *tmp = list->head;
node->data = data;
node->next = NULL;
// 走到尾部
while(tmp->next)
{
tmp = tmp->next;
}
tmp->next = node;
}
void print_list(LinkedList *list)
{
int i;
struct Node *tmp = list->head;
while(tmp)
{
printf("%d\t",tmp->data);
tmp = tmp->next;
}
puts("");
}
int main()
{
LinkedList list;
puts("無頭結點:");
Init_List(&list);
puts("頭插法");
Head_Insert_List(&list,1);
Head_Insert_List(&list,2);
Head_Insert_List(&list,3);
print_list(&list);
puts("尾插法");
Init_List(&list);
Tail_Insert_List(&list,1);
Tail_Insert_List(&list,2);
Tail_Insert_List(&list,3);
print_list(&list);
puts("有頭結點:");
Init_List_With_Head_Node(&list);
puts("頭插法");
Head_Insert_List_With_Head_Node(&list,1);
Head_Insert_List_With_Head_Node(&list,2);
Head_Insert_List_With_Head_Node(&list,3);
print_list(&list);
puts("尾插法");
Init_List_With_Head_Node(&list);
Tail_Insert_List_With_Head_Node(&list,1);
Tail_Insert_List_With_Head_Node(&list,2);
Tail_Insert_List_With_Head_Node(&list,3);
print_list(&list);
return 0;
}
上面的執行結果為:
上面紅圈圈處的數字是因為沒有對頭結點的數據進行初始化,內存中的垃圾數字.
從上面可以看出:
不帶頭結點的鏈表,因為頭指針指向第一個元素結點:
- 尾插法添加元素時,需要判斷當前鏈表是否是沒有元素,如果沒有元素,則需要更新頭指針指向當前添加的這個元素;
- 刪除元素時,需要判斷刪除的元素是否為第一個元素,如果是第一個元素,需要更新頭指針指向下一個元素;
帶頭結點的鏈表
因為頭指針指向這個頭結點,不會出現頭指針為NULL的情況,不會有上述的特殊考慮,對第一個元素的操作和對其他元素的操作統一;