上節說過這節會講雙向鍊錶,環形鍊錶和應用舉例,我們開始吧!!!!
首先,明白什麼是雙向鍊錶。所謂雙向鍊錶是如果希望找直接前驅結點和直接後繼結點的時間複雜度都是 o(1),那麼,需要在結點中設兩個引用域,乙個儲存直接前驅結點的位址,叫 prev,乙個直接後繼結點的位址,叫 next,這樣的鍊錶就是雙向鍊錶(doubly linked list)。雙向鍊錶的結點結構示意圖如圖所示。
雙向鍊錶結點的定義與單鏈表的結點的定義很相似, ,只是雙向鍊錶多了乙個字段 prev。其實,雙向鍊錶更像是一根鏈條一樣,你連我,我連你,不清楚,請看圖。
雙向鍊錶結點類的實現如下所示
//乙個鏈條的類
public class dbnode
//構造器 這是不是初始化
public dbnode(dbnodep)
//構造器 吧這個鍊子相應值 傳遞給他
public dbnode(t val)
//構造器 構造乙個空的鍊子
public dbnode()
//資料域屬性
public t data
set }
//前驅引用域屬性
public dbnodeprev
set }
//後繼引用域屬性
public dbnodenext
set }
}說了這麼多雙向鍊錶接點的類的屬性,我們要看一看他的相關的操作。這裡只做一些畫龍點睛地方的描述
p.next.prev = s;
s.prev = p;
s.next = p.next;
p.next = s;
插入過程如圖所示(以 p 的直接後繼結點存在為例) 。
注意:引用域值的操作的順序不是唯一的,但也不是任意的,操作必須放到操作的前面完成,否則 p 直接後繼結點的就找不到了。這一點需要讀者把每個操作的含義搞清楚。此演算法時間操作消耗在查詢上,其時間的複雜度是o(n).
下面,看他的刪除操作,以在結點之後刪除為例來說明在雙向鍊錶中刪除結點的情況。 設 p是指向雙向鍊錶中的某一結點,即 p儲存的是該結點的位址,現要將乙個結點 s插入到結點 p的後面 。偽**如下:操作如下:
p.next = p.next.next;
p.next.prev = p.prev;
刪除過程如圖所示(以 p的直接後繼結點存在為例)
相應的演算法的時間複雜度也是消耗到結點的查詢上,其複雜度應該是o(n)
查詢操作與單鏈表的極其的類似,也是從頭開始遍歷。相應偽**如圖所示:
current.next=p.next.next
current.prev=p.next.prev;
相應的偽**如下圖所示:
該演算法的時間複雜度,是乙個個的遍歷的過程中,顧時間複雜度是o(n)
獲取當前的雙向鍊錶長度與 查詢類似,不做過多的贅述,這裡,我們把雙向鍊錶基本概念和操作基本介紹完了,下面介紹乙個重要的鍊錶——環形鍊錶。
首先,還是老樣子,看看環形鍊錶的定義。有些應用不需要鍊錶中有明顯的頭尾結點。在這種情況下,可能需要方便地從最後乙個結點訪問到第乙個結點。此時,最後乙個結點的引用域不是空引用,而是儲存的第乙個結點的位址(如果該煉錶帶結點,則儲存的是頭結點的位址) ,也就是頭引用的值。我們把這樣的鍊錶結構稱之為環形鍊錶。他就像小朋友手拉手做遊戲。如圖所示。
用鍊錶如圖所示:
這裡基本新增,刪除,操作的操作與單鏈表簡直是一模一樣,這裡就沒有必要寫這些東西。我們主要看他們一些簡單應用。
應用舉例一 已知單鏈表 h,寫一演算法將其倒置,即實現如圖所示的操作,其中(a)為倒置前,(b)為倒置後。
演算法思路:由於單鏈表的儲存空間不是連續的,所以,它的倒置不能像順表那樣,把第 i 個結點與第 n-i 個結點交換(i 的取值範圍是 1 到 n/2,n 為單鏈表的長度) 。其解決辦法是依次取單鏈表中的每個結點插入到新鍊錶中去。並且,為了節省記憶體資源,把原鍊錶的頭結點作為新鍊錶的頭結點。儲存整數的單鏈表的倒置的演算法實現如下:
public void reverslinklist(linklisth) }
該演算法要對鍊錶中的結點順序掃瞄一遍才完成了倒置,所以時間複雜度為o(n),但比同樣長度的順序表多花一倍的時間,因為順序表只需要掃瞄一半的資料元素。這個是不是你已經頭腦糊了嗎?如果糊了把,請看我的圖例的解釋。
舉例2,約瑟夫環問題,題目如下:
已知n個人(以編號1,2,3...n分別表示)圍坐在一張圓桌周圍。從編號為k的人開始報數,數到m的那個人出列;他的下乙個人又從1開始報數,數到m的那個人又出列;依此規律重複下去,直到圓桌周圍的人全部出列。求最後出列的人相應的編號。
void josephus(int n,int k,int m) //n為總人數,k為第乙個開始報數的人,m為出列者喊到的數
/* p為當前結點 r為輔助結點,指向p的前驅結點 list為頭節點*/
linklist p,r,list; /*建立迴圈鍊錶*/
for(int i=0;ip=(linklist)lnode;
p.data=i;
if(list==null)
list=p;
else
r.link=p;
r=p;
p.link=list; /*使鍊錶迴圈起來*/
p=list; /*使p指向頭節點*/
/*把當前指標移動到第乙個報數的人*/
for(i=0;ir=p;
p=p.link;
/*迴圈地刪除佇列結點*/
while(p.link!=p)
for(i=0;ir=p;
p=p.link;
r.link=p.link;
console.writeline("被刪除的元素: ",p.data);
free(p);
p=r.node.;
console.writeline("\n最後被刪除的元素是:",p.data);
具體的演算法,如圖所示:
這個演算法的時間的複雜度是o(n2) }
還和大家分享的乙個例子,就是我做做乙個類似與網易郵箱的產品時候,幾千萬甚至數以億級的大數量登入的時候,發現使用者登入的時候真他媽的慢,你猜我開始是怎麼做的,就是直接查資料庫,這當然是不行的。這怎麼辦了, 最後,我在乙個高人的指教下,發現登入的時候速度飛快,怎麼搞的。我把所有的資料庫的資料讀入到記憶體中,然後把資料用鍊錶把他們串起來,到我查詢某個使用者時候,只比較使用者的 位元組數。
這就是我眼中的鍊錶結構。
資料結構與演算法 揭秘
字面意思就是研究資料的一種方法,就是研究資料在程式中組織的一種方法。資料結構就是,元素與元素有一種或者多種關係的集合,在軟體界有一種比較普片的公式就是程式 資料結構 演算法。1 集合 set 和數學的集合一樣,具有唯一性,確定性,無序性。2 線性結構 典型的資料庫二維表,一對一的關係。3 樹形結構 ...
C 資料結構與演算法揭秘一
這裡,我們 來說一說c 的資料結構了。什麼是資料結構。資料結構,字面意思就是研究資料的方法,就是研究資料如何在程式中組織的一種方法。資料結構就是相互之間存在一種或多種特定關係的資料元素的集合。程式界有一點很經典的話,程式設計 資料結構 演算法。用源 來體現,資料結構,就是程式設計。他有哪些具體的關係...
C 資料結構與演算法揭秘13
這節,我們來看看一下什麼了,來看看圖的遍歷吧!首先,搞清楚,圖的遍歷的基本的含義了。圖的遍歷是指從圖中的某個頂點出發,按照某種順序訪問圖中的每個頂點,使每個頂點被訪問一次且僅一次。圖的遍歷與樹的遍歷操作功能相似。圖的遍歷是圖的一種基本操作,並且圖的許多其他操作都是建立在遍歷操作的基礎之上的。遍歷示意...