1. 程式人生 > >二叉樹的常見演算法

二叉樹的常見演算法

1.二叉樹的遍歷演算法 二叉樹的遍歷主要分為三種:先序遍歷,中序遍歷和後序遍歷。還有一種就是按照層次遍歷。 按照慣例,左孩子優先於右孩子,那麼:

先序遍歷指的就是先訪問本節點,再訪問該節點的左孩子和右孩子; 中序遍歷指的就是:先訪問左孩子,再訪問本節點,最後訪問右孩子; 後序遍歷指的就是:先訪問左右孩子,最後訪問本節點。 層次遍歷:按照樹的每一層(高度)進行遍歷。 樹的節點的資料結構常宣告為:

約定給出根節點,分別使用三種遍歷方式得到二叉樹的序列:得益於遞迴的簡潔性,三種遍歷方式的遞迴演算法也是非常簡潔和易懂的。

(1). 先序遍歷

先序遍歷的理解:沿著最左側通路自頂而下訪問各個節點,自底而上遍歷對應的右子樹。迭代版本需要用到棧這種資料結構。

(2). 中序遍歷

後序遍歷的迭代版本和前序遍歷類似: 可以證明,右孩子優先的先序遍歷序列的逆序列就是左孩子優先的後序遍歷序列。

假設根節點的值為a,L1和R1分別是左子樹和右子樹在右孩子優先的先序遍歷序列,L2和R2分別是左子樹和右子樹在左孩子優先的後序遍歷序列,所以只需要證明:序列"a,R1,L1"是序列"L2,R2,a"的逆序列即可。 從序列的組成來看,只需要證明R1是R2的逆序列且L1是L2的逆序列,顯然這就將問題分解,平凡的情況下是顯然成立的,因此可以歸納證明出這個結論。

2. 二叉樹的其它演算法

(1). 二叉樹的深度

遞迴版本非常簡潔,也非常易懂;迭代版本則需要利用我們之前介紹的按照層次遍歷,層數就是二叉樹的深度。

(2). 二叉樹的映象

操作給定的二叉樹,將其變換為源二叉樹的映象。使用遞迴,當節點存在至少一個孩子時,交換左右孩子,再遞迴處理。

(3). 平衡二叉樹 輸入一棵二叉樹,判斷該二叉樹是否是平衡二叉樹。 平衡二叉樹指的是:它是一棵空樹或它的左右兩個子樹的高度差的絕對值不超過1,並且左右兩個子樹都是一棵平衡二叉樹,見百度百科。關鍵點:子樹的高度差不超過1且子樹也是平衡樹。構造一個遞迴函式IsBalanced來判斷這兩個條件。

(4). 對稱的二叉樹

請實現一個函式,用來判斷一顆二叉樹是不是對稱的。注意,如果一個二叉樹同此二叉樹的映象是同樣的,定義其為對稱的。

(5). 把二叉樹列印成多行

從上到下按層列印二叉樹,同一層結點從左至右輸出。每一層輸出一行。 在求二叉樹的深度的時候,迭代解法起始我們已經做了這個事情,只是沒有按照多行輸出,所以只需要記錄每一行的val即可。

(6). 二叉樹的下一個結點

給定一個二叉樹和其中的一個結點,請找出中序遍歷順序的下一個結點並且返回。注意,樹中的結點不僅包含左右子結點,同時包含指向父結點的指標。 節點的資料結構表示為:

若允許一定的空間複雜度,可以直接中序遍歷儲存序列之後直接查詢。這種方法就不介紹了,下面介紹常數空間的演算法:

如果該節點有右孩子,必然下一個節點就是該節點的右孩子的沿著左側鏈下的最後一個左孩子; 該節點沒有右孩子,也沒有父節點,說明這個節點是最後一個節點; 該節點沒有右孩子,但是有父節點且是父節點的左孩子,必然下一個節點就是該節點的父節點; 該節點沒有右孩子,且有父節點且是父節點的右孩子,要麼該節點是最後一個節點,要麼沿著父鏈上升,之後第一個節點的是其父節點的左孩子,這個父節點就是下一個節點。

(7). 二叉搜尋樹與雙向連結串列 輸入一棵二叉搜尋樹,將該二叉搜尋樹轉換成一個排序的雙向連結串列。要求不能建立任何新的結點,只能調整樹中結點指標的指向。 二叉搜尋樹(Binary Search Tree, BST):它或者是一棵空樹,或者是具有下列性質的二叉樹: 若它的左子樹不空,則左子樹上所有結點的值均小於它的根結點的值; 若它的右子樹不空,則右子樹上所有結點的值均大於它的根結點的值; 它的左、右子樹也分別為二叉排序樹。詳見百度百科。 中序遍歷一顆二叉搜尋樹,必然得到的是一個有序的序列。

