学习笔记:《程序员小灰》二叉查找树(Binary Search Tree)

1 定义

二叉查找树(Binary Search Tree)的特征:

  • 若左子树不空,则左子树上所有节点的值均小于或等于它的根节点的值;
  • 若右子树不空,则右子树上所有节点的值均大于或等于它的根节点的值;
  • 左、右子树也分别为二叉查找树。

在这里插入图片描述

2 查找

将要查找的数据与根节点的值进行比较,如果相等就返回,如果小于就到左子树中递归查找,如果大于就到右子树中递归查找。

在这里插入图片描述

3 添加

在二叉查找树中进行插入操作时只需找到待插入的父节点,将数据插入即可。

在这里插入图片描述

4 删除

  • 在待删除的节点没有子节点时,直接删除该节点,即在其父节点中将其对应的子节点置空即可。

在这里插入图片描述

  • 在待删除的节点只有一个子节点时,使用子节点替换当前节点,然后删除该节点即可。

在这里插入图片描述

  • 在待删除的节点有两个子节点时,首先查找该节点的替换节点(两种做法:左子树中的最大节点、右子树中的最小节点),然后替换待删除的节点为替换节点,最后删除替换节点。

在这里插入图片描述

5 存在的问题

倘若二叉查找树多次在同一侧插入节点,将会导致性能变差,几乎变成线性查找。

在这里插入图片描述

6 实现

定义二叉查找树节点:

/**
 * 二叉查找树节点
 *
 * @author wgm
 * @since 2021/4/25
 */
public class BinarySearchTreeNode {

    private Integer data;
    private BinarySearchTreeNode leftChild;
    private BinarySearchTreeNode rightChild;

    public BinarySearchTreeNode(Integer data) {
        this.data = data;
    }

    public BinarySearchTreeNode setLeftChild(int data) {
        BinarySearchTreeNode node = new BinarySearchTreeNode(data);
        this.setLeftChild(node);
        return node;
    }

    public BinarySearchTreeNode setRightChild(int data) {
        BinarySearchTreeNode node = new BinarySearchTreeNode(data);
        this.setRightChild(node);
        return node;
    }

    @Override
    public String toString() {
        return data.toString();
    }

    public Integer getData() {
        return data;
    }

    public void setData(Integer data) {
        this.data = data;
    }

    public BinarySearchTreeNode getLeftChild() {
        return leftChild;
    }

    public void setLeftChild(BinarySearchTreeNode leftChild) {
        this.leftChild = leftChild;
    }

    public BinarySearchTreeNode getRightChild() {
        return rightChild;
    }

    public void setRightChild(BinarySearchTreeNode rightChild) {
        this.rightChild = rightChild;
    }
}

定义二叉查找树:

import java.util.LinkedList;

/**
 * 二叉查找树
 *
 * @author wgm
 * @since 2021/4/25
 */
public class BinarySearchTree {

    private BinarySearchTreeNode root;

    public BinarySearchTree(BinarySearchTreeNode root) {
        this.root = root;
    }

    /**
     * 建树
     *
     * @param ints
     * @return
     */
    public static BinarySearchTree buildTree(int[] ints) {
        if (ints == null || ints.length == 0) {
            return null;
        }
        BinarySearchTree tree = new BinarySearchTree(new BinarySearchTreeNode(ints[0]));
        for (int i = 1; i < ints.length; i++) {
            tree.insert(ints[i]);
        }
        return tree;
    }

    /**
     * 查找节点是否存在
     *
     * @param data
     * @return
     */
    public boolean exist(int data) {
        if (this.root == null) {
            return false;
        }
        BinarySearchTreeNode visit = root;
        while (visit != null) {
            if (data < visit.getData()) {
                visit = visit.getLeftChild();
            } else if (data > visit.getData()) {
                visit = visit.getRightChild();
            } else {
                return true;
            }
        }
        return false;
    }

    /**
     * 添加节点
     *
     * @param data
     */
    public void insert(int data) {
        if (this.root == null) {
            return;
        }
        BinarySearchTreeNode preVisit = root;
        BinarySearchTreeNode visit = root;
        while (visit != null) {
            preVisit = visit;
            if (data <= visit.getData()) {
                visit = visit.getLeftChild();
            } else {
                visit = visit.getRightChild();
            }
        }
        if (data <= preVisit.getData()) {
            preVisit.setLeftChild(data);
        } else {
            preVisit.setRightChild(data);
        }
    }

