• 二叉查找树(c++)——实用的数据结构


    下图无比现实

    二叉查找树又名二叉排序树、二叉搜索树、BST(Binary Search Tree)。首先知道它的基础数据结构。

    它是一颗二叉树(带权),通常会用结构体实现(数组或者指针——个人比较喜欢用数组,之后的代码也是用数组来实现的)。

    struct node{
      int l, r, val;//左孩子,右孩子,权值
      int sz, cnt;//子树大小,出现几次 (update:用来求kth,val的排名)   
    }tree[SIZE];
    int tot; //记录共有几个节点
    int root; //记录根节点

    不过它有一个性质:对于任意一个非叶子节点,它的左子树所有的节点的权值都<他的权值,它的右子树所有的节点的权值>它的权值。

    其次它可以实现以下功能:

    • 建立关键码为x的节点

    • 检索

    • 插入关键码为x的节点

    • 求关键码为x的节点的前驱

    • 求关键码为x的节点的后继

    • 删除关键码为x的节点


    update:求排名和排名的关键码

    • 求关键码为x的节点的排名

    • 求排名为x的节点的关键码


    1,建立关键码

    这个没什么好讲的吧。

    //插入一个权值为v的新节点 
    int New(int v) {
        BST[++tot].val = v;
        BST[tot].cnt = BST[tot].sz = 1;
    }

    2,BST的检索

    在BST中检索是否存在权值为v的节点。

    设变量p等于根节点root,执行以下过程:

    1.若p的权值等于v,则已经找到

    2.若p的权值大于v

    a.若p的左子节点为空,则说明不存在v

    b.若p的左子节点不空,在p的左子树中递归进行检索

    3.若p的权值小于v

    a.若p的右子节点为空,则说明不存在v

    b.若p的右子节点不空,在p的右子树中递归进行检索

    //检索关键码为v的编号 
    int Find(int p, int v) {
        if (p == 0) return -1;//没有此数
        if (v == BST[p].val) return v;//找到了 
        if (v > BST[p].val) return Find(BST[p].ron, v);//比他大,在他的右子树里找 
        else return Find(BST[p].lson, v);//比他小,在他的左子树里找 
    }

    3,BST的插入

    插入其实跟检索差不多,只不过找的合适的位置后要把元素插入进去。

    用一个引用记录其父节点的lson或rson值,这样就可以轻松插入了。

    //更新节点信息
    void Up(int p) {
        BST[p].sz = BST[BST[P].lson].sz + BST[BST[p].rson].sz + 1;
        //p的子树大小等于p的左儿子的子树大小加p的右儿子的子树大小再加1 
    }
    //插入一个关键码为v的数 
    void Insert(int &p, int v) {
        if (p == 0) {
            //之前没有v这个数 
            //需要新建一个节点
            p = New(v);//p是引用,其父节点的lson或rson值会随着改变 
            return; 
        }
        if (v == BST[p].val) BST[P].cnt++;//之前已经有v这个数了,直接次数+1
        if (v > BST[p].val) Insert(BST[p].ron, v);//比他大,在他的右子树里插 
        else Insert(BST[p].lson, v);//比他小,在他的左子树里插 
        Up(p);//需要更新p的信息(这里是子树大小) 
    }

    Up和线段树类似,因为改变了p的儿子的信息所以p的信息肯定也要更改。

    4,BST的后继

    首先检索v,然后开始分类讨论:

    设v的节点编号为p。

    I p有右子树,则它的后继一定在它的右子树中否则它的右子树将不是“它的右子树”。

    而且一定是它右孩子后一直往左孩子找。

    II 若p没有右子树,则它的后继就是它的直系祖先中 > 它且最小的一个(在检索的时候记录)

    //找关键码为v的后继 
    int Next(int v) {
        int ret = -1;
        //检索 
        int p = root;
        while (p) {
            if (v == BST[p].val) {
                //找到了
                if (BST[p].rson) {
                    //有右子树
                    p = BST[p].rson;
                    while (BST[p].lson) p = BST[p].lson;//一直往左走 
                    ret = p;
                }
                break;
            }
            if (BST[p].val > v && (ret == -1 || BST[p].val < BST[ret].val)) ret = p, p = BST[p].lson;//检索时记录 
            else p = BST[p].rson;
        }
        
        return ret;
        //返回后缀的节点编号,若没有返回-1(如在2,3,4中找5的后缀) 
    }

    5,BST找前驱

    更后继差不多,不过是左子树一直往右走,纪录时也是<它最大的一个。

    //找关键码为v的前驱 
    int Pre(int v) {
        int ret = -1;
        //检索 
        int p = root;
        while (p) {
            if (v == BST[p].val) {
                //找到了
                if (BST[p].lson) {
                    //有左子树
                    p = BST[p].lson;
                    while (BST[p].rson) p = BST[p].rson;//一直往右走 
                    ret = p; 
                }
                break;
            }
            if (BST[p].val < v && (ret == -1 || BST[p].val > BST[ret].val)) ret = p, p = BST[p].rson;//检索时记录 
            else p = BST[p].lson;
        }
        return ret;
        //返回前驱的节点编号,若没有返回-1(如在2,3,4中找1的前驱) 
    }

    6,BST中删除节点

    在往下看之前,读者可以先结合上述知识思考一下如何删除(提示:和前驱后继有关)。

    首先也是先检索它得到节点p

    若v出现的大于1,那么直接cnt--即可。

    若p的子节点个数小于2,则直接删除p,并令p的子节点代替p的位置,与p 的父节点相连。

    若p既有左子树又有右子树,则在BST中求出v的后继节点next。

    因为next没有左子树(想想为什么?),所以可以直接删除next,并令next的右子树代替next的位置。

    最后, 再让next节点代替p节点,删除p即可。

    void Del(int &p, int v) {
        if (p == 0) return;//没有v这个数
        if (v == BST[p].val) {
            //找到了
            if (BST[p].cnt > 1) BST[p].cnt--;//出现次数大于1,直接减1 
            else {
                //出现次数等于1,现在要删掉次数,此节点没有存在的必要了 
                if (!BST[p].lson) {
                    p = BST[p].rson;//没有左儿子,此处依然是引用,p变成它的右儿子即可 
                } else if (!BST[p].rson) {
                    p = BST[p].lson;//没有右儿子,p变成它的左儿子即可 
                } else {
                    //p有两个儿子
                    int nxt = Next(v);//求出v的后继
                    Del(root, BST[nxt].val);//删除nxt 
                    //用nxt代替p 
                    BST[nxt].lson = BST[p].lson, BST[nxt].rson = BST[p].rson;
                    p = nxt;
                    
                }
            }
            Up(p);
            return; 
        }
        if (BST[p].val < v) Del(BST[p].rson, v);
        else Del(BST[p].lson, v);
        Up(p);
    }

    update:

    7,BST查询排名

    我们用循环的方式来求

    对于当前节点p开始分类讨论

    (1)valp=v,ret+=p左子树大小并退出循环

    (2)valp<v,则valp及其左子树均排在v前面,ret+=cntp+p左子树的大小,跳p

    (3)valp>v,此时ret并不能增加,直接跳p

    //查询v的排名 
    int Rank(int v) {
        int ret = 0;
        int p = root;
        while (p) {
            if (v == BST[p].val) {
                ret += BST[BST[p].lson].sz;
                break;
            }
            if (v > BST[p].val) {
                ret += BST[p].cnt + BST[BST[p].lson].sz;
                p = BST[p].rson;
            } else {
                p = BST[p].lson;
            }
        }
        return ret + 1;
    }

    8,BST根据排名查询数

    有点类似主席树,时间不早了,这就不讲了

    //查询排名为k的数
    int Kth(int k) {
        int ret = -1;
        int p = root;
        while (p) {
            if (k <= BST[BST[p].lson].sz) p = BST[p].lson;
            else {
                k -= BST[BST[p].lson].sz;
                if (k <= BST[p].cnt) {
                    ret = p;
                    break;
                }
                k -= BST[p].cnt;
                p = BST[p].rson;
            }
        }
        return ret;
    } 

    update1:3-8

    讲真BST没什么n用,但它是学习平衡树的基础。

  • 相关阅读:
    深入揭秘HTTPS安全问题&连接建立全过程
    申请https证书需要注意的4大问题
    如何排查APP服务端和客户端是否支持ATS
    Apache和Nginx配置支持苹果ATS方法
    服务器配置ssl证书支持苹果ATS方法
    HTTPS背后的加密算法
    图解HTTPS协议加密解密全过程
    Java单例模式——并非看起来那么简单
    flask+mako+peewee(上)
    [转]ubuntu中查找软件的安装位置
  • 原文地址:https://www.cnblogs.com/zcr-blog/p/11324467.html
Copyright © 2020-2023  润新知