從0開始學FHQ-Treap(無旋Treap)
阿新 • • 發佈:2021-08-23
目錄
寫在前頭
本文節奏較慢,目標是讓有僅有一定程式設計基礎的小學生OIer也能學會,基礎較強的OIer可以直接跳過部分,直接從什麼是FHQ-Treap開始,內附例題、講解、程式碼,博主已盡力詳實。
那麼從現在開始,就請各位讀者老爺泡一杯養生茶,坐下,跟著博主一起,從0開始學FHQ-Treap。
FHQ-Treap詳解
這一部分,我將分層次將FHQ-Treap剖析透徹。
什麼是二叉樹(Binary Tree,BT)
首先,FHQ-Treap是一種二叉樹,二叉樹是一種滿足以下性質的資料結構:
- 該資料結構可以為空,即一棵二叉樹可以沒有任何節點;
- 一棵非空的二叉樹必須有且僅有一個確定的根節點。
- 一棵二叉樹中,每個節點可以至多擁有兩個子節點,稱作左右兒子,除根節點外,每個點都有一個確定的父親,同時該節點必須確定為它父親的左兒子或右兒子(即使他的父親只有一個兒子)。
特別的,二叉樹有三種遍歷方式:
- 前序遍歷:對於每個節點,先列印自己的編號,再遞迴列印其左子樹,最後遞迴列印其右子樹;
- 中序遍歷:對於每個節點,先遞迴列印其左子樹,再列印自己的編號,最後遞迴列印其右子樹;
- 後序遍歷:對於每個節點,先遞迴列印其左子樹,再遞迴列印其右子樹,最後列印自己的編號。
綜上所述,對於下面這幾棵二叉樹, 他們的三種遍歷結果如下:
- 前序遍歷:
1,2,4,5,3,6
- 中序遍歷:
5,4,2,1,6,3
- 後序遍歷:
5,4,2,6,3,1
- 前序遍歷:
1,2,4,5,3
- 中序遍歷:
4,2,5,1,3
- 後序遍歷:
4,5,2,3,1
- 前序遍歷:
1,2,4,5,6,3,7,8,9,10
- 中序遍歷:
5,4,6,2,1,9,8,10,7,3
- 後序遍歷:
5,6,4,2,9,10,8,7,3,1
從程式設計的角度來講,我們可以用一個結構體來表示一個二叉樹的節點,運用遞迴將三種遍歷的結果輸出,程式碼如下:
struct Node { // 二叉樹的節點 int v; // 該節點的值 int l, r; // 該節點的左右兒子在陣列中的下標位置,若為0則該兒子為空 Node(int v = 0) // 建構函式,Node(v)返回一個值為v左右兒子為空的節點 : v(v), l(0), r(0) {} } a[N]; // 用陣列模擬地址,下標模擬指標,動態分配 void print(int p) { // 前序遍歷結果列印 if (!p) return; cout << a[p].v << ' '; // 先列印自己的編號 print(a[p].l); // 再遞迴列印其左子樹 print(a[p].r); // 最後遞迴列印其右子樹 } void print(int p) { // 中序遍歷結果列印 if (!p) return; print(a[p].l); // 先遞迴列印其左子樹 cout << a[p].v << ' '; // 再列印自己的編號 print(a[p].r); // 最後遞迴列印其右子樹 } void print(int p) { // 後序遍歷結果列印 if (!p) return; print(a[p].l); // 先遞迴列印其左子樹 print(a[p].r); // 再遞迴列印其右子樹 cout << a[p].v << ' '; // 最後列印自己的編號 }
二叉樹有很多衍生,如完全二叉樹、滿二叉樹、二叉堆、0/1Trie樹、二叉搜尋樹等等,在不同領域也有很多應用,涉及到的二叉樹的性質也各不相同,但這次我們僅僅只是為了學習FHQ-Treap,所以以上的知識已經夠用了,有興趣的讀者老爺可以自行百度,我們接下來重點講其中一種重要的分支——二叉搜尋樹。
什麼是二叉搜尋樹(Binary Search Tree,BST)
其次,FHQ-Treap是一種二叉搜尋樹,一棵最基本的二叉搜尋樹滿足以下幾個性質:
- 每個節點有一個權值;
- 每個節點的權值大於它左子樹任意節點的權值,同時小於它右子樹任一節點的權值,特殊的,與當前節點權值相同的節點可以存在在該節點的左子樹亦或右子樹中,也可以統計在該節點中;
- 二叉搜尋樹的任意一個節點及它的子樹也是一棵二叉搜尋樹。
根據上面的性質,我們可以總結出一個定理:二叉搜尋樹的中序遍歷結果必然是一個單調不遞減序列。
接下來就是重頭戲:二叉查詢樹有什麼用?
二叉查詢樹的性質使我們可以很方便地維護一棵二叉查詢樹,包括插入,查詢存在(已知數集是否包含 \(x\) )、前繼(已知數集中小於 \(x\) 的最大數)、後繼(已知數集中大於 \(x\) 的最小數)、排名( \(x\) 在已知數集中的排名),刪除,索引(查詢排名第 \(i\) 的數)等等。