到目前為止,本章描述了一些用於建立和維護鍊錶的演算法,包括在鍊錶的開頭、結尾和中間新增項的演算法,查詢鍊錶中項的演算法和從鍊錶中刪除項的演算法。
以下各節描述了利用其他方式來操作鍊錶的演算法。
3.5.1 複製鍊錶
一些演算法重新排列鍊錶。本節和下一節將描述一些對鍊錶中的項進行排序的演算法。如果想保持原來鍊錶的順序,就必須在排序之前就做乙個該鍊錶的副本。
下面的偽**演示了如何複製乙個單鏈表:
該演算法相當簡單,但有一點值得一提:該演算法使用last_added來跟蹤最新被加入到鍊錶副本中的單元格。當要把乙個項插入到鍊錶中時,該演算法將last_added.next指向乙個新的單元格物件。這使新的物件在該鍊錶的末尾。然後該演算法更新last_added以指向新單元格並將原始單元格的值賦予它。
這允許鍊錶從底部而不是從頂部增長。如果如同3.10節「練習1」中的所描述的那樣:跟蹤鍊錶的最後乙個項,類似於如何輕鬆地將項新增到鍊錶的末尾。
3.5.2 鍊錶的插入排序
在第6章中談到了許多關於排序的演算法,但是其中有兩種演算法值得在這裡進行討論:選擇排序(我們將在下一節中進行描述)和插入排序。
排序演算法的基本思想是把乙個單元格從待輸入鍊錶中提取出來,並將其插入到乙個已經排好序的鍊錶(其最初開始是空的)中的適當位置。
下面的偽**展示了插入排序演算法,這裡的待排序的各個單元格被儲存在乙個具有top哨兵的單向鍊錶中:
該演算法開始通過建立乙個空的鍊錶來儲存排序的輸出。然後,它從乙個無序的輸入鍊錶的頭至尾進行迴圈。對於每個輸入單元格,演算法遍歷前面已經排好序的鍊錶來找到乙個新單元格對應的位置。然後,它把新單元格插入。
如果呼叫之前的3.2.6節中描述的插入單元格演算法insetcell,可以簡化**。
如果輸入鍊錶中的單元格已經按照從大到小的順序排列好,該演算法只需用短短的幾個步驟就可以實現從鍊錶開頭插入乙個新單元格。如果鍊錶儲存n個單元格,在新的鍊錶中插入的所有項總共需要o(n)的步驟。這是該演算法的最佳情況。
如果在輸入鍊錶中單元格已經從小到大排序,這種演算法必須從新的鍊錶的末尾插入每個單元格。而對於每次插入乙個鍊錶都需要查詢已有鍊錶的末尾單元格。因此,插入所有項需要1+2+3+…+n=n×(n+1)÷2=o(n2)的步驟。
在平均情況下,要插入的項是隨機排列的,該演算法可以快速插入一些項,但其他項需要更長的時間。其結果是,該演算法的執行時間仍然是o(n2),雖然實際上它不會需要最壞情況所需的時間。許多其他的排序演算法只需要o(n log n)的時間,而這種演算法的時間複雜度為o(n2),表現則相對緩慢。這使得該演算法面對資料量較大的鍊錶表現無力。然而,它在資料量較小的鍊錶中執行很快,並且它適用於鍊錶,許多其他的演算法並不適用於鍊錶。
《演算法基礎》 3 4 有序鍊錶
有時,讓鍊錶中的項保持有序是十分方便的。當將乙個新項加入有序鍊錶時,需要搜尋鍊錶來找到該項所屬位置,並更新相應的鏈結來插入該項。下面的偽 顯示了在乙個有序鍊錶中插入乙個項的演算法 在最壞的情況下,該演算法可能需要遍歷整個鍊錶為新項找到正確的位置。因此,如果該鍊錶儲存n個單元格,其執行時間為o n 雖...
演算法 鍊錶常用演算法
常見演算法 1.鍊錶逆序 2.鍊錶求交點 3.鍊錶求環 4.鍊錶劃分 5.複雜鍊錶的複製 6 a.2個排序鍊錶歸併 6 b.k個排序鍊錶歸併 鍊錶定義 definition for singly linked list.struct listnode 1.鍊錶逆序 classsolution ret...
基礎演算法模板之鍊錶 排序
1.鍊錶 操作 建表 反轉 合併兩個有序的鍊錶,其他簡單的刪除節點 插入節點就不說了 include include using namespace std struct listnode 建表 void createlist listnode phead 鍊錶非空 else pnode next ...