数据结构-二分搜索树(Binary Search Tree)

一,简单了解二分搜索树

树结构:

问题:为什么要创造这种数据结构

1,树结构本身是一种天然的组织结构,就好像我们的文件夹一样,一层一层的.


2,树结构可以更高效的处理问题

二,二分搜索树的基础

1、二叉树
 

2,二叉树的重要特性

满二叉树

总结:

1. 叶子结点出现在二叉树的最底层,除叶子结点之外的其它结点都有两个孩子结点。
2. 一个层数为k 的满二叉树总结点数为:
3. 第i层上的结点数为:
4. 一个层数为k的满二叉树的叶子结点个数(也就是最后一层):
4、二叉树不一定是“满”的

3,二分搜索树

(注意:存储的元素必须具有可比性)

1,向二分搜索树添加元素

2,向二分搜索树查询操作

1,递归终止的条件 : if(node == null ) return false;
2,递归操作
if (ele.compareTo(node.ele) < 0) {
return search(node.left, ele);
} else if (ele.compareTo(node.ele) > 0) {
return search(node.right, ele);
} else {
return true;
}

3,二分搜索树的遍历操作

遍历操作:把树中所有节点都访问一遍

1前序遍历,

2中序遍历,

3后序遍历

4层序遍历(广度优先)

(代码,会在后面一起展现.)

4,二分搜索树寻找最大值,最小值

同样的原理找出二分搜素树中最大的元素,这里不在过多赘述.

5,删除操作
情况一:(叶子结点)

情况二、(非叶子结点)

删除后


6,删除二分搜索树中的节点

情况一:
 

情况二、
 

情况三:左右孩子都不为空的情况

使用Hibbard Deletion
 

三,用代码实现二分搜索树.实现相关功能.

(由于功能实现较多,代码较长)

其中包含是前面的各种功能操作的实现,包括,前,中,后,层,序把遍历,删除,添加,等等操作.需要的同学可以仔细查看.

mport java.nio.channels.Pipe;
import java.util.*;
import java.util.stream.Collectors;

// 二分搜索树
public class BinarySearchTree<T extends Comparable<T>> {
    // 定义树的结点
    public class Node {
        T val;
        Node left; //  左孩子
        Node right; // 右孩子

        public Node(T val) {
            this.val = val;
        }
    }

    // 定义树的根
    private Node root;// 树根

    // 统计树中结点的个数
    private int size;// 树中结点的个数

    public BinarySearchTree() {
        this.root = null;
        this.size = 0;
    }

    // 判断树是否为空
    public boolean isEmpty() {
        return this.size == 0;
    }

    // 获取树中元素的个数
    public int getSize() {
        return this.size;
    }

    // 向树中添加元素
    public void add(T val) {
        this.size++;
        this.root = add(this.root, val);
    }

    /**
     * 添加的递归方法
     *
     * @param node 树的根结点
     * @param val  添加的值
     */
    private Node add(Node node, T val) {
        //递归终止的条件
        if (node == null) {
            Node leafNode = new Node(val);
            return leafNode;
        }
        // 递归操作
        if (node.val.compareTo(val) > 0) {
            node.left = add(node.left, val);
        } else {
            node.right = add(node.right, val);
        }
        return node;
    }

    // 将树中所有结点进行遍历--中序遍历( 深度优先搜索 DFS,使用的栈来实现)
    public String middleTravel() {
        List<T> result = new ArrayList<>();
        middleTravel(this.root, result);
        return result.stream().map(item -> item.toString()).collect(Collectors.joining(","));
    }

    /**
     * 中序遍历
     *
     * @param node 树的根结点
     */
    private void middleTravel(Node node, List<T> result) {
        // 递归终止的条件
        if (node == null) {
            return;
        }
        // 递归操作
        // 先遍历左子树
        middleTravel(node.left, result);
        //  打印当前值
        result.add(node.val);
        //  再遍历右子树
        middleTravel(node.right, result);
    }

