數據結構和算法:鏈表(下)

如何輕鬆寫出的正確的鏈表代碼

技巧一:掌握指針或引用的含義

事實上,看懂鏈表的結構並非很難,可是一旦把它和指針混在一塊兒,就和容易讓人摸不着頭腦。因此,要寫好代碼,首先就要理解好指針node

有些語言有「指針」的概念  ,好比C語言;有些語言沒有指針,取而代之的是「引用」。實際上,它們的意思都是同樣的,都是存儲所指對象的內存地址算法

對於指針的理解:將某個變量賦值給指針,實際上就是將這變量的地址賦值給指針,或者反過來講,指針中存儲了這個變量的內存地址指向了這個變量,經過指針就能夠找到這個變量。瀏覽器

在編寫鏈表代碼的時候,咱們常常會這樣寫:p->next=q,這行代碼是說,p結點的next指針存儲的是q結點的內存地址數據結構

還有一個比較複雜的:p->next=p->next->next,這行代碼表示,p結點的next指針存儲的是p結點的下下個結點的內存地址post


技巧二:警戒指針丟失和內存泄漏 

寫鏈表代碼的時候,指針指來指去,一會就不知道指哪裏了。因此,咱們在寫的時候,必定注意不要丟了指針指針

指針是怎麼弄丟的呢?舉例說明一下:假如一個鏈表有頭結點a和尾結點b兩個結點,咱們但願在a和b之間插入結點x,假設當前指針p指向結點a,即p=a若是向下述同樣實現代碼,就會發生指針丟失和內存泄漏。調試

p->next=x;//將p的next指針指向x結點;code

x-next=p->next;//將x的next結點指向b結點對象

可是這裏有個問題:p->next指針在完成第一步操做以後,已經再也不指向結點p了,而是指向結點x。第二行代碼至關於將x賦給x->next本身指向本身。所以,整個鏈表就斷成了兩半從b之後的結點都沒法訪問了。內存


技巧三:利用哨兵簡化實現難度

首先,回顧一下單鏈表的插入和刪除操做。若是我麼在p結點的後邊插入一個新的結點,下面兩行代碼就能實現:

new_node->next=p-next;

p->next=new_node;

可是,對於一個空鏈表插入一個新節點,上述代碼邏輯就不能用了。咱們須要進行以下的特殊處理:

if(head=null){head=new_node}

單鏈表刪除結點,要刪除p結點的後繼結點,只須要一行代碼就能實現:

p->next=p->next->next

可是,若是咱們要刪除鏈表的尾結點,上述代碼邏輯就不work了。跟插入相似,咱們也須要作以下的特殊處理

if(p->next==null){p=null;}

從上述的分析得出,針對鏈表的插入、刪除操做,須要對插入第一個結點刪除最後一個結點的狀況作特殊處理。這樣代碼就會看起來很繁瑣,不簡潔,並且也容易由於考慮不全而出錯。如何來解決這個問題呢?

哨兵就能夠派上用場了。

若咱們引入哨兵結點,在任什麼時候候,無論鏈表是否是空,head指針都會一直指向這哨兵結點。咱們把這種有哨兵結點的鏈表稱做帶頭鏈表相反,沒有哨兵的鏈表叫作不帶頭鏈表

哨兵結點是不存儲數據的。由於哨兵結點一直存在,因此插入第一個結點和插入其餘結點,刪除最後一個結點和刪除其餘結點,均可以統一爲相同的代碼了。 

技巧四:重點留意邊界條件處理

軟件開發中,代碼在如下邊界或者異常狀況下,最容易產生Bug。

咱們常常用來檢查鏈表代碼是否正確的邊界條件有這幾個:

1)若是鏈表爲空時,代碼是否正常工做?

2)若是鏈表只包含一個結點時,代碼是否正常工做?

3)若是鏈表只包含兩個結點時,代碼是否正常工做?

4)代碼處理頭結點和尾結點時,代碼是否能正常工做?

技巧五:舉例畫圖,輔助思考

技巧六:多寫多練,沒有捷徑

若是已理解不可以掌握以前的技巧,可是手寫代碼仍是會出現各類各樣的錯誤,也不要着急,就是把常見的鏈表操做都本身多寫幾遍,出問題就一點一點調試,孰能生巧

下述選了鏈表的五個經常使用操做。將這幾個操做都能寫熟練,不熟就多寫幾遍,鏈表代碼就不用懼怕了。

1)單鏈表反轉

2)鏈表中環的檢測

3)兩個有序的鏈表合併

4)刪除鏈表倒數第n個結點

5)求鏈表的中間結點


下一篇:數據結構與算法:棧:如何實現瀏覽器的前進和後退功能?

相關文章
相關標籤/搜索