二分搜索树

导读:本篇文章讲解 二分搜索树,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com

二分搜索树

树结构

树的概念

​ 树是一种重要的**非线性**数据结构,直观地看,它是数据元素(在树中称为结点)按分支关系组织起来的结构,很象自然界中的树那样。

树的结点

树的每个元素都是一个节点,树有根节点,孩子结点,叶子节点,一棵树只能有一个根节点(对于非空树来说)

在这里插入图片描述

树按照最多有n个孩子节点就称为n叉树,如上图就是二叉树,只有两个孩子节点

二叉树

二叉树具有天然的递归结构

  • 每个结点的左子树是也二叉树
  • 每个结点的右子树是也二叉树

总结

  • 一个层数为k 的满二叉树总结点数为:(2^k) -1
  • 第i层上的结点数为:2^(i-1)

二分搜索树

概述

二分搜索树(Binary Search Tree) 也称为 二叉查找树 、二叉搜索树 、有序二叉树或排序二叉树

二分搜索树的规则

​ 对于二分搜索树的每个节点

  • 结点的值大于左子树所有节点的值
  • 结点的值小于右子树所有节点的值

在这里插入图片描述

二分搜索树的子树也是二分搜索树(递归结构)

注意:二分搜索树中存储的元素必须具有可比性

时间复杂度:

  • 平均时间复杂度O(logn)
  • 最差情况O(n)—(退化为了链表)

二分搜索树的实现

定义二分搜素树的基本结构和基本方法

要实现可比性,让树的存储内容<T>泛型实现 Comparable 接口,且接口的类型为泛型,从而实现数据可比性

public class BinarySearchTree<T extends Comparable<T>>