    public String beforeTravel() {
        List<T> result = new ArrayList<>();
        beforeTravel(this.root, result);
        return result.stream().map(item -> item.toString()).collect(Collectors.joining(","));
    }

    /**
     * 前序遍历
     *
     * @param node 树的根结点
     */
    private void beforeTravel(Node node, List<T> result) {
        // 递归终止的条件
        if (node == null) {
            return;
        }
        // 递归操作
        //  打印当前值
        result.add(node.val);
        // 先遍历左子树
        beforeTravel(node.left, result);
        //  再遍历右子树
        beforeTravel(node.right, result);
    }


    public String afterTravel() {
        List<T> result = new ArrayList<>();
        afterTravel(this.root, result);
        return result.stream().map(item -> item.toString()).collect(Collectors.joining(","));
    }

    /**
     * 后序遍历
     *
     * @param node 树的根结点
     */
    private void afterTravel(Node node, List<T> result) {
        // 递归终止的条件
        if (node == null) {
            return;
        }
        // 递归操作
        // 先遍历左子树
        afterTravel(node.left, result);
        //  再遍历右子树
        afterTravel(node.right, result);
        //  打印当前值
        result.add(node.val);
    }

    // 查找的方法
    public boolean contains(T val) {
        return contains(this.root, val);
    }

    /**
     * 从以node为根的二分搜索树中查找元素val
     *
     * @param node 根节点
     * @param val  要搜索的值
     * @return
     */
    private boolean contains(Node node, T val) {
        // 递归到底的情况
        if (node == null) {
            return false;
        }
        // 递归操作
        if (node.val.compareTo(val) == 0) {
            return true;
        } else if (node.val.compareTo(val) > 0) {
            return contains(node.left, val);
        } else {
            return contains(node.right, val);
        }
    }

    // 树的层序遍历(广度优先搜索  BFS,使用队列来实现)
    public String levelTravel() {
        List<String> list = new ArrayList<>();
        // 1、 创建一个队列
        Queue<AbstractMap.SimpleEntry<Integer, Node>> queue = new LinkedList<>();
        // 2、将根结点入入队
        if (this.root != null) {
            queue.offer(new AbstractMap.SimpleEntry<>(1, this.root));
        }
        // 3、遍历队列
        while (!queue.isEmpty()) {
            AbstractMap.SimpleEntry<Integer, Node> temp = queue.poll();
            Node value = temp.getValue();
            int key = temp.getKey();
            //3-1  先将内容保存
            list.add(value.val.toString() + "------" + key);
            // 3-2  判断左子树是否为空,不为空就入队
            if (value.left != null) {
                queue.offer(new AbstractMap.SimpleEntry<>(key + 1, value.left));
            }
            // 3-3 判断右子树是否为空,不为空就入队
            if (value.right != null) {
                queue.offer(new AbstractMap.SimpleEntry<>(key + 1, value.right));
            }
        }

        return list.stream().collect(Collectors.joining(","));
    }

    public List<List<T>> levelOrder() {
        // 返回值类型是啥,就创建啥
        List<List<T>> result = new ArrayList<>();
        // 1、 创建一个队列
        Queue<AbstractMap.SimpleEntry<Integer, Node>> queue = new LinkedList<>();
        // 2、将根结点入入队
        if (this.root != null) {
            queue.offer(new AbstractMap.SimpleEntry<>(1, this.root));
        }
        while (!queue.isEmpty()) {
            AbstractMap.SimpleEntry<Integer, Node> temp = queue.poll();
            Node value = temp.getValue();
            int key = temp.getKey();
            //3-1  先将内容保存
            if(result.get(key-1)==null){
                result.add(new ArrayList<>());
            }
            result.get(key-1).add(value.val);

            // 3-2  判断左子树是否为空,不为空就入队
            if (value.left != null) {
                queue.offer(new AbstractMap.SimpleEntry<>(key + 1, value.left));
            }
            // 3-3 判断右子树是否为空,不为空就入队
            if (value.right != null) {
                queue.offer(new AbstractMap.SimpleEntry<>(key + 1, value.right));
            }
        }
        return result;
    }

