[C++日常小題] 計算二叉查找樹的高度

Description

給定一個二叉查找樹,要求計算其高度,每個二叉查找樹將給出先序與中序的遍歷。

例如:一個二叉查找樹其先序遍歷爲:16, 10, 4, 15, 23 ; 中序遍歷爲 4, 10, 15, 16, 23,則其高度爲2(假定空樹高度爲-1,只有根節點的數高度爲0)

Input

第一行輸入測試用例個數。

對於每個測試用例,

第一行是節點個數n,第二行是key值的先序遍歷,第三行是key值的中序遍歷

Output

對於每個測試用例,用一行輸出樹的高度


Sample Input

2
3
4 5 6
4 5 6
5
6 4 8 9 10
4 6 8 9 10

Sample Output

2
3


解題思路

首先,根據二叉樹的遍歷特性

  • 先序遍歷: 首先訪問根結點然後遍歷左子樹,最後遍歷右子樹。在遍歷左、右子樹時,仍然先訪問根結點,然後遍歷左子樹,最後遍歷右子樹,如果二叉樹爲空則返回。
  • 中序遍歷: 首先遍歷左子樹,然後訪問根結點,最後遍歷右子樹。在遍歷左、右子樹時,仍然先遍歷左子樹,再訪問根結點,最後遍歷右子樹。

可以推出,先序遍歷的第一個元素就爲二叉樹的根結點。
又在中序遍歷中,若找到根節點,則根節點左側的爲其左子樹結點,右側的爲其右子樹結點。

爲了更方便闡述,設幾個變量

  • root: 當前根結點在先序遍歷中的下標
  • pos: 當前根結點在中序遍歷中的下標
  • left: 根樹或子樹的所有結點在中序遍歷中的下標範圍的左邊界
  • right: 根樹或子樹的所有結點在中序遍歷中的下標範圍的右邊界(結點不包括右邊界)

若根結點有左子樹,則先序遍歷中下標爲 root + 1 的結點爲其左子樹的根結點,[left, pos) 爲其左子樹的所有結點。
當前根結點在先序遍歷中下標爲 root + pos - left + 1 的結點爲右子樹的根結點,[pos + 1, right) 爲其右子樹的所有結點。

知道了左子樹和右子樹的元素,並且找到了兩個子樹的根結點,通過同樣的方法,在左右子樹中分別再找其左右子樹,這樣遞歸下去,可以確定唯一的二叉樹。
這道題只要求出樹的高度,那麼我們可以認爲,若一個樹有左子樹或者右子樹,則樹的高度 + 1。

上面的描述可能不是很準確,舉個例子更清楚一點,一棵有9個結點的二叉樹

二叉樹樣例

  • 先序遍歷: 5 2 1 4 3 8 7 6 9
  • 中序遍歷: 1 2 3 4 5 6 7 8 9

根樹

  • root = 0;
  • pos = 4;
  • left = 0;
  • right = 9;

根結點爲 5;
左子樹的根結點爲 2先序遍歷中下標爲 root + 1 = 1;所有結點爲 1 2 3 4中序遍歷中下標範圍爲 [0, 4)
右子樹的根結點爲 8先序遍歷中下標爲 root + pos - left + 1 = 5;所有結點爲 6 7 8 9中序遍歷中下標範圍爲 [5, 9)

根樹的左子樹

  • root = 1;
  • pos = 1;
  • left = 0;
  • right = 4;

根結點爲 2;
左子樹的左子樹的根結點爲 1先序遍歷中下標爲 root + 1 = 2;所有結點爲 1中序遍歷中下標範圍爲 [0, 1)
左子樹的右子樹的根結點爲 4先序遍歷中下標爲 root + pos - left + 1 = 3;所有結點爲 3 4中序遍歷中下標範圍爲 [2, 4)

根樹的右左子樹

  • root = 5;
  • pos = 7;
  • left = 5;
  • right = 9;

根結點爲 8;
左子樹的左子樹的根結點爲 7先序遍歷中下標爲 root + 1 = 6;所有結點爲 6 7中序遍歷中下標範圍爲 [5, 7)
左子樹的右子樹的根結點爲 9先序遍歷中下標爲 root + pos - left + 1 = 8;所有結點爲 9中序遍歷中下標範圍爲 [8, 9)

左子樹的左子樹和右子樹,右子樹的左子樹和右子樹同理遞歸下去,就不再繼續列出來了。

Code

#include <cstdio>

int preorder[1000001];  // 先序遍歷
int inorder[1000001];  // 中序遍歷
int height = 0;  // 樹的高度

void findHeight(int root, int left, int right, int h);
int main() {
    int t, n;
    scanf("%d", &t);
    while (t--) {
        height = -1;
        scanf("%d", &n);

        for (int i = 0; i < n; ++i)
            scanf("%d", preorder + i);

        for (int i = 0; i < n; ++i)
            scanf("%d", inorder + i);

        findHeight(0, 0, n, 0);
        printf("%d\n", height);
    }
    return 0;
}
void findHeight(int root, int left, int right, int h) {
    if (left >= right) return;  // 判斷是否爲空樹
    int pivot = preorder[root];
    int pos = left;

    if (h > height) height = h;  // 更新樹的高度
    while (pos < right && inorder[pos] != pivot) pos++;  // 找根結點在中序遍歷中的下標

    // 遞歸遍歷左子樹
    if (left < pos) findHeight(root + 1, left, pos, h + 1);

    // 遞歸遍歷右子樹
    if (pos + 1 < right) findHeight(root + pos - left + 1, pos + 1, right, h + 1);
}

以上所有,如有錯誤,麻煩指出,我會及時更改的。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章