二叉查找树(c++)

下图无比现实

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

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

为什么用数组呢?

因为写的舒服,一个字

struct node{
  int l, r, val; //左孩子,右孩子,权值    
}tree[SIZE];
int tot; //记录共有几个节点
int root; //记录根节点

不过它有一个性质:对于任意一个非叶子节点,它的左子树所有的节点的权值都<=他的权值,它的右子树所有的节点的权值 >= 它的权值(等于号根据题目各异)。

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

• 树的建立

·检索

• 插入关键码为x的节点

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

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

• 删除关键码为x的节点

1,BST的建立

通常会插入两个虚拟节点INF和-INF,防止越界。

//存放一个权值为v的节点
int make_new(int v) {
    tree[++tot].val = v;
    return tot; //返回存放的节点编号
}
//建树,插入-INF,INF两个节点
void build() {
    root = make_new(-INF);
    tree[root].r = make_new(INF);  //INF > -INF 所以INF应是-INF的右孩子
} 

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的右子树中递归进行检索

void find(int p, int v) {
    if (p == 0) {
        return 0; // 没有找到 
    }
    if (tree[p].val == v) return p; //找到了 
    else if (tree[p].val > v) find(tree[p].l, v); // p的权值 > v 在左子树中找 
    else find(tree[p].r, v); // p的权值 < v 在右子树中找 
}

3,BST的插入

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

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

void insert(int &p, int v) {
    if (p == 0) {
        //插入
        p = make_new(v); // p是引用,其父节点的l或r值会随着改变 
        return 0;
    }
    if (tree[p].val == v) return p; //找到了 
    else if (tree[p].val > v) find(tree[p].l, v); // p的权值 > v 在左子树中插 
    else find(tree[p].r, v); // p的权值 < v 在右子树中插 
}

特别提醒:这个递归不能用while循环替代,否则root的值会改变

4,BST的后继

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

设v的节点编号为p。

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

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

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

int get_next(int v) {
    int ret = 2;//tree[2].val == INF
    //检索 
    int p = root;
    while (p) {
        if (v == tree[p].val) {
             //找到了 
            if (tree[p].r) {
                //有右子树 
                p = tree[p].r;
                while (tree[p].l) p = tree[p].l; //一直往左找 
                ret = p;
            }
            break;
        }
        if (tree[p].val > v && tree[p].val < tree[ret].val) ret = p; //检索时记录 
        p = tree[p].val > v ? tree[p].l : tree[p].r;
    }
    //这里不是引用所以可以用while循环 
    return tree[ret].val;
}

温馨提示:没有后继(最大值会返回INF)

5,BST找前驱

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

int get_pre(int v) {
    int ret = 1;//tree[1].val == -INF
    int p = root;
    while (p) {
        if (v == tree[p].val) {
            if (tree[p].l) {
                p = tree[p].l;
                while (tree[p].r) p = tree[p].r; //一直网友找 
                ret = p;
            }
            break;
        }
        if (tree[p].val < v && tree[p].val > tree[ret].val) ret = p; //小于且最大 
        p = tree[p].val > v ? tree[p].l : tree[p].r;
    }
    return tree[ret].val;
    //若没有前驱返回-INF 
}

6,BST中删除节点

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

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

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

因为neat 没有左子树,所以可以直接删除next,并令next的右子树代替next的位置。

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

void remove(int &p, int v) {
    if (p == 0) { // not exit
        return;
    }
    if (tree[p].val == v && tree[tree[p].r].val != v) { // found it
        // remove
        if (!tree[p].l) {
            p = tree[p].r;
        } else if (!tree[p].r) {
            p = tree[p].l;
        } else {
            int next = tree[p].r;
            while (tree[next].l) next = tree[next].l;
            remove(root, tree[next].val);
            tree[next].l = tree[p].l, tree[next].r = tree[p].r;
            p = next;
        }
        return;
    }
    if (tree[p].val <= v) {
        remove(tree[p].r, v);
    } else {
        remove(tree[p].l, v);
    }
}

六项基本操作就讲完了,贴一份完整代码,有建议大家可以提出来我再改进。

#include <bits/stdc++.h>
using namespace std;
const int SIZE = 100000;
struct node{
    int l, r, val;
}tree[SIZE];
int tot, root;
int make_new(int v) {
    tree[++tot].val = v;
    return tot;
}
void build() {
    root = make_new(-INF);
    tree[root].r = make_new(INF); 
}
void find(int p, int v) {
    if (p == 0) {
        return 0;
    }
    if (tree[p].val == v) return p;
    else if (tree[p].val > v) find(tree[p].l, v);
    else find(tree[p].r, v);
}
void insert(int &p, int v) {
    if (p == 0) {
        p = make_new(v);
        return 0;
    }
    if (tree[p].val == v) return p;
    else if (tree[p].val > v) find(tree[p].l, v);
    else find(tree[p].r, v); 
}
int get_next(int v) {
    int ret = 2;
    int p = root;
    while (p) {
        if (v == tree[p].val) {
            if (tree[p].r) {
                p = tree[p].r;
                while (tree[p].l) p = tree[p].l;
                ret = p;
            }
            break;
        }
        if (tree[p].val > v && tree[p].val < tree[ret].val) ret = p;
        p = tree[p].val > v ? tree[p].l : tree[p].r;
    }
    return tree[ret].val;
}
int get_pre(int v) {
    int ret = 1;
    int p = root;
    while (p) {
        if (v == tree[p].val) {
            if (tree[p].l) {
                p = tree[p].l;
                while (tree[p].r) p = tree[p].r;
                ret = p;
            }
            break;
        }
        if (tree[p].val < v && tree[p].val > tree[ret].val) ret = p; 
        p = tree[p].val > v ? tree[p].l : tree[p].r;
    }
    return tree[ret].val;
}
void remove(int &p, int v) {
    if (p == 0) {
        return;
    }
    if (tree[p].val == v && tree[tree[p].r].val != v) {
        if (!tree[p].l) {
            p = tree[p].r;
        } else if (!tree[p].r) {
            p = tree[p].l;
        } else {
            int next = tree[p].r;
            while (tree[next].l) next = tree[next].l;
            remove(root, tree[next].val);
            tree[next].l = tree[p].l, tree[next].r = tree[p].r;
            p = next;
        }
        return;
    }
    if (tree[p].val <= v) {
        remove(tree[p].r, v);
    } else {
        remove(tree[p].l, v);
    }
}
int read() {
    int ret = 0, f = 1;
    char ch = getchar();
    while ('0' > ch || ch > '9') {
        if (ch == '-') f = -1;
        ch = getchar();
    }
    while ('0' <= ch && ch <= '9') {
        ret = ret * 10 + ch - '0';
        ch = getchar();
    }
    return ret * f;
}
void write(int x) {
    if (x >= 10) write(x / 10);
    putchar(x % 10 + '0');
}
int main() {
    build();
    int n;
    n = read();
    while (n--) {
        int opt, x;
        opt = read();
        x = read();
        switch (opt) {
            case 1:
                insert(root, x);
                break;
            case 2:
                remove(root, x);
                break;
            case 3:
                write(get_pre(x));
                putchar('\n');
                break;
            case 4:
                write(get_next(x));
                putchar('\n');
                break;
        }
    }
}

转载于:https://www.cnblogs.com/zcr-blog/p/11324467.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值