    // Pair对
    public class Pair<Node> {
        private Node value; // 保存值
        private int key; // 保存层

        public Pair(Node value, int key) {
            this.value = value;
            this.key = key;
        }

        public Node getValue() {
            return value;
        }

        public int getKey() {
            return key;
        }
    }

    // 从二分搜索树中找最小值(在整棵树的最左边)
    public T getMinVal() {
        // 判断树是否为空
        if (this.root == null) {
            return null;
        }
        Node curNode = this.root;
        while (curNode.left != null) {
            curNode = curNode.left;
        }
        return curNode.val;
    }

    public T getMinValDG() {
        // 判断树是否为空
        if (this.root == null) {
            return null;
        }
        return getMinValDG(this.root).val;
    }

    /**
     * 从以node为根的二分搜索树中查找最小值
     *
     * @param node 树的根节点
     */
    private Node getMinValDG(Node node) {
        //递归终止的条件
        if (node.left == null) {
            return node;
        }
        // 递归操作
        return getMinValDG(node.left);
    }


    // 从二分搜索树中找最 大值(在整棵树的最右边)
    public T getMaxVal() {
        // 判断树是否为空
        if (this.root == null) {
            return null;
        }
        Node curNode = this.root;
        while (curNode.right != null) {
            curNode = curNode.right;
        }
        return curNode.val;
    }

    public T getMaxValDG() {
        // 判断树是否为空
        if (this.root == null) {
            return null;
        }
        return getMaxValDG(this.root).val;
    }

    private Node getMaxValDG(Node node) {
        //递归终止的条件
        if (node.right == null) {
            return node;
        }
        // 递归操作
        return getMinValDG(node.right);
    }


    // 从以this.root为根的二分搜索树中删除最小的结点
    public void removeMinNode() {
        if (this.root == null) {
            return;
        }
        this.root = removeMinNode(this.root);
    }

    /**
     * 从以node为根的二分搜索树中删除最小的节点
     *
     * @param node 树的根节点
     * @return 删除之后的树的根节点
     */
    private Node removeMinNode(Node node) {
        // 递归终止的条件
        if (node.left == null) {
            this.size--;
            return node.right;
        }
        // 递归操作
        node.left = removeMinNode(node.left);
        return node;
    }


    // 删除操作
    public void remove(T val) {
        if (!contains(val)) {
            return;
        }
        this.root = remove(this.root, val);
    }

    /**
     * 从以node为根的二分搜索树中删除元素val的节点
     *
     * @param node 树的根节点
     * @param val  删除的值
     * @return
     */
    private Node remove(Node node, T val) {
        // 递归终止的条件
        if (node == null) {
            return null;
        }
        if (node.val.compareTo(val) == 0) {
            // 更新size
            this.size--;
            if (node.right == null) {
                //1、右子树为空
                return node.left;
            } else if (node.left == null) {
                //2、左子树为空
                return node.right;
            } else {
                // 3、左右子树都不为空
                // 3-1 找到删除节点的后继
                Node suffixNode = getMinValDG(node.right);
                // 3-2 更新suffixNode的左右子树
//                suffixNode.right = removeMinNode(node.right);
                suffixNode.right = remove(node.right, getMinValDG(node.right).val);
                suffixNode.left = node.left;
                this.size++;
                // 3-3 返回suffixNode
                return suffixNode;
            }
        }
        // 递归操作
        if (node.val.compareTo(val) > 0) {
            node.left = remove(node.left, val);
        } else {
            node.right = remove(node.right, val);
        }
        return node;
    }}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/407727.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

数字热潮:iGaming 能否推动加密货币的普及?

过去十年&#xff0c;iGaming&#xff08;互联网游戏&#xff09;世界有了显著增长&#xff0c;每月有超过一百万的新用户加入。那么&#xff0c;这一主流的秘密是什么&#xff1f;让我们在本文中探讨一下。 领先一步&#xff1a;市场 数字时代正在重新定义娱乐&#xff0c;iG…

