数据结构之二叉排序树的创建和删除(java)

一、二叉排序树的定义

  二叉排序树(也称二叉查找树)或者是一棵空树,或者是具有下列特性的二叉树:
  1)若左子树非空,则左子树上所有结点的值均小于根结点的值。
  2)若右子树非空,则右子树上所有结点的值均大于根结点的值。
  3)左、右子树也分别是一棵二叉排序树。

  根据二叉排序树的定义,左子树结点值<根结点值<右子树结点值,所以对二叉排序树进行中序遍历,可以得到一个递增的有序序列。

二、二叉排序树的查找

  二叉排序树的查找是从根结点开始,沿某个分支逐层向下比较的过程。若二叉排序树非空,先将给定值与根结点的关键字比较,若相等,则查找成功;若不等,如果小于根结点的关键字,则在根结点的左子树上查找,否则在根结点的右子树上查找。这显然是一个递归的过程。

三、二叉排序树的插入

  二叉排序树作为一种动态树表,其特点是树的结构通常不是一次生成的,而是在查找过程中,当树中不存在关键字值等于给定值的结点时再进行插入的。
  插入结点的过程如下:若原二叉排序树为空,则直接插入结点;否则,若关键字k小于根结点值,则插入到左子树,若关键字k大于根结点值,则插入到右子树。插入的结点一定是一个新添加的叶结点,且是查找失败时的查找路径上访问的最后一个结点的左孩子或右孩子。

四、二叉排序树的删除

  在二叉排序树中删除一个结点时,不能把以该结点为根的子树上的结点都删除,必须先把被删除结点从存储二叉排序树的链表上摘下,将因删除结点而断开的二叉链表重新链接起来,同时确保二叉排序树的性质不会丢失。删除操作的实现过程按3种情况来处理:
  1)若被删除结点z是叶结点,则直接删除,不会破坏二叉排序树的性质。
  2)若结点z只有一棵左子树或右子树,则让z的子树成为z父结点的子树,替代z的位置。
  3)若结点z有左、右两棵子树,则令z的直接后继(或直接前驱)替代z,然后从二叉排序树中删去这个直接后继(或直接前驱),这样就转换成了第一或第二种情况。

五、二叉排序树的代码实现

package com.haiyang.datastructure.binarysorttree;

import java.util.ArrayList;
import java.util.List;

/**
 * @author haiYang
 * @create 2022-01-22 9:41
 */
public class BinarySortTree {
    private static Node tRoot = null;//根结点

    public static void main(String[] args) {
        int[] ints = {2, 16, 18, 9, 34, 8, 7, 1};


        //创建一颗二叉排序树
        for (int i = 0; i < ints.length; i++) {
            Node node = new Node(ints[i]);
            addNode(tRoot, node);
        }
        //删除目标结点
        deleteNode(16);
        //删除后的树
        preOrder(tRoot);


    }


    /**
     * 先序遍历 用于查看删除后结果
     *
     * @param root
     */

    private static void preOrder(Node root) {
        if (root == null) {
            return;
        }
        System.out.println(root);
        if (root.getLeft() != null) {
            preOrder(root.getLeft());
        }
        if (root.getRight() != null) {
            preOrder(root.getRight());
        }
    }

    /**
     * 删除目标结点
     *
     * @param targetNode 要删除结点的value
     */
    private static void deleteNode(int targetNode) {
        //查找目标结点的父结点和目标结点[parent,target]
        List<Node> nodes = searchNode(tRoot, targetNode);
        if (nodes == null) {//未找到
            System.out.println("目标结点不存在");
            return;
        }
        Node parent = nodes.get(0);//获取目标结点的父结点
        Node target = nodes.get(1);//获取目标结点
        //当目标结点的左右孩子都为null时
        if (target.getLeft() == null && target.getRight() == null) {
            if (parent == null) {//如果是根结点,删除后树为空
                tRoot = null;
            } else if (target == parent.getLeft()) {//如果删除节点为父结点的左孩子,则将父结点parent的左孩子置为null;
                parent.setLeft(null);
            } else {如果删除节点为父结点的右孩子,则将父结点parent的右孩子置为null;
                parent.setRight(null);
            }
        }
        //当目标结点的左孩子为null,右孩子非空时
        if (target.getLeft() == null && target.getRight() != null) {
            if (parent == null) {//如果是根结点,删除后树为右孩子
                tRoot = target.getRight();
            } else if (target == parent.getLeft()) {
                parent.setLeft(target.getRight());
            } else {
                parent.setRight(target.getRight());
            }
        }
        //当目标结点的右孩子为null,左孩子非空时
        if (target.getLeft() != null && target.getRight() == null) {
            if (parent == null) {//如果是根结点,删除后树为左孩子
                tRoot = target.getLeft();
            } else if (target == parent.getLeft()) {
                parent.setLeft(target.getLeft());
            } else {
                parent.setRight(target.getLeft());
            }
        }
        //左右孩子都非空时
        if (target.getLeft() != null && target.getRight() != null) {

            //寻找中序遍历的第一个孩子,也就是右子树的最小值
            Node node = infixOrder(target.getRight());
            Node r = node;//临时变量
            deleteNode(node.getValue());//转为删除中序遍历的第一个孩子
            target.setValue(r.getValue());//将第一个孩子的值赋给目标结点


        }
    }

