手写一个二叉搜索树(BST)

前言

在上一篇写了一个简单的双向链表,难度是简简单单,这次来尝试二叉树,难度是也还行吧,多少有点夸张的成分了,不过对于大佬来说这些就是简简单单。

难度列表:

  • 集合:有手就行
  • 链表:简简单单
  • 队列:基础操作
  • 二叉树:也还行吧
  • 平衡二叉树:普普通通
  • 红黑树:有点难度了
  • 堆/栈:难度提升了
  • :今天是高端局

属性

二叉树是有一个根节点的,大部分操作都是基于根节点进行向下处理,所以需要显式的定义一个根节点root。

private Node root;

二叉树的每个节点都有左右节点跟它自身的属性值,所以Node内容中需要有左右节点的属性leftright,至于自身属性Demo还是用数值类型Integer

代码如下:

    private class Node{
        private Node left;
        private Node right;
        private Integer data;
        
        public Node(Integer data){
            this.data = data;
        }
        
    }

ADD

第一步:判断root是否为空,为空则初始化root节点,否则执行add方法

    private void add(int data) {
        if (Objects.isNull(root)){
            root = new Node(data);
        }else{
            root.add(data);
        }
    }

第二步:add方法,判断参数值是否小于节点值,小于挪移到左节点处理,大于挪移到右节点处理

        private void add(Integer data) {
            if (this.data >= data){
                // left
            }else{
                // right
            }
        }

第三步:判断左/右节点是否为空,为空则初始化节点,否则递归进行add方法

        private void add(Integer data) {
            if (this.data >= data){
                if (Objects.isNull(this.left)){
                    this.left = new Node(data);
                }else{
                    this.left.add(data);
                }
            }else{
                if (Objects.isNull(this.right)){
                    this.right = new Node(data);
                }else{
                    this.right.add(data);
                }
            }
        }

GET

二叉搜索树查询值有三种遍历方式:

  • 前序遍历
  • 中序遍历
  • 后续遍历

这里不每一个都将了,就选择最常用的中序遍历作为我们查询值内容的遍历方式

代码如下:

        public Node get(Integer data) {
            Node now = this;
            if (now.data == data){
                return now;
            }else{
                if (now.left != null){
                    Node left = now.left.get(data);
                    if (left != null){
                        return left;
                    }
                }
                if (now.right != null){
                    Node right = now.right.get(data);
                    if (right != null){
                        return right;
                    }
                }
            }
            return null;
        }

Delete

删除操作是二叉搜索树里最复杂的了,根据删除节点的不同需要进行调整,分类有以下几类:

  • 叶子节点:叶子节点是左右节点属性都没有的节点,这种的可以直接删除
  • 单向非叶子节点:单向非叶子节点代表左右节点只有一个节点存在,这种的直接将指向删除节点的指针指向子节点即可
  • 双向非叶子节点:如果删除节点的左右节点都存在,这种的是最复杂的,因为如果删除这种节点需要对子节点进行挪移,
    找到左节点的最右节点进行替换现在删除节点的位置,之前节点位置删除。

删除代码:

        public Node delete(Node parrent, Integer data) {
            if (this != null){
                if (this.data == data){
                    if (this.left == null && this.right == null){
                        if (parrent.left != null && parrent.left.data == data){
                            parrent.left = null;
                        }else {
                            parrent.right = null;
                        }
                    }else if(this.left != null && this.right == null){
                        parrent.left = this.left;
                    }else if(this.left == null && this.right != null){
                        parrent.right = this.right;
                    }else{
                        Node node = this.left.getRightNode(this.left);
                        node.left = this.left;
                        node.right = this.right;
                        if (parrent.left != null && parrent.left.data == data){
                            parrent.left = node;
                        }else {
                            parrent.right = node;
                        }
                    }
                }else{
                    if (data > this.data) {
                        this.right.delete(this,data);
                    } else {
                        this.left.delete(this,data);
                    }
                    return this;
                }
            }
            return null;
        }

获取最左叶子节点并删除返回

这个获取最左节点的方式我写了两种,一种是递归、一种是循环

递归方式:

        private Node getRightNode(Node node){
            if(this.right != null){
            	return this.right.getRightNode(this);
            }
            node.right = null;
            return this;
        }

循环方式:

        private Node getRightNode(Node node){
            Node now = this;
            Node buffer = this;
            while(now.right != null){
                buffer = now;
                now = now.right;
            }
            buffer.right = null;
            return now;
        }

验证

往树结构内写入部分数据并删除其中节点,查看输出内容是否对应。