LeetCode 2476.二叉搜索树最近节点查询:中序遍历 + 二分查找

【LetMeFly】2476.二叉搜索树最近节点查询&#xff1a;中序遍历 二分查找 力扣题目链接&#xff1a;https://leetcode.cn/problems/closest-nodes-queries-in-a-binary-search-tree/ 给你一个 二叉搜索树 的根节点 root &#xff0c;和一个由正整数组成、长度为 n 的数组 qu…

Java零基础 - 算术运算符

哈喽&#xff0c;各位小伙伴们&#xff0c;你们好呀&#xff0c;我是喵手。 今天我要给大家分享一些自己日常学习到的一些知识点&#xff0c;并以文字的形式跟大家一起交流&#xff0c;互相学习&#xff0c;一个人虽可以走的更快&#xff0c;但一群人可以走的更远。 我是一名后…

基于Java+SSM+Jsp宿舍管理系统(源码+演示视频+包运行成功+Maven版)

您好&#xff0c;我是码农小波&#xff08;wei158888&#xff09;&#xff0c;感谢您阅读本文&#xff0c;欢迎一键三连哦。 ❤️ 1. 毕业设计专栏&#xff0c;毕业季咱们不慌&#xff0c;上千款毕业设计等你来选。 目录 1、项目背景 2、项目演示 3、使用技术 4、系统设计 …

Linux--shell编程中分区表常用操作 全面且详细

文章中关于分区表常用操作目录&#xff1a; 一、概念 二、​​​​​​​创建分区表语法 ​​​​​​​三、创建一个表带多个分区 四、​​​​​​​加载数据到分区表中 五、加载数据到一个多分区的表中去 ​​​​​​​六、查看分区 七、​​​​​​​添加一个分区…

Vue局部注册组件实现组件化登录注册

Vue局部注册组件实现组件化登录注册 一、效果二、代码1、index.js2、App.vue3、首页4、登录&#xff08;注册同理&#xff09; 一、效果 注意我这里使用了element组件 二、代码 1、index.js import Vue from vue import VueRouter from vue-router import Login from ../vie…

vue源码分析之nextTick源码分析-逐行逐析-个人困惑

nextTick的使用背景 在vue项目中&#xff0c;经常会使用到nextTick这个api&#xff0c;一直在猜想其是怎么实现的&#xff0c;今天有幸研读了下&#xff0c;虽然源码又些许问题&#xff0c;但仍值得借鉴 核心源码解析 判断当前环境使用最合适的API并保存函数 promise 判断…

云原生超融合八大核心能力|ZStack Edge云原生超融合产品技术解读

企业数字化转型的焦点正在发生变化&#xff0c;云基础设施由资源到应用&#xff0c;数据中心从核心到边缘。面向云原生趋势&#xff0c;围绕应用升级&#xff0c;新一代超融合产品——云原生超融合应运而生。 ZStackEdge云原生超融合是基于云原生架构研发的新一代IT基础设施 …

EI论文联合复现:含分布式发电的微网/综合能源系统储能容量多时间尺度线性配置方法程序代码!

适用平台&#xff1a;Matlab/Gurobi 程序提出了基于线性规划方法的多时间尺度储能容量配置方法&#xff0c;以满足微电网的接入要求为前提&#xff0c;以最小储能配置容量为目标&#xff0c;对混合储能装置进行容量配置。程序较为基础&#xff0c;算例丰富、注释清晰、干货满满…

【设计模式】策略模式及函数式编程的替代

本文介绍策略模式以及使用函数式编程替代简单的策略模式。 策略模式 在策略模式&#xff08;Strategy Pattern&#xff09;中一个类的行为或其算法可以在运行时更改。这种类型的设计模式属于行为型模式。 在策略模式定义了一系列算法或策略&#xff0c;并将每个算法封装在独立…

PyPDF2:项目实战源码分享(PDF裁剪)

