330 likes | 524 Vues
第四章 — 鏈結串列. 4.1 單一鏈結串列與鏈 鏈結串列畫出時如一連串節點,其中箭頭表示指標鏈結 ( 圖 4.1) 。指向串列第一個節點的指標之名稱也是串列的名稱。亦即,圖 4.1 中的串列稱為 prt 。 ptr 圖 4.1 :畫出鏈結串列常用的方法 要在 cat 和 sat 之間插入 mat ,我們必須: 取得一個目前未使用的節點;令其位址為 paddr 。 將此節點的資料欄位設定為 mat 。. 3. 將 paddr 的鏈結欄位設定為包含 cat 的節點之鏈結欄位中找到的位址。
E N D
4.1 單一鏈結串列與鏈 • 鏈結串列畫出時如一連串節點,其中箭頭表示指標鏈結(圖4.1)。指向串列第一個節點的指標之名稱也是串列的名稱。亦即,圖4.1中的串列稱為prt。 ptr 圖4.1:畫出鏈結串列常用的方法 • 要在cat和sat之間插入mat,我們必須: • 取得一個目前未使用的節點;令其位址為paddr。 • 將此節點的資料欄位設定為mat。
3. 將paddr的鏈結欄位設定為包含cat的節點之鏈結欄位中找到的位址。 4. 設定包含cat的節點之鏈結欄位,使其指向paddr。 • 圖4.2表示在插入mat以後串列的改變。從包含cat的結點出來的虛線為舊的鏈結,實線為新的鏈結。 ptr 圖4.2:在cat之後插入mat • 假設我們想要自串列中刪除mat。只要找到在mat之前的元素,即cat,設定其鏈結欄位,使其指向mat的鏈結欄位(圖4.3)。
ptr • 圖4.3;從串列刪除mat
函數create2建立了有兩個結點的鏈結函數。 函數create2傳回first,為指向串列開始的指標。 • 程式:建立有兩個節點的串列
ptr • 圖:有兩個節點的串列 • 程式:在串列之前的簡易插入
因為第二個指標node的位址不會改變,我們不需要將它的位址當作參數來傳送。典型的函數呼叫為insert (&ptr, node);其中ptr指向串列的開始,而node指向新的節點。 對於空的串列,設定temp的鏈結點為NULL,並將ptr之值改成temp的位址。對於非空的串列,將節點temp插入node與其鏈結欄所指的節點之間。 ptr node temp • 圖4.6:具有兩個節點的串列,在呼叫函數insert(&ptr, ptr);以後的串列
圖4.7中刪除的結點試試串列的第一個節點。這表示我們必須確實的改變起始位址ptr。圖4.7中刪除的結點試試串列的第一個節點。這表示我們必須確實的改變起始位址ptr。 在圖4.8中,node不是第一個節點,我們只要改變trial的鏈結欄,使其指向node之鏈結欄所指的節點即可 ptr node trail = NULL (a)刪除前 ptr (b)刪除後 • 圖4.7:呼叫函數delete( &per, NULL, ptr );以後的串列
ptr trial node (a) 刪除前 ptr (b) 刪除後 • 呼叫函數delete( &ptr, ptr, ptr-> link );以後的串列
函數delete從鏈結串列中刪除任意一個節點。除了改變鏈結欄,或*ptr之值以外,delete也會將配置給所刪除的節點之空間環給系統記憶體。要完成這件工作,使用free函數。函數delete從鏈結串列中刪除任意一個節點。除了改變鏈結欄,或*ptr之值以外,delete也會將配置給所刪除的節點之空間環給系統記憶體。要完成這件工作,使用free函數。 • 程式:自串列中刪除
4.6 鏈結堆疊與佇列 • 程式:在鏈結的堆疊中加入元素 • template <classT> • void LinkedStack <T>::Push(constT& e) { • top = new ChainNode <T>(e, top); • }
程式:自鏈結的堆疊中刪除元素 • template <class T> • voidLinkStack <T>::Pop( ) • {// 刪除堆疊的頂端節點 • if (IsEmpty()) throw “Stack is empty. Cannot delete.”; • ChainNode <T> *delNode = top; • top = top→link; // 移除頂端節點 • deletedelNode; // 釋回此節點 • }
程式:在鏈結佇列的後端加入元素 • template <class T> • voidLinkedQueue <T>::Push(constT& e) • { • if (IsEmpty( )) front = rear = newChainNode(e,0); //空佇列 • elserear = rear→link = new ChainNode(e,0); // 連接節點並且更新rear • }
程式:自鏈結佇列的前端刪除元素 • template <class T> • voidLinkedQueue <T>:: Pop() • {// 刪除佇列的第一個元素 • if (IsEmpty()) throw “Queue is empty. Cannot delete.”; • ChainNode<T> *delNode = front; • front = front→link; // 移除鏈的第一個節點 • deletedelNode; // 釋回此節點 • }
4.7 多項式 • 以單項鏈結串列來表示多項式 • 我們可將多項式的每一項以一個節點表示,其中包含係數欄和指數欄,以及一個指向下一項的指標。假設係數均為整數,則其型態宣告為: • struct Term • {// 所有Term的成員都內定為公用的 • int coef; // 係數 • int exp; // 指數 • Term Set(int c,int e) {coef = c;exp = e;return *this;}; • }; • classPolynomial { • public: • // 在這裡定義公用函式 • private: • Chain<Term> poly; • }; • 將Poly_node圖示如下:
圖4.11表示如何儲存多項式 和圖4.11表示如何儲存多項式 和 a (a) b (b) • 圖4.11:多項式表示法
多項式相加 • 要將兩個多項式相加,先從a和b所指的節點開始檢查他們的項次: • 如果兩個項次的指數是相等的,則將係數相加,並為相加結果的多項式產生一個新的項目。 • 如果在a中目前處理的項目之指數小於b中的,則複製b中的項目,並將其附加到結果多項式d中,而後將指標移到b的下一項。 • 若a expon > b expon,則在a上採用類似的運算。 a
b d • a->expon == b-> expon a b d
(b) a -> expon < b -> expon a b d (c) a -> expon > b -> expon • 圖4.12:產生d=a+b結果的前三項
每當 產生一個新的項目,我們要設定它的coef和expon欄位,並將他附加在d之後。為了避免每次加入的節點時必須找尋d的最後一個節點,我們使用一個指標rear來指向目前d中最後的一個節點。 • 程式:兩個多項式相加 • Polynomial Polynomial::operator+(constPolynomial& b) const • 2 {// 相加多項式 *this(a)與b並且回傳它們的和 • 3 Term temp; • 4 Chain <Term>::ChainIterator ai = poly.begin(), • 5 bi =b. poly.begin(); • 6 Polynomial c; • 7 while (ai&&bi) { // 目前的節點不是空的 • 8 if (ai→exp = = bi→exp) { • 9 int sum = ai→coef + bi→coef; • 10 if (sum) c.poly.InsetBack (temp.Set(sum, ai→exp)); • 11 ai++; bi++; // 前進至下一個項目 • 12 } • 13 else if (ai→exp < bi→exp) { • 14 c.poly.InsertBack(temp.Set(bi→coef, bi→exp)) ; • 15 bi++; // b的下一個項目 • 16 }
17 else { • 18 c.poly.InsertBack(temp.Set(ai→coef , ai→exp)) ; • 19 ai++; // a的下一個項目 • 20 } • 21 } • 22 while (ai) { // 複製a剩下的部份 • 23 c.poly.InsertBack(temp.Set(ai→coef,ai→exp)) ; • 24 ai++; • 25 } • 26 while (bi) { // 複製b剩下的部份 • 27 c.poly.InsertBack (temp.Set(bi→coef,bi→exp)) ; • 28 bi++ ; • 29 } • 29 returnc; • 30 }
刪除多項式 • 如果我們希望讀取多項式a(x), b(x), d(x),而後計算e(x)=a(x)*b(x)+d(x),他可能需要編寫許多的主函數,
當我們需要計算更多的多項式時,回收用來儲存temp(x)的節點是很有幫助的。將temp(x)的節點釋回,我們也許會用到她們來儲存其他的多項式。當我們需要計算更多的多項式時,回收用來儲存temp(x)的節點是很有幫助的。將temp(x)的節點釋回,我們也許會用到她們來儲存其他的多項式。 • 以環狀鏈結串列表示法表示多項式 • 如果我們修改串列的結構,使的最後一個節點的鏈結欄指向串列的第一個節點(如圖4.13),我們即可更有效率的釋回一個多項式的所有節點。我們稱其為環狀串列(circular list) 。單向鍊結串列的最後一個節點有虛鏈結時稱為鏈( chain ) 。 ptr • 圖4.13: 的環狀表示法
4.8 等價類 • 定義:在集合S上的關係, ≡稱為在S上的等價關係(equivalence relation),若且惟若他在S上具有對稱性,反身性,和遞移性。 • 舉例而言: 如果有12個多邊形,編號為0到11,且下列各組重複: 0 ≡ 4, 3 ≡ 1, 6 ≡ 10, 8 ≡ 9, 7 ≡ 4, 6 ≡ 8, 3 ≡ 5, 2 ≡ 11, 11 ≡ 0 我們可將這些分割成下列的等價類: { 0,2,4,7,11 };{ 1,3,5 };{ 6,8,9,10 }
判斷等價的演算法有兩個階段。在第一個階段中,我們讀取並儲存等價序對 < i, j >。 在第二個階段中,由0開始找出所有形式為< 0, j >的序對。持續以這種方法工作,直到找出,標記,並印出整個也含0的等價類為止。而後繼續找出別的等價類。 • 如果說有個陣列如:pairs[ n ][ m ]。這種方法可能會浪費許多的時間,因為只會用到少數的元素。而且這可能需要使用大量的時間將新的序對< i, k >插入列i。
[0] [1] [2] [3] [4] [5] [6] [7] [8] [9] [10] [11] data 11 3 11 5 7 3 8 4 6 8 6 0 link 0 0 0 0 0 0 data 4 1 0 10 9 2 link 0 0 0 0 0 0 • 圖4.18:在序對輸入以後的串列 • 分析等價程式:seq和out的設置需時O(n)。在第一階段輸入等價序對時,每一個序對需要一個常數時間。因此,這階段一共需要O(m+n)時間。
在第二階段,需要O(m+n)時間。所以,全部的計算為O(m+n)。 在第二階段,需要O(m+n)時間。所以,全部的計算為O(m+n)。
4.9 稀疏矩陣 • 在資料表示法中,稀疏矩陣的每一行以具有標頭節點的環狀鏈結串列表示。稀疏矩陣的每一列也有類似的表示法。 • 每一個節點有一個標示欄位,他用來區別標頭節點與資料節點。 每個標頭節點有3個欄位:down, right, next 每個資料節點載標示欄位外還有5個欄位:row, col, down, righ, value • 如果ai j ≠ 0, 就有一個節點的標示欄為entry, value = aij, row = i, col = j • 每一個標頭節點出現在三個串列中;列串列,行串列,以及標頭節點串列。
down head right next down head row col right entry i j value (a) 標頭節點 (b)資料節點 (c)設定aij • 圖4.19:稀疏矩陣節點構造 • 圖4.20:4*4稀疏矩陣a
4 0 1 3 4 2 0 3 H1 H3 H2 H0 Matrix head H0 11 • 圖4.21:稀疏矩陣a的鏈結表示法 H1 12 2 1 H2 -4 H3 -15
4.10 雙向鏈結串列 • 如果指標指向一個特定點的節點ptr,而我們想要找到在ptr之前的節點。必須從串列的開頭找起,直到找到一個節點,其鏈結欄位指向ptr為止。 • 對於刪除運算而言,因為我們必須知道前一個節點的位置,所以最有用的方法是使用雙向鏈結串列。 • 在雙向鍊結串列中的節點至少有三個欄位,一個是左鏈結欄( llink ),一個是資料欄( item ),一個是右鍵欄位( rlink )。 • 圖4.23中,標頭節點就像之前所說的一樣,可以使我們實作各種運算時較為容易。標頭節點的item欄通常不包含任何資訊。
rlink item llink Head Node • 圖4.23:具有標頭節點的雙向鏈結環狀串列 ptr • 圖4.24:具有標頭節點的空的雙向鏈結環狀串列 rlink item llink
node node • 圖4.25:插入節點到空的雙向鏈結環狀串列 delete • 圖4.26:從雙向鏈結環型串列中刪除 newnode node node