    /**
     * @param root       根结点
     * @param targetNode 目标结点
     * @return 目标结点的父结点和目标结点[parent, target]
     */
    private static List<Node> searchNode(Node root, int targetNode) {
        List<Node> nodes = new ArrayList<>();
        Node pre = null;//标记父亲结点
        while (true) {
            if (targetNode == root.getValue()) {
                nodes.add(pre);//当根结点就是要删除结点,则parent为null
                nodes.add(root);
                return nodes;
            }
            if (targetNode < root.getValue()) {//当目标结点小于当前结点,向左查找
                if (root.getLeft() == null) {//未找到,返回null
                    return null;
                } else if (root.getLeft().getValue() == targetNode) {//找到返回
                    pre = root;
                    nodes.add(pre);
                    nodes.add(root.getLeft());
                    return nodes;
                } else {
                    pre = root;
                    root = root.getLeft();
                }
            }
            if (targetNode > root.getValue()) {//当目标结点大于当前结点,向右查找
                if (root.getRight() == null) {
                    return null;
                } else if (root.getRight().getValue() == targetNode) {
                    pre = root;
                    nodes.add(pre);
                    nodes.add(root.getRight());
                    return nodes;
                } else {
                    pre = root;
                    root = root.getRight();

                }
            }
        }
    }

    /**
     * @param root
     * @return 中序遍历的第一个结点
     */
    private static Node infixOrder(Node root) {
        Node p = root;
        while (p.getLeft() != null) {
            p = p.getLeft();
        }
        return p;

    }

    /**
     * 添加结点
     *
     * @param root 根结点
     * @param node 待添加结点
     */
    private static void addNode(Node root, Node node) {
        if (node == null) {
            return;
        } else if (root == null) {//如果是第一个结点,初始化为根结点
            tRoot = node;
        } else {
            if (node.getValue() < root.getValue()) {
                if (root.getLeft() == null) {
                    //添加到当前位置
                    root.setLeft(node);
                } else {
                    //如果比当前结点小,且左孩子不为空,递归调用
                    addNode(root.getLeft(), node);
                }
            } else {
                if (root.getRight() == null) {
                    //添加到当前位置
                    root.setRight(node);
                } else {
                    //如果比当前结点大,且右孩子不为空,递归调用
                    addNode(root.getRight(), node);
                }
            }
        }

    }
}

//结点类
class Node {
    private int value;//当前结点值
    private Node left;//左孩子
    private Node right;//右孩子

    public Node() {
    }

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

    public int getValue() {
        return value;
    }

    public void setValue(int value) {
        this.value = value;
    }

    public Node getLeft() {
        return left;
    }

    public void setLeft(Node left) {
        this.left = left;
    }

    public Node getRight() {
        return right;
    }

    public void setRight(Node right) {
        this.right = right;
    }

    @Override
    public String toString() {
        return "Node{" +
                "value=" + value +
                '}';
    }
}

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
在二排序中查找对应姓氏的编码,可以通过遍历的方式实现。假设要查找的姓氏为 `target`,首先从根节点开始遍历,比较 `target` 和当前节点存储的姓氏的大小关系。如果 `target` 小于当前节点的姓氏,就继续遍历左子;如果 `target` 大于当前节点的姓氏,就继续遍历右子;如果 `target` 等于当前节点的姓氏,就返回当前节点的编码。 下面是一个实现查找对应姓氏编码的JAVA代码: ```java public class BST { private Node root; private class Node { private String key; private int code; private Node left, right; public Node(String key, int code) { this.key = key; this.code = code; } } public int searchCode(String target) { Node x = root; while (x != null) { int cmp = target.compareTo(x.key); if (cmp < 0) x = x.left; else if (cmp > 0) x = x.right; else return x.code; } return -1; } public void put(String key, int code) { root = put(root, key, code); } private Node put(Node x, String key, int code) { if (x == null) return new Node(key, code); int cmp = key.compareTo(x.key); if (cmp < 0) x.left = put(x.left, key, code); else if (cmp > 0) x.right = put(x.right, key, code); else x.code = code; return x; } public static void main(String[] args) { BST bst = new BST(); bst.put("张三", 1); bst.put("李四", 2); bst.put("王五", 3); System.out.println(bst.searchCode("李四")); // 2 System.out.println(bst.searchCode("赵六")); // -1 } } ``` 在这个二排序中,我们使用整数作为编码,节点中存储的是姓氏和对应的编码。在 `searchCode` 函数中,我们使用 `while` 循环遍历,直到找到对应的姓氏或者遍历到叶子节点为止。如果找到了对应的姓氏,就返回节点中存储的编码;如果没有找到,就返回 `-1` 表示未找到。在 `main` 函数中,我们演示了如何插入三个不同的姓氏和对应的编码,并且查找其中一个姓氏的编码。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

HEU_THY

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

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

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

打赏作者

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

抵扣说明:

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

余额充值