1. 程式人生 > >簡單資料結構(佇列 棧 樹 堆 )

簡單資料結構(佇列 棧 樹 堆 )

基礎知識

基本概念

    程式 = 演算法 + 資料結構

    資料結構是計算機儲存、組織資料的方式。

    資料結構是指相互之間存在一種或多種特定關係的資料元素的集合。

    通常情況下,精心選擇的資料結構可以帶來更高的執行或者儲存效率。

    資料結構往往同高效的檢索演算法和索引技術有關。

常見資料結構

    集合:set,multiset

    線性結構:陣列、連結串列、佇列、棧

    樹形結構:二叉樹及其變型,線段樹,巴拉巴拉

    圖形結構:各種圖

棧和佇列

棧Stack

    先進後出(FILO)
1240
FILO

佇列Queue

    先進先出(FIFO)
1240
FIFO

樹和堆

樹的定義

樹(tree)是包含n(n>0)個結點的有窮集,其中:

  1. 每個元素稱為結點(node)
  2. 有一個特定的結點被稱為根結點或樹根(root)
  3. 除根結點之外的其餘資料元素被分為m(m≥0)個互不相交的集合T1,T2,……Tm-1,其中每一個集合Ti(1<=i<=m)本身也是一棵樹,被稱作原樹的子樹(subtree)。
  4. 空集也是一棵樹
    樹去掉根節點叫做森林

樹的定義的等價命題

  • 設G=<V,E>是n階m條邊的無向圖,則下面各命題是等價的:
    • G 是樹.
    • G 中任意兩個頂點之間存在惟一的路徑.
    • G 中無迴路且 m=n-1.
    • G 是連通的且 m=n-1.
    • G 是連通的且 G 中任何邊均為橋.
    • G 中沒有迴路,但在任何兩個不同的頂點之間加一條新邊,在所得圖中得到惟一的一個含新邊的圈.

樹的性質

  • 如果G是樹,那麼邊數=頂點數-1
  • 樹中任意兩點存在唯一路徑
  • 樹是連通的而且任何邊均為橋
  • 在樹中不同兩點加上一個邊會得到唯一一個圈

二叉樹

1240
二叉樹
  • 二叉樹是每個節點最多有兩個子樹的樹結構。通常子樹被稱作“左子樹”和“右子樹”。二叉樹常被用於實現二叉查詢樹和二叉堆。
  • 一棵深度為k,且有2^(k-1)個節點稱之為滿二叉樹,一棵二叉樹第i層最多有2^(i-1)個節點;
  • 深度為k,有n個節點的二叉樹,當且僅當其每一個節點都與深度為k的滿二叉樹中,序號為1至n的節點對應時,稱之為完全二叉樹

任何一個包含n個節點完全二叉樹(滿足從根節點開始,依次從上往下,從左往右遍歷子節點,進行標記。如上圖),對於任何下標為i的節點來說,1≤i≤n 有:

  • 當i≠1時,parent(i)在⌊i/2⌋.i=1時,i是樹根,沒有父節點。
  • 當2i≤n時,lchild(i)在2i。2i>n,i沒有左孩子。
  • 當2i+1≤n時,rchild(i)在2i+1.2i+1>n,i沒有右孩子。

堆(Heap)

  • 最大堆:每個節點的值都大於等於它的孩子節點。
  • 最小堆:每個節點的值都小於等於它的孩子節點。

堆的儲存

  • 可以理解為二叉樹的一種,是節點間有序關係的完全二叉樹,所以可以用陣列來表示。
  • 對於下標為i的節點,它的子樹的左節點的下標為2i,右節點為2i+1,父親的節點下標為i/2(向下取整)。
  • 在程式設計中,使用位運算來代替直接*2可以提高執行速度。-
  • 某些編譯器中會把一些特定的乘法運算改寫為位運算。

字首、中綴、字尾表示式轉換與求值

  • 字首表示式:運算子位於運算元之前。
  • 中綴表示式:操作符處於運算元的中間。中綴表示式是人們常用的算術表示方法。(但是計算機計算中綴表示式是複雜的,所以一般需要將中綴表示式轉換成字首或者字尾表示式)
  • 字尾表示式:運算子位於運算元之後。