目录&#x1f4d1; 1. 背景&#x1f4d1;2. 源码模块解析&#x1f4d1;2.1 读取PDF页数2.2 获取指定页的宽高尺寸2.3 裁剪单页PDF2.4 批量裁剪PDF 总结&#x1f4d1; 1. 背景&#x1f4d1; 接PyPDF2模块推荐博文中提到的实际需求&#xff08;将银行网站下载来的多页且单页多张…

【大数据】Flink 内存管理(二):JobManager 内存分配(含实际计算案例)

Flink 内存管理&#xff08;二&#xff09;&#xff1a;JobManager 内存分配 1.分配 Total Process Size2.分配 Total Flink Size3.单独分配 Heap Size4.分配 Total Process Size 和 Heap Size5.分配 Total Flink Size 和 Heap Size JobManager 是 Flink 集群的控制元素。它由三…

亿道丨三防平板丨加固平板丨为零售业提供四大优势

随着全球经济的快速发展&#xff0c;作为传统行业的零售业也迎来了绝佳的发展机遇&#xff0c;在互联网智能化的大环境下&#xff0c;越来越多的零售企业选择三防平板电脑作为工作中的电子设备。作为一种耐用的移动选项&#xff0c;三防平板带来的不仅仅是坚固的外壳。坚固耐用…

【Python笔记-设计模式】前端控制器模式

一、说明 常作为MVC&#xff08;Model-View-Controller&#xff09;模式的一部分&#xff0c;用来处理用户请求并将其分发给相应的处理程序&#xff08;即路由匹配&#xff09;。 (一) 解决问题 将请求的处理流程集中管理&#xff0c;统一处理所有的请求 (二) 使用场景 需…

向量数据库的特性、索引和分析权衡

向量数据库概述 向量数据库的特征 数据库多样性&#xff1a;向量数据库在实现、性能、可扩展性和易用性方面存在差异&#xff0c;支持语义搜索应用。融资与地理位置&#xff1a;多数向量数据库初创公司集中在加州湾区&#xff0c;但资金并不直接反映数据库能力。编程语言&…

【前端素材】推荐优质后台管理系统Dashmin平台模板(附源码)

一、需求分析 后台管理系统在多个层次上提供了丰富的功能和细致的管理手段&#xff0c;帮助管理员轻松管理和控制系统的各个方面。其灵活性和可扩展性使得后台管理系统成为各种网站、应用程序和系统不可或缺的管理工具。 后台管理系统是一种具有多层次结构的软件系统&#xf…

【DDD】学习笔记-领域模型与数据模型

领域模型与数据模型 领域驱动的设计模型最重要的概念就是聚合&#xff0c;同时&#xff0c;聚合还要受到限界上下文边界的控制。Eric Evans 之所以要引入限界上下文&#xff0c;其中一个重要原因就是因为我们“无法维护一个涵盖整个企业的统一模型”&#xff0c;于是需要限界上…

我花了5天时间,开发了一个在线学习的小网站

大三寒假赋闲在家&#xff0c;闲来无事&#xff0c;用了5天时间做了一个在线学习的小网站&#xff0c;一鼓作气部署上线&#xff0c;制作的过程比较坎坷。内心经历过奔溃&#xff0c;也经历过狂喜。 按照惯例先放出网址&#xff0c;欢迎大家来访问学习&#xff1a;www.pbjlove…

滑动窗口刷题(二)

目录 1.最大连续1的个数 III 1.题目解析 2.算法原理 2.1暴力枚举&#xff08;不过多介绍&#xff09; 2.2双指针优化 3.代码编写 2. 将 x 减到 0 的最小操作数 1.题目解析 2.算法原理 2.1滑动窗口 3.代码编写 3. 水果成篮 1.题目解析 2.算法思路 2.1滑动窗口哈希…

关于电脑功耗与电费消耗的问题,你了解多少?

一台电脑24小时运行需要多少电量&#xff1f; 大家好&#xff0c;我是一名拥有多年维修经验的上门维修师傅。 今天我就来回答大家关于电脑24小时运行需要多少电量的问题。 电脑功耗及用电量 首先我们来看看电脑的功耗情况。 普通台式电脑的功耗通常在300瓦左右&#xff0c;即…