    /**
     * 删除节点
     *
     * @param data
     */
    public void delete(int data) {
        if (this.root == null) {
            return;
        }
        BinarySearchTreeNode preVisit = root;
        BinarySearchTreeNode visit = root;
        while (visit != null) {
            if (data < visit.getData()) {
                preVisit = visit;
                visit = visit.getLeftChild();
            } else if (data > visit.getData()) {
                preVisit = visit;
                visit = visit.getRightChild();
            } else {
                delete(preVisit, visit);
                break;
            }
        }
    }

    private void delete(BinarySearchTreeNode preVisit, BinarySearchTreeNode visit) {
        BinarySearchTreeNode leftChild = visit.getLeftChild();
        BinarySearchTreeNode rightChild = visit.getRightChild();
        if (leftChild == null && rightChild == null) { // 待删除节点为叶子节点
            if (visit.getData() < preVisit.getData()) { // 待删除节点为父节点的左节点
                preVisit.setLeftChild(null);
            } else if (visit.getData() > preVisit.getData()) { // 待删除节点为父节点的右节点
                preVisit.setRightChild(null);
            } else { // 待删除节点为根节点
                root = null;
            }
        } else if (leftChild != null && rightChild == null) { // 待删除节点只有左节点
            if (visit.getData() < preVisit.getData()) { // 待删除节点为父节点的左节点
                preVisit.setLeftChild(leftChild);
            } else if (visit.getData() > preVisit.getData()) { // 待删除节点为父节点的右节点
                preVisit.setRightChild(leftChild);
            } else { // 待删除节点为根节点
                root = leftChild;
            }
        } else if (leftChild == null && rightChild != null) { // 待删除节点只有右节点
            if (visit.getData() < preVisit.getData()) { // 待删除节点为父节点的左节点
                preVisit.setLeftChild(rightChild);
            } else if (visit.getData() > preVisit.getData()) { // 待删除节点为父节点的右节点
                preVisit.setRightChild(rightChild);
            } else { // 待删除节点为根节点
                root = rightChild;
            }
        } else { // 待删除节点有左、右节点
            BinarySearchTreeNode ppre = leftChild;
            BinarySearchTreeNode p = leftChild;
            while (p.getRightChild() != null) {
                ppre = p;
                p = p.getRightChild();
            }

            ppre.setRightChild(null);
            if (visit.getLeftChild() != p) {
                p.setLeftChild(visit.getLeftChild());
            }
            p.setRightChild(visit.getRightChild());
            visit.setLeftChild(null);
            visit.setRightChild(null);

            if (visit.getData() < preVisit.getData()) { // 待删除节点为父节点的左节点
                preVisit.setLeftChild(p);
            } else if (visit.getData() > preVisit.getData()) { // 待删除节点为父节点的右节点
                preVisit.setRightChild(p);
            } else { // 待删除节点为根节点
                root = p;
            }
        }
    }

    /**
     * 层序遍历(队列)
     */
    public void level() {
        if (this.root == null) {
            return;
        }
        BinarySearchTreeNode visit = this.root;
        LinkedList<BinarySearchTreeNode> queue = new LinkedList<>();
        queue.offer(visit);
        while (!queue.isEmpty()) {
            visit = queue.poll();
            System.out.print(visit + " ");
            BinarySearchTreeNode leftChild = visit.getLeftChild();
            if (leftChild != null) {
                queue.offer(leftChild);
            }
            BinarySearchTreeNode rightChild = visit.getRightChild();
            if (rightChild != null) {
                queue.offer(rightChild);
            }
        }
        System.out.println();
    }

    @Override
    public String toString() {
        return "BinarySearchTree{" +
                "root=" + root +
                '}';
    }

    public BinarySearchTreeNode getRoot() {
        return root;
    }

    public void setRoot(BinarySearchTreeNode root) {
        this.root = root;
    }
}

编写测试类:

import java.util.Arrays;