舉例:
(3+4)×5-6 中綴表示式
-×+3456 字首表示式
34+5×6- 字尾表示式

字首表示式的計算機求值:

從右至左掃描表示式,遇到數字時,將數字壓入堆疊,遇到運算子時,彈出棧頂的兩個數,用運算子對它們做相應的計算(棧頂元素 op 次頂元素),並將結果入棧;重複上述過程直到表示式最左端,最後運算得出的值即為表示式的結果。
例如字首表示式“- × + 3 4 5 6”:

  1. 從右至左掃描,將6、5、4、3壓入堆疊;
  2. 遇到+運算子,因此彈出3和4(3為棧頂元素,4為次頂元素,注意與字尾表示式做比較),計算出3+4的值,得7,再將7入棧;
  3. 接下來是×運算子,因此彈出7和5,計算出7×5=35,將35入棧;
  4. 最後是-運算子,計算出35-6的值,即29,由此得出最終結果。

字尾表示式的計算機求值:

與字首表示式類似,只是順序是從左至右:
從左至右掃描表示式,遇到數字時,將數字壓入堆疊,遇到運算子時,彈出棧頂的兩個數,用運算子對它們做相應的計算(次頂元素 op 棧頂元素),並將結果入棧;重複上述過程直到表示式最右端,最後運算得出的值即為表示式的結果。
例如字尾表示式“3 4 + 5 × 6 -”:

  1. 從左至右掃描,將3和4壓入堆疊;
  2. 遇到+運算子,因此彈出4和3(4為棧頂元素,3為次頂元素,注意與字首表示式做比較),計算出3+4的值,得7,再將7入棧;
  3. 將5入棧;
  4. 接下來是×運算子,因此彈出5和7,計算出7×5=35,將35入棧;
  5. 將6入棧;
  6. 最後是-運算子,計算出35-6的值,即29,由此得出最終結果。

將中綴表示式轉換為字首表示式:

  1. 初始化兩個棧:運算子棧S1和儲存中間結果的棧S2;
  2. 從右至左掃描中綴表示式;
  3. 遇到運算元時,將其壓入S2;
  4. 遇到運算子時,比較其與S1棧頂運算子的優先順序:
    • 如果S1為空,或棧頂運算子為右括號“)”,則直接將此運算子入棧;
    • 否則,若優先順序比棧頂運算子的較高或相等,也將運算子壓入S1;
    • 否則,將S1棧頂的運算子彈出並壓入到S2中,再次轉到4-1與S1中新的棧頂運算子相比較;
  5. 遇到括號時:
    • 如果是右括號“)”,則直接壓入S1;
    • 如果是左括號“(”,則依次彈出S1棧頂的運算子,並壓入S2,直到遇到右括號為止,此時將這一對括號丟棄;
  6. 重複步驟(2)至(5),直到表示式的最左邊;
  7. 將S1中剩餘的運算子依次彈出並壓入S2;
  8. 依次彈出S2中的元素並輸出,結果即為中綴表示式對應的字首表示式。

將中綴表示式轉換為字尾表示式:

  1. 初始化兩個棧:運算子棧S1和儲存中間結果的棧S2;
  2. 從左至右掃描中綴表示式;
  3. 遇到運算元時,將其壓入S2;
  4. 遇到運算子時,比較其與S1棧頂運算子的優先順序:
    • 如果S1為空,或棧頂運算子為左括號“(”,則直接將此運算子入棧;
    • 否則,若優先順序比棧頂運算子的高,也將運算子壓入S1(注意轉換為字首表示式時是優先順序較高或相同,而這裡則不包括相同的情況);
    • 否則,將S1棧頂的運算子彈出並壓入到S2中,再次轉到4-1與S1中新的棧頂運算子相比較;
  5. 遇到括號時:
    • 如果是左括號“(”,則直接壓入S1;
    • 如果是右括號“)”,則依次彈出S1棧頂的運算子,並壓入S2,直到遇到左括號為止,此時將這一對括號丟棄;
  6. 重複步驟(2)至(5),直到表示式的最右邊;
  7. 將S1中剩餘的運算子依次彈出並壓入S2;
  8. 依次彈出S2中的元素並輸出,結果的逆序即為中綴表示式對應的字尾表示式(轉換為字首表示式時不用逆序)。