1. 程式人生 > 其它 >P5076 【深基16.例7】普通二叉樹(簡化版)題解

P5076 【深基16.例7】普通二叉樹(簡化版)題解

題目傳送門

一、陣列+lower_bound+upper_bound模擬

#include <bits/stdc++.h>

using namespace std;
const int N = 10010;
int a[N];
int q, cmd, number, cnt;
long long p;

//解法1:暴力(使用二分優化查詢)
//lower_bound(begin,end,number)函式是求begin-end中(排好序)第一個大於等於 number的數
//upper_bound(begin,end,number)函式是求begin-end中(排好序)第一個大於 number的數
int main() {
    //q次詢問
    cin >> q;
    while (q--) {
        cin >> cmd; //1-5共5個命令
        switch (cmd) {
            case 1:
                cin >> number;
                cout << lower_bound(a + 1, a + cnt + 1, number) - a << endl;
                break;
            case 2:
                cin >> number;
                cout << a[number] << endl;
                break;
            case 3:
                cin >> number;
                p = lower_bound(a + 1, a + cnt + 1, number) - a;
                if (p == 1) cout << -2147483647 << endl;
                else cout << a[p - 1] << endl;
                break;
            case 4:
                cin >> number;
                p = upper_bound(a + 1, a + cnt + 1, number) - a;
                if (p == cnt + 1) cout << 2147483647 << endl;
                else cout << a[p] << endl;
                break;
            case 5:
                cin >> number;
                a[++cnt] = number;
                sort(a + 1, a + cnt + 1);
                break;
        }
    }
    return 0;
}

二、BST二叉搜尋樹

#include <bits/stdc++.h>

using namespace std;

//題解
//https://www.cnblogs.com/do-while-true/p/13566274.html
//const int INF = 2147483647;  這個 2147483647的十六進位制表示:0x7fffffff,是等價的
const int INF = 0x7fffffff;
const int N = 1000010;
int cnt;
struct Node {
    int left;   //左兒子
    int right;  //右兒子
    int size;   //以該節點為根結點的子樹的結點個數
    int value;  //該結點的權值
    int num;    //該結點權值出現的次數
} tree[N];
int q, opt, x;
/**
* 測試用例:
7
5 1
5 3
5 5
1 3
2 2
3 3
4 3

參考答案:
2
3
1
5
 */
//查詢數x的排名(找出以root為根的樹中,有多少個結點值小於查詢數x)
int queryVal(int root, int x) {
    //root==0表示進入到一個結點,準備再向下,結果再向下沒有了,就是:表示找不到權值為x的數,返回0
    if (root == 0) return 0;

    //如果x等於樹根的value值,那麼比它小的有tree[tree[root].left].size個,
    // 它是第tree[tree[root].left].size+1個
    if (x == tree[root].value) return tree[tree[root].left].size;

    //如果x小於樹根的value值,那麼需要到左子樹中去計算排名
    if (x < tree[root].value) return queryVal(tree[root].left, x);

    //如果x大於樹根的value值,那麼需要到右子樹中去計算排名,同時,需要加上左子樹的
    // 結點個數+root的結點個數
    return queryVal(tree[root].right, x) + tree[tree[root].left].size + tree[root].num;
}

//查詢排名為x的數
int queryRank(int root, int x) {
    //root==0表示進入到一個結點,準備再向下,結果再向下沒有了,就是:表示找不到排名為x的數,返回0
    if (root == 0) return 0;//其實這裡的有坑的,找不到題目沒有說明是返回INF,還是返回0,
    // 實測返回INF也是可以AC的。

    //如果左子樹中包含這個排名,那麼遞迴到左子樹去找
    if (tree[tree[root].left].size >= x) return queryRank(tree[root].left, x);
    //如果根結點的排名==x,那麼返回根結點的權值
    if (tree[tree[root].left].size + tree[root].num >= x) return tree[root].value;
    //如果右子樹中包含這個排名,那麼遞迴到右子樹中去找,這個子排名就是減去左子樹的總結點個數
    // 再減去root的個數
    return queryRank(tree[root].right, x - tree[tree[root].left].size - tree[root].num);
}