先定义树的结构,使用内部类定义树

	//树的结点
    class Node {
        //结点的内容
        private T val;
        //左孩子结点
        private Node left;
        //右孩子结点
        private Node right;

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

定义二分搜索树的属性方法

	//根节点   二叉树的属性
    private Node root;
    //数的结点个数
    private int size = 0;

    //构造方法
    public BinarySearchTree() {
        this.root = null;
        this.size = 0;
    }
	//获取树的节点个数
	public int getSize(){
        return this.size;
    }

	//判断树为空
    public boolean isEmpty() {
        return this.root == null;
    }
	
    @Override
    public String toString() {
        List<T> list = this.middleOrder();
        StringBuilder sb = new StringBuilder();
        list.forEach(item->sb.append(item+" "));
        return sb.toString();
    }

按值查找树中的结点

在这里插入图片描述

	 //查找树中的结点
	public Node contains(T ele) {
        return contains(root, ele);
    }

    //查找  遍历
    private Node contains(Node node, T ele) {
        //递归到底的情况
        if (node == null) {
            return null;
        }
        //递归操作
        if (ele.compareTo(node.val) == 0) {//当前结点与目标值相同,返回当前结点
            return node;
        } else if (ele.compareTo(node.val) > 0) {//若目标值>当前结点值,从右子树找
            return contains(node.right, ele);
        } else {//若目标值<当前结点值,从左子树找
            return contains(node.left, ele);
        }
    }

添加结点

	//添加
    public void add(T ele) {
        //使用递归进行添加操作
        root = add(root, ele);
        this.size += 1;//结点个数+1
    }
	
    //添加递归
    private Node add(Node root, T ele) {
        //递归到底的情况
        if (root == null) {
            Node node = new Node(ele);
            return node;
        }
        //递归操作
        //比较要插入的值与树中已有结点大小,找到合适位置插入
        if (ele.compareTo(root.val) > 0) {
            root.right = add(root.right, ele);
        } else if (ele.compareTo(root.val) < 0) {
            root.left = add(root.left, ele);
        }
        return root;
    }

查找最小结点

	//查找最小结点
    public T findMinNodeDG() {
        if (root == null) {
            return null;
        }
        return findMinNodeDG(root).val;
    }

    //查找最小结点递归
    private Node findMinNodeDG(Node root) {
        //递归到底的情况
        if (root.left == null) {
            return root;
        }
        //递归操作
        return findMinNodeDG(root.left);
    }

查找最大结点

	//查找最大结点
    public T findMaxNodeDG() {
        if (root == null) {
            return null;
        }
        return findMaxNodeDG(root).val;
    }

    //查找最大结点递归
    private Node findMaxNodeDG(Node root) {
        //递归到底的情况
        if (root.right == null) {
            return root;
        }
        //递归操作
        return findMaxNodeDG(root.right);
    }

删除最小结点

在这里插入图片描述

	/**
     * 删除最小结点
     */
    public void removeMinNode() {
        //先找到最小结点
        T minNodeVal = findMinNodeDG();
        //判断找到的最小结点
        if (minNodeVal == null) {
            //进行删除
            throw new IllegalArgumentException("找不到该节点");
        }

        root = removeMinNodeDG(root);
        this.size -= 1;
    }

    //删除最小结点递归
    private Node removeMinNodeDG(Node node) {
        if (node.left == null) {
            //若左孩子为空进行删除操作
            Node rightNode = node.right;
            node.right = null;
            return rightNode;
        }

        node.left = removeMinNodeDG(node.left);//左孩子不为空继续向左找
        return node;
    }

删除最大结点

	//删除最大结点
    public void removeMaxNode() {
        //先找到最小结点
        T maxNodeVal = findMaxNodeDG();
        //判断找到的最小结点
        if (maxNodeVal == null) {
            //进行删除
            throw new IllegalArgumentException("找不到该节点");
        }

        root = removeMaxNodeDG(root);
        this.size -= 1;
    }

    //删除最大结点递归
    private Node removeMaxNodeDG(Node node) {
        if (node.right == null) {
            //若右孩子为空进行删除操作
            Node leftNode = node.left;
            node.left = null;
            return leftNode;
        }

        node.right = removeMaxNodeDG(node.right);//右孩子不为空继续向右找
        return node;
    }

查找任意节点

	//查找任意结点
    private Node findNodeDG(Node node, T val) {
        //递归到底
        if (node == null) {
            return null;
        }
        //递归操作
        if (node.val.compareTo(val) == 0) {
            return node;
        } else if (node.val.compareTo(val) > 0) {
            return findNodeDG(node.left, val);
        } else {
            return findNodeDG(node.right, val);
        }
    }

删除任意节点

	 //删除任意结点
    public T removeNode(T val) {
        //边界判断
        if (root == null) {
            throw new IllegalArgumentException("树为空树!");
        }
        //查找结点
        Node delNode = findNodeDG(root, val);
        if (delNode != null) {
            //删除操作
            removeNodeDG(root, val);
            this.size -= 1;
            return delNode.val;//返回删除节点的值
        }
        return null;
    }

    /**
     * 删除以root为根节点的二叉搜索树中值为val的结点
     *
     * @param node 根节点
     * @param val  目标结点的值
     * @return 删除目标结点之后二叉搜索树的根节点
     */
    private Node removeNodeDG(Node node, T val) {

        //找到目标结点时,执行删除操作
        if (node.val.compareTo(val) == 0) {

            if (node.left == null) {//左子树为空
                Node rightNode = node.right;
                node.right = null;
                return rightNode;
            } else if (node.right == null) {//右子树为空
                Node leftNode = node.left;
                node.left = null;
                return leftNode;
            } else { //左右子树都不为空
                //1.找node的后继,从node.right中找到最小结点
                Node s = findMinNodeDG(node.right);
                //2.删除node.right中的最小结点并返回
                Node rightNode = removeMinNodeDG(node.right);
                //3.使用后继结点替换node
                s.left = node.left;
                s.right = rightNode;
                //4.生成新树,新树的root就是node的后继
                node.right = node.left = null;
                //返回后继结点
                return s;
            }
        }

        if (node.val.compareTo(val) > 0) {
            node.left = removeNodeDG(node.left, val);
        } else {
            node.right = removeNodeDG(node.right, val);
        }
        return node;
    }

二叉树的遍历

在这里插入图片描述

前序遍历

对于当前节点,先输出该节点,然后输出它的左孩子,最后输出它的右孩子。

	//前序遍历
    public List<T> preOrder() {
        //存储从树中遍历的值
        List<T> res = new ArrayList<>();
        //使用递归遍历
        preOrder(root, res);
        //返回存储树中元素的集合
        return res;
    }

    //前序遍历   递归
    private void preOrder(Node root, List<T> res) {
        //递归到底的情况
        if (root == null) {
            return;
        }
        //递归操作
        //先遍历root,再遍历左树,然后遍历右树
        res.add(root.val);
        preOrder(root.left, res);
        preOrder(root.right, res);
    }
中序遍历

首先遍历左子树,然后访问根结点,最后遍历右子树。若二叉树为空则结束返回。

	//中序遍历
    public List<T> middleOrder() {
        //存储从树中遍历的值
        List<T> res = new ArrayList<>();
        //使用递归遍历
        middleOrder(root, res);
        //返回存储树中元素的集合
        return res;
    }

    //中序遍历   递归
    private void middleOrder(Node root, List<T> res) {
        //递归到底的情况
        if (root == null) {
            return;
        }
        //递归操作
        //先遍历左树,再遍历root,然后遍历右数
        middleOrder(root.left, res);
        res.add(root.val);
        middleOrder(root.right, res);
    }	
后序遍历

对于当前结点,先输出它的左孩子,然后输出它的右孩子,最后输出该结点

	//后序遍历
    public List<T> subOrder() {
        //存储从树中遍历的值
        List<T> res = new ArrayList<>();
        //使用递归遍历
        subOrder(root, res);
        //返回存储树中元素的集合
        return res;
    }

    //后序遍历  递归
    private void subOrder(Node root, List<T> res) {
        //递归到底的情况
        if (root == null) {
            return;
        }
        //递归操作
        //先遍历左树,再遍历右树,然后取root
        preOrder(root.left, res);
        preOrder(root.right, res);
        res.add(root.val);
    }
层序遍历
	//层序遍历  (广度优先)
    public List<T> layerOrder() {
        List<T> res = new ArrayList<>();
        if (root != null) {
            Queue<Node> queue = new LinkedList<>();
            queue.add(root);
            while (!queue.isEmpty()) {
                //从队列取出队首元素
                Node node = queue.poll();
                res.add(node.val);
                if (node.left != null) {
                    queue.offer(node.left);
                }
                if (node.right != null) {
                    queue.offer(node.right);
                }
            }
        }
        return res;
    }

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/15616.html

(0)
小半的头像小半

相关推荐

极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!