(8). 二叉樹中和為某一值的路徑

輸入一顆二叉樹的跟節點和一個整數,打印出二叉樹中結點值的和為輸入整數的所有路徑。路徑定義為從樹的根結點開始往下一直到葉結點所經過的結點形成一條路徑。(注意: 在返回值的list中,陣列長度大的陣列靠前)

(9). 按之字形順序列印二叉樹 請實現一個函式按照之字形列印二叉樹,即第一行按照從左到右的順序列印,第二層按照從右至左的順序列印,第三行按照從左到右的順序列印,其他行以此類推。 思路,構建兩個棧,依次儲存奇數行和偶數行的節點,注意左右孩子的入棧順序;例如:從第0行到第1行,第1行先左孩子,再右孩子入棧,這樣出棧的時候才會從右往左的順序;從第1行到第2行,先右孩子,再左孩子入棧,這樣才能保證出棧的順序是從左往右。 //按之字形順序列印二叉樹 vector<vector<int>> Print(TreeNode *pRoot) {     if (!pRoot) return {};     vector<vector<int>> result;     stack<TreeNode *> odd, even;     even.push(pRoot);        //從第零行開始     while (!even.empty() || !odd.empty()) {         vector<int> line;         if (odd.empty()) {             while (!even.empty()) {                 TreeNode *curr_node = even.top();                 even.pop();                 line.push_back(curr_node->val);                 if (curr_node->left) odd.push(curr_node->left);                 if (curr_node->right) odd.push(curr_node->right);    //注意,先左後右             }         }         else {             while (!odd.empty()) {                 TreeNode *curr_node = odd.top();                 odd.pop();                 line.push_back(curr_node->val);                 if (curr_node->right) even.push(curr_node->right);                 if (curr_node->left) even.push(curr_node->left);        //注意,先右後左             }                     }         result.push_back(line);     }     return result;

10. 二叉搜尋樹的第k個結點

給定一棵二叉搜尋樹,請找出其中的第k小的結點。例如: (5,3,7,2,4,6,8) 中,按結點數值大小順序第三小結點的值為4。 如果按照中序遍歷儲存起來,這種思路是非常簡單且容易實現的:

11. 二叉搜尋樹的後序遍歷序列 輸入一個整數陣列,判斷該陣列是不是某二叉搜尋樹的後序遍歷的結果。如果是則輸出Yes,否則輸出No。假設輸入的陣列的任意兩個數字都互不相同。 對於後序遍歷,我們知道序列最後一個數字必定是二叉搜尋樹的根節點,由於保證序列的任意兩個數字都互不相同,設序列中第一個大於根節點的位置為k,那麼[0,k)這一段子序列必然是左子樹後序遍歷得到的子序列,[k+1, end)必然是右子樹後序遍歷得到的子序列,依此遞迴即可,另外,在[k+1, end)若存在有一個節點的值小於根節點,說明不是二叉搜尋樹的後序遍歷序列 。

11. 重建二叉樹

輸入某二叉樹的前序遍歷和中序遍歷的結果,請重建出該二叉樹。假設輸入的前序遍歷和中序遍歷的結果中都不含重複的數字。例如輸入前序遍歷序列{1,2,4,7,3,5,6,8}和中序遍歷序列{4,7,2,1,5,3,8,6},則重建二叉樹並返回。

//重建二叉樹 TreeNode *reConstructBinaryTree(vector<int>::iterator pre_first,                              vector<int>::iterator pre_last,                                 vector<int>::iterator vin_first,                                 vector<int>::iterator vin_last) {     if (vin_last - vin_first != pre_last - pre_first ||             pre_last == pre_first ||             vin_first == vin_last) {         return nullptr;     }     TreeNode *curr_node = new TreeNode(*pre_first);     if (pre_last == pre_first + 1) return curr_node;     auto iter = vin_first;     while (iter < vin_last) {         if (*iter == *pre_first) break;         iter++;     }     int len = iter - vin_first;     curr_node->left = reConstructBinaryTree(pre_first + 1, pre_first + len + 1, vin_first, iter);     curr_node->right = reConstructBinaryTree(pre_first + len + 1, pre_last, iter + 1, vin_last);     return curr_node; } TreeNode *reConstructBinaryTree(vector<int> pre, vector<int> vin) {   return reConstructBinaryTree(pre.begin(), pre.end(), vin.begin(), vin.end()); }

12. 樹的子結構

輸入兩棵二叉樹A,B,判斷B是不是A的子結構。(ps:我們約定空樹不是任意一個樹的子結構)