//向二叉搜尋樹中增加一個權值為x的數字
void insert(int root, int x) {
    //由於增加了一個結點,root的總結點個數+1
    //這一步是遞迴的必要步驟,表示以它為根的家族中增加了一個結點
    tree[root].size++;

    //如果新增加的數字x與root根的權值相等,那麼num++
    if (tree[root].value == x) {
        tree[root].num++;
    } else if (tree[root].value > x) {//如果root的權值大於x,左子樹
        //如果左子樹不空,則遞歸向左子樹插入結點x
        if (tree[root].left != 0) insert(tree[root].left, x);
        else {
            //如果左子樹為空,那麼需要建立一個新結點
            tree[++cnt].value = x; //新的陣列位置用來儲存x
            tree[cnt].size = 1;    //新增加的結點,下面沒有子樹,所以size=1
            tree[cnt].num = 1;     //權值為x的目前只有1個
            tree[root].left = cnt; //新增為左子樹
        }
    } else { //如果root的權值小於x,右子樹
        //如果右子樹不空,則遞歸向右子樹插入結點x
        if (tree[root].right != 0) insert(tree[root].right, x);
        else {
            //如果右子樹為空,那麼需要建立一個新結點
            tree[++cnt].value = x;          //新的陣列位置用來儲存x
            tree[cnt].size = 1;             //新增加的結點,下面沒有子樹,所以size=1
            tree[cnt].num = 1;              //權值為x的目前只有1個
            tree[root].right = cnt;         //新增為右子樹
        }
    }
}

/**
 * 功能:查詢前驅
 * @param root 以root為根的樹
 * @param x    要查詢的數字x
 * @param ans  暫存的前驅值,待更新成最合理的前驅值
 * @return     數字x的前驅
 */
int queryPrev(int root, int x, int ans) {
    //如果根結點值大於x,那麼需要去左子樹中去找
    if (tree[root].value >= x) {
        //如果左子樹為空的,將前面獲取到的最優解返回就可以了
        if (tree[root].left == 0) return ans;
        else
            //到左子樹中去查詢
            return queryPrev(tree[root].left, x, ans);
    } else {
        //如果根結點值小於x,那麼需要到右子樹中去找
        //如果右子樹為空,那麼就是根結點的value值
        if (tree[root].right == 0) return tree[root].value;
        //右子樹不為空,遞迴到右子樹去查詢
        return queryPrev(tree[root].right, x, tree[root].value);
    }
}

/**
 * 功能:查詢後繼
 * @param root 以root為根的樹
 * @param x    要查詢的數字x
 * @param ans  暫存的後繼值,待更新成最合理的後繼值
 * @return     數字x的後繼
 */
int queryNext(int root, int x, int ans) {
    //如果根結點的權值小於等於x,需要向右子樹去找
    if (tree[root].value <= x) {
        //如果右子樹為空,則沒有比它更大的,返回暫存的最合理值ans
        if (tree[root].right == 0) return ans;
        else
            //如果右子樹不空,則遞迴到右子樹中去找
            return queryNext(tree[root].right, x, ans);
    } else {
        //向左子樹中去找

        //如果左子樹為空,那麼就是根的value值
        if (tree[root].left == 0) return tree[root].value;
        //如果左子樹不空,遞迴到左子樹去找
        return queryNext(tree[root].left, x, tree[root].value);
    }
}


int main() {
    //q次詢問
    cin >> q;
    while (q--) {
        //操作命令和操作的值
        cin >> opt >> x;
        switch (opt) {
            case 1: //查詢x數的排名,注意這後面的+1,其實,rnk就是在根為root的子樹中,
            // 找到比x值小的結點的個數,然後再加1,就是x的排名
                cout << queryVal(1, x) + 1 << endl;
                break;
            case 2: //查詢排名為x的數
                cout << queryRank(1, x) << endl;
                break;
            case 3: //求x的前驅
                cout << queryPrev(1, x, -INF) << endl;
                break;
            case 4: //求x的後繼
                cout << queryNext(1, x, INF) << endl;
                break;
            case 5: //插入一個數 x
                if (cnt == 0) {                 //一個都沒有,那麼,第一個是根結點
                    cnt++;                      //陣列中1號位置
                    tree[cnt].num = 1;          //權值是x的數量目前是1個
                    tree[cnt].size = 1;         //它+子樹結點的總結點個數是1個
                    tree[cnt].value = x;        //權值是x
                } else insert(1, x);       //將數x插入到根為1的二叉搜尋樹中
                break;
        }
    }
    return 0;
}