1. 程式人生 > 其它 >二叉搜尋樹(BST)

二叉搜尋樹(BST)

二叉查詢樹

引入:

平衡樹的基礎就是二叉尋找樹。

首先看一下這個二叉樹:

假如說查詢第 \(n\) 大的值,那麼排序時間複雜度爆炸。

如果我們用上這棵樹,就可以在 \(O(\log n)\) 的時間內查詢,並且 \(O(\log n)\) 修改,\(O(1)\) 連邊。

實現過程:

函式定義:

struct node{
    int sizes,val,cnt; int ls,rs;
    //rank表示排名,cnt表示當前節點代表的數有幾個.
    //sizes表示當前節點的子樹大小和自己的大小的和
}t[N];

插入:

一句話總結:就是大的往右,小的往左,直到該節點沒有兒子,就新生一個兒子節點賦值。

程式碼:

void add(int x,int v){
    t[x].sizes++;
    if(t[x].val==v){t[x].cnt++; return;}
    if(t[x].val>v){
        if(t[x].ls!=0) add(t[x].ls,v);
        else bnt++,t[bnt].val=v,t[bnt].sizes=t[bnt].cnt=1,t[x].ls=bnt;
    }
    else{
        if(t[x].rs!=0) add(t[x].rs,v);
        else bnt++,t[bnt].val=v,t[bnt].sizes=t[bnt].cnt=1,t[x].rs=bnt;
    }
}

刪除:

這個圖的刪除可以用到 \(cnt\) 標記,找到這個數後 \(cnt-1\) 即可,如果 \(cnt=0\) ,就說明沒有這個數。

查詢:

支援的查詢有很多種:找前驅,找後繼,按值查排名和按排名查值

前驅後繼:

定義為小於/大於 \(x\) 的最大的數。

總結一下,就是從根節點開始,如果該節點 大於/小於 要找的數,就找其 左子樹/右子樹 ,否則找他的 右子樹/左子樹 ,直到不能找。

程式碼:

int getpre(int x,int val,int ans){
    if(t[x].val>=val){
        if(t[x].ls==0) return ans;
        else return getpre(t[x].ls,val,ans);
    }
    else
        if(t[x].rs==0){
            if(t[x].val<val) return t[x].val;
            else return ans;
        }
    if(t[x].cnt!=0) return getpre(t[x].rs,val,t[x].val);
    else return getpre(t[x].rs,val,ans);
}
int getnxt(int x,int val,int ans){
    if(t[x].val<=val){
        if(t[x].rs==0) return ans;
        else return getpre(t[x].rs,val,ans);
    }
    else
        if(t[x].ls==0){
            if(t[x].val>val) return t[x].val;
            else return ans;
        }
    if(t[x].cnt!=0) return getpre(t[x].ls,val,t[x].val);
    else return getpre(t[x].ls,val,ans);
}

按排名找值:

使用 \(sizes\) ,根據 \(BST\) 的性質,右邊的元素嚴格大於左邊的元素,所以右面的排名也大於左面的,排名為 \(n\) 的數就是第 \(n\) 靠左的節點。

我們在按排名查值時,當前位置的排名為他左子樹的大小加上自己 \(cnt\)(該節點有幾個)的大小,如果當前排名小於要找的排名,就去右子樹找,並更新要找的排名,反之先自查,不行再去左子樹找.

按值找排名:

總結一下,按值找排名時,從根開始,如果該位置的值小於要查詢的值,就找他的右子樹,同時記住他左子樹的大小,如果小於,就查詢他的左子樹,直到大小相等,他的排名就是該點左子樹的大小加上一路上比他小的節點個數再加上 \(1\).

本文主要內容借鑑ztz11 的部落格