帶頭節點:head-> p1->p2->p3 ->p1->p2->p3-> p1.....
不帶頭節點: p1->p2->p3 ->p1->p2->p3-> p1.....
卻別還不明顯嗎?帶頭節點可以方便,快速的定位鏈表第1個節點
比如循環鏈表的時候,刪除p1 的時候:
head->next = p2;
p3->next = head->next; ok?
free(p1);
思路很清晰,鏈表開始的第1個節點現在就是head->next 即 p2
否則沒有頭節點:
p3->next = p1->next;
free(p1);
會不會有一種鏈表第1個節點到底是哪個的感覺?
當然單向循環這個不明顯,如果你寫個雙向循環,就會很方便,麻煩,我就寫單向吧``
就是方便,清晰,明了,也不是一定要用,要看情況,看需要對鏈表做什么操作,來決定要不要帶頭節點。你不寫幾個鏈表沒法體會的。
在帶頭節點的單鏈表中,頭指針只有一個域,即鏈指針,它指向頭節點,頭節點有兩個域,一個是數據域,值為0 (NULL),還有一個域,鏈指針,這個指針鏈指向單鏈表的第一個數據元素
而在不帶頭節點的單鏈表中,頭節點也只有一個鏈指針,但它指向單鏈表的第一個數據元素
什么時候要使用帶頭節點的單鏈表?
為了在第一個數據元素前面加入新元素或者刪除第一個節點時頭指針的值不變,在第一個數據前面要加一個所謂的頭節點
帶頭結點初始化
Node *head; //聲明頭結點
void InitList(Node *head){
head=(Node *)malloc( sizeof(Node));
head->next=NULL;
}
帶頭結點尾插入,統一操作。
方式一:
void CreatList(Node **head){
Node *r=*head,*s;
int a;
while(scanf("%d",&a)){
if(a!=0){
s=(Node *)malloc(sizeof(Node));
s->value=a;
r->next=s;
r=s;
}
else{
r->next=NULL;
break;
}
}
}
調用CreatList(&head);
方式二:
void CreatList(Node *head){
Node *r=head,*s;
... //下面的都一樣
}
調用CreatList(head);
不帶頭結點初始化
方式一:
void InitList(Node **head){
*head=NULL;
}
調用InitList(&head);
方式二:
void InitList(Node *head){
head=NULL;
}
調用InitList(head);
不帶頭結點尾插入,第一個節點與其他節點分開操作。
void CreatList(Node **head){
Node *p,*t; /*p工作指針,t臨時指針*/
int a,i=1;
while(scanf("%d",&a)){
if(a!=0){
t=(Node *)malloc(sizeof(Node));
t->value=a;
if(i==1){
*head=t;
}
else{
p->next=t;
}
p=t;
}
else{
p->next=NULL;
break;
}
i++;
}
}
調用CreatList(&head);
兩種初始化方法的區別
不帶頭結點的單鏈表對於第一個節點的操作與其他節點不一樣,需要特殊處理,這增加了程序的復雜性和出現bug的機會,因此,通常在單鏈表的開始結點之前附設一個頭結點。
帶頭結點的單鏈表,初始時一定返回的是指向頭結點的地址,所以一定要用二維指針,否則將導致內存訪問失敗或異常。
帶頭結點與不帶頭結點初始化、插入、刪除、輸出操作都不樣,在遍歷輸出鏈表數據時,帶頭結點的判斷條件是while(head->next!=NULL),而不帶頭結點是while(head!=NULL),雖然頭指針可以在初始時設定,但是如1所述,對於特殊情況如只有一個節點會出現問題。
為什么不帶頭結點初始化有2種方式,而帶頭結點只有1種方式呢?
因為不帶頭結點聲明Node *head 時,C編譯器將其自動初始化為NULL,於是根本不需要調用InitList(head); 也即不帶頭結點的初始化是個偽操作。而帶頭結點的初始化在堆開辟了一段內存,需要修改head指針變量指向的地址(即head的值),所以要修改head的值,必須傳保存head變量的地址(即二維指針)。而直接調用CreatList(head);相當於傳head變量的值,函數修改的是head的副本,無法真正改變head的值。
注:這里可以將head指針看成一個變量(不管它保存的是地址),就比較好理解了。
這其實本質上還是傳值,傳址的問題,只不過指針本身保存的地址,讓這個過程變得有點糾結。在函數調用需要修改指針變量的指向(value)時,應該傳遞指針變量的地址(address)。
另外,對於函數的形參是指針時,只要該參數不在左邊(即都是右值操作),二維指針(形參)就可以簡化為一維指針。如上面帶頭結點的尾插簡化版本。