/**
 * 二叉查找树测试
 *
 * @author wgm
 * @since 2021/4/25
 */
public class BinarySearchTreeTest {

    public static void main(String[] args) {
        int[] ints = new int[] {10, 4, 5, 2, 12, 14, 8};

        BinarySearchTree tree = BinarySearchTree.buildTree(ints);
        tree.level();
        System.out.println("节点11是否存在:" + tree.exist(11));
        System.out.println("节点12是否存在:" + tree.exist(12));

        System.out.println("\r\n循环添加元素");
        for (int i = 0; i < ints.length; i++) {
            if (i == 0) {
                tree = BinarySearchTree.buildTree(Arrays.copyOfRange(ints, 0, 1));
                tree.level();
            } else {
                tree.insert(ints[i]);
                tree.level();
            }
        }

        System.out.println("\r\n循环删除元素");
        for (int i = 0; i < ints.length; i++) {
            tree.level();
            tree.delete(ints[i]);
        }
    }
}

7 测试

D:\program\Java\jdk1.8.0_241\bin\java.exe -agentlib:jdwp=transport=dt_socket,address=127.0.0.1:59009,suspend=y,server=n "-javaagent:C:\Users\GMWANG~1\AppData\Local\Temp\captureAgent172jars\debugger-agent.jar" -Dfile.encoding=UTF-8 -classpath "D:\program\Java\jdk1.8.0_241\jre\lib\charsets.jar;D:\program\Java\jdk1.8.0_241\jre\lib\deploy.jar;D:\program\Java\jdk1.8.0_241\jre\lib\ext\access-bridge-64.jar;D:\program\Java\jdk1.8.0_241\jre\lib\ext\cldrdata.jar;D:\program\Java\jdk1.8.0_241\jre\lib\ext\dnsns.jar;D:\program\Java\jdk1.8.0_241\jre\lib\ext\jaccess.jar;D:\program\Java\jdk1.8.0_241\jre\lib\ext\jfxrt.jar;D:\program\Java\jdk1.8.0_241\jre\lib\ext\localedata.jar;D:\program\Java\jdk1.8.0_241\jre\lib\ext\nashorn.jar;D:\program\Java\jdk1.8.0_241\jre\lib\ext\sunec.jar;D:\program\Java\jdk1.8.0_241\jre\lib\ext\sunjce_provider.jar;D:\program\Java\jdk1.8.0_241\jre\lib\ext\sunmscapi.jar;D:\program\Java\jdk1.8.0_241\jre\lib\ext\sunpkcs11.jar;D:\program\Java\jdk1.8.0_241\jre\lib\ext\zipfs.jar;D:\program\Java\jdk1.8.0_241\jre\lib\javaws.jar;D:\program\Java\jdk1.8.0_241\jre\lib\jce.jar;D:\program\Java\jdk1.8.0_241\jre\lib\jfr.jar;D:\program\Java\jdk1.8.0_241\jre\lib\jfxswt.jar;D:\program\Java\jdk1.8.0_241\jre\lib\jsse.jar;D:\program\Java\jdk1.8.0_241\jre\lib\management-agent.jar;D:\program\Java\jdk1.8.0_241\jre\lib\plugin.jar;D:\program\Java\jdk1.8.0_241\jre\lib\resources.jar;D:\program\Java\jdk1.8.0_241\jre\lib\rt.jar;D:\project\untitled\out\production\untitled;D:\program\JetBrains\IntelliJ IDEA 2020.1\lib\idea_rt.jar" BinarySearchTreeTest
Connected to the target VM, address: '127.0.0.1:59009', transport: 'socket'
10 4 12 2 5 14 8 
节点11是否存在:false
节点12是否存在:true

循环添加元素
10 
10 4 
10 4 5 
10 4 2 5 
10 4 12 2 5 
10 4 12 2 5 14 
10 4 12 2 5 14 8 

循环删除元素
10 4 12 2 5 14 8 
8 4 12 2 5 14 
8 2 12 5 14 
8 2 12 14 
8 12 14 
8 14 
8 
Disconnected from the target VM, address: '127.0.0.1:59009', transport: 'socket'

Process finished with exit code 0

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值