代码如下:

    public static void main(String[] args) {
        OrdinaryBinaryTree tree = new OrdinaryBinaryTree();
        tree.add(4);tree.add(7);tree.add(8);tree.add(2);
        tree.add(-1);tree.add(3);tree.add(0);tree.add(11);tree.add(14);tree.add(21);tree.add(9);
        tree.print();
        tree.delete(2);
        tree.print();
    }

结果:

在这里插入图片描述

图形化结构:原始数据

删除钱

图形化结构:删除后结构

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
二叉搜索树是一种特殊的二叉树,它满足以下性质: - 左子树上所有节点的值均小于它的根节点的值。 - 右子树上所有节点的值均大于它的根节点的值。 - 左右子树也分别为二叉搜索树。 下面是一个使用 C++ 实现的简单二叉搜索树: ```cpp #include <iostream> using namespace std; struct Node { int data; Node* left; Node* right; Node(int value) { data = value; left = nullptr; right = nullptr; } }; class BinarySearchTree { private: Node* root; Node* insert(Node* node, int value) { if (node == nullptr) { node = new Node(value); } else if (value < node->data) { node->left = insert(node->left, value); } else if (value > node->data) { node->right = insert(node->right, value); } return node; } Node* search(Node* node, int value) { if (node == nullptr || node->data == value) { return node; } else if (value < node->data) { return search(node->left, value); } else { return search(node->right, value); } } Node* remove(Node* node, int value) { if (node == nullptr) { return node; } else if (value < node->data) { node->left = remove(node->left, value); } else if (value > node->data) { node->right = remove(node->right, value); } else { if (node->left == nullptr && node->right == nullptr) { delete node; node = nullptr; } else if (node->left == nullptr) { Node* temp = node; node = node->right; delete temp; } else if (node->right == nullptr) { Node* temp = node; node = node->left; delete temp; } else { Node* minNode = findMin(node->right); node->data = minNode->data; node->right = remove(node->right, minNode->data); } } return node; } Node* findMin(Node* node) { while (node->left != nullptr) { node = node->left; } return node; } public: BinarySearchTree() { root = nullptr; } void insert(int value) { root = insert(root, value); } bool search(int value) { return search(root, value) != nullptr; } void remove(int value) { root = remove(root, value); } void print() { printInorder(root); } private: void printInorder(Node* node) { if (node == nullptr) { return; } printInorder(node->left); cout << node->data << " "; printInorder(node->right); } }; int main() { BinarySearchTree bst; bst.insert(5); bst.insert(3); bst.insert(7); bst.insert(1); bst.insert(4); bst.insert(6); bst.insert(8); bst.print(); // 输出:1 3 4 5 6 7 8 bst.remove(3); bst.print(); // 输出:1 4 5 6 7 8 return 0; } ``` 在这个实现中,我们使用了一个 `Node` 结构体来表示二叉搜索树中的节点,每个节点包含一个 `data` 字段表示节点的值,以及 `left` 和 `right` 指针分别指向左右子节点。 我们也定义了一个 `BinarySearchTree` 类来管理二叉搜索树。在这个类中,我们定义了一些私有方法来实现插入、查找和删除节点的功能,同时也定义了一些公有方法供外部使用。 在 `insert` 方法中,我们首先判断当前节点是否为空,如果是的话,我们就创建一个新节点并将它插入到这个位置。如果不为空,我们就根据节点的值与当前节点的值的大小关系,递归地向左或向右寻找插入的位置。 在 `search` 方法中,我们首先判断当前节点是否为空或者是否等于要查找的值。如果是的话,我们就返回这个节点。否则,我们就根据节点的值与要查找的值的大小关系,递归地向左或向右查找。 在 `remove` 方法中,我们首先判断当前节点是否为空,如果是的话,我们就直接返回这个节点。如果不为空,我们就根据节点的值与要删除的值的大小关系,递归地向左或向右查找要删除的节点。如果找到了要删除的节点,我们就需要考虑以下三种情况: - 要删除的节点没有子节点。在这种情况下,我们直接删除这个节点并将它的父节点指向空指针。 - 要删除的节点只有一个子节点。在这种情况下,我们将要删除的节点的子节点替换到要删除的节点的位置上。 - 要删除的节点有两个子节点。在这种情况下,我们需要找到要删除节点右子树中的最小值节点,将它的值复制到要删除的节点中,并递归地删除这个最小值节点。 最后,我们定义了一个 `print` 方法来按照中序遍历的顺序输出二叉搜索树中所有节点的值。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

余生大大

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值