链表的分类
分類:
單鏈表
雙鏈表:每一個(gè)節(jié)點(diǎn)有兩個(gè)指針域
循環(huán)鏈表:能通過(guò)任何一個(gè)節(jié)點(diǎn)找到其他所有的結(jié)點(diǎn)
非循環(huán)鏈表
?
鏈表中第一個(gè)結(jié)點(diǎn)的存儲(chǔ)位置叫做頭指針,那么整個(gè)鏈表的存取就必須是從頭指針開(kāi)始進(jìn)行了。之后的每一個(gè)結(jié)點(diǎn),其實(shí)就是上一個(gè)的后繼指針指向的位置。
這里有個(gè)地方要注意,就是對(duì)頭指針概念的理解,這個(gè)很重要。“鏈表中第一個(gè)結(jié)點(diǎn)的存儲(chǔ)位置叫做頭指針”,如果鏈表有頭結(jié)點(diǎn),那么頭指針就是指向頭結(jié)點(diǎn)數(shù)據(jù)域的指針。畫(huà)一個(gè)圖吧。
頭指針就是鏈表的名字。頭指針僅僅是個(gè)指針而已。
- 頭結(jié)點(diǎn)是為了操作的統(tǒng)一與方便而設(shè)立的,放在第一個(gè)元素結(jié)點(diǎn)之前,其數(shù)據(jù)域一般無(wú)意義(當(dāng)然有些情況下也可存放鏈表的長(zhǎng)度、用做監(jiān)視哨等等)。
- 有了頭結(jié)點(diǎn)后,對(duì)在第一個(gè)元素結(jié)點(diǎn)前插入結(jié)點(diǎn)和刪除第一個(gè)結(jié)點(diǎn),其操作與對(duì)其它結(jié)點(diǎn)的操作統(tǒng)一了。
- 首元結(jié)點(diǎn)也就是第一個(gè)元素的結(jié)點(diǎn),它是頭結(jié)點(diǎn)后邊的第一個(gè)結(jié)點(diǎn)。
- 頭結(jié)點(diǎn)不是鏈表所必需的。
- ?
- 是的,對(duì)于頭指針,我們也可以有相應(yīng)的理解了。
- 在線性表的鏈?zhǔn)酱鎯?chǔ)結(jié)構(gòu)中,頭指針是指鏈表指向第一個(gè)結(jié)點(diǎn)的指針,若鏈表有頭結(jié)點(diǎn),則頭指針就是指向鏈表頭結(jié)點(diǎn)的指針。
- 頭指針具有標(biāo)識(shí)作用,故常用頭指針冠以鏈表的名字。
- 無(wú)論鏈表是否為空,頭指針均不為空。頭指針是鏈表的必要元素。
-
?
單鏈表也可以沒(méi)有頭結(jié)點(diǎn)。如果沒(méi)有頭結(jié)點(diǎn)的話,那么單鏈表就會(huì)變成這樣:
單鏈表源碼:
typdef struct LNode{int data,LinkList *next;}LNode,*LinkList;LinkList head=new LNode(); head->next=null;//插入n個(gè)元素LinkList p=head; for(int i=0;i<n;i++){LinkList q=new LNode();q->data=i; q->next=null;p->next=q;}單鏈表帶頭結(jié)點(diǎn)&不帶頭結(jié)點(diǎn)?
Node *head; //聲明頭結(jié)點(diǎn)帶頭結(jié)點(diǎn)初始化 void InitList(Node **head){*head=(Node *)malloc( sizeof(Node));(*head)->next=NULL; }帶頭結(jié)點(diǎn)尾插入,統(tǒng)一操作 方式一: 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; }} } 調(diào)用CreatList(&head);方式二: void CreatList(Node *head){Node *r=head,*s;... //下面的都一樣 } 調(diào)用CreatList(head);------------------------------------------------------------------------------------------------------不帶頭結(jié)點(diǎn)初始化 方式一: void InitList(Node **head){*head=NULL; } 調(diào)用InitList(&head);方式二: void InitList(Node *head){head=NULL; } 調(diào)用InitList(head);不帶頭結(jié)點(diǎn)尾插入,第一個(gè)節(jié)點(diǎn)與其他節(jié)點(diǎn)分開(kāi)操作 void CreatList(Node **head){Node *p,*t; /*p工作指針,t臨時(shí)指針*/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++;} } 調(diào)用CreatList(&head);一、兩者區(qū)別:
? ? ?1、不帶頭結(jié)點(diǎn)的單鏈表對(duì)于第一個(gè)節(jié)點(diǎn)的操作與其他節(jié)點(diǎn)不一樣,需要特殊處理,這增加了程序的復(fù)雜性和出現(xiàn)bug的機(jī)會(huì),因此,通常在單鏈表的開(kāi)始結(jié)點(diǎn)之前附設(shè)一個(gè)頭結(jié)點(diǎn)。
? ? ?2、帶頭結(jié)點(diǎn)的單鏈表,初始時(shí)一定返回的是指向頭結(jié)點(diǎn)的地址,所以一定要用二維指針,否則將導(dǎo)致內(nèi)存訪問(wèn)失敗或異常。
? ? ?3、帶頭結(jié)點(diǎn)與不帶頭結(jié)點(diǎn)初始化、插入、刪除、輸出操作都不樣,在遍歷輸出鏈表數(shù)據(jù)時(shí),帶頭結(jié)點(diǎn)的判斷條件是while(head->next!=NULL),
?而不帶頭結(jié)點(diǎn)是while(head!=NULL),雖然頭指針可以在初始時(shí)設(shè)定,但是如1所述,對(duì)于特殊情況如只有一個(gè)節(jié)點(diǎn)會(huì)出現(xiàn)問(wèn)題。
? ? ? ? ?
二、為什么不帶頭結(jié)點(diǎn)初始化有2種方式,而帶頭結(jié)點(diǎn)只有1種方式呢?
?
?????因?yàn)椴粠ь^結(jié)點(diǎn)聲明Node *head 時(shí);C編譯器將其自動(dòng)初始化為NULL,于是根本不需要調(diào)用InitList(head);也即不帶頭結(jié)點(diǎn)的初始化是個(gè)偽操作。而帶頭結(jié)點(diǎn)的初始化在堆開(kāi)辟了一段內(nèi)存,需要修改head指針變量指向的地址(即head的值),所以要修改head的值,必須傳保存head變量的地址(即二維指針)。而直接調(diào)用CreatList(head);相當(dāng)于傳head變量的值,函數(shù)修改的是head的副本,無(wú)法真正改變head的值。?
?
?????注:這里可以將head指針看成一個(gè)變量(不管它保存的是地址),就比較好理解了。
? ? ??
三(key)、其實(shí)本質(zhì)上還是傳值,傳址的問(wèn)題,只不過(guò)指針本身保存的地址,讓這個(gè)過(guò)程變得有點(diǎn)糾結(jié)。在函數(shù)調(diào)用需要修改指針變量的指向(value)時(shí),
?應(yīng)該傳遞指針變量的地址(address)。
? ? ? 另外,對(duì)于函數(shù)的形參是指針時(shí),只要該參數(shù)不在左邊(即都是右值操作),二維指針(形參)就可以簡(jiǎn)化為一維指針。如上面帶頭結(jié)點(diǎn)的尾插
簡(jiǎn)化版本。
總結(jié)
- 上一篇: JavaWeb三大组件(ServletF
- 下一篇: ElasticSearch模糊查询(中文