手写红黑树【数据结构】

手写红黑树【数据结构】

  • 前言
  • 版权
  • 推荐
  • 手写红黑树
    • 一、理论知识
      • 红黑树的特征
      • 增加
      • 删除
    • 二、手写代码
      • 初始-树结点
      • 初始-红黑树
      • 初始-遍历
      • 初始-判断红黑树是否有效
      • 查找
      • 增加-1.父为黑,直接插入
      • 增加-2. 父叔为红,颜色调换
      • 增加-3. 父红叔黑,颜色调换,再移动
      • 增加-4. 父子不同向,先掰直,再执行第3种情况
      • 测试增加
      • 删除-0 初始化数据
      • 删除-1.单个红节点 直接删除
      • 删除-3.带有一个子节点
      • 删除-4.带有两个子节点
      • 删除-2.单个黑结点
        • 2.1.1兄黑,对侄红
        • 2.1.2兄黑,顺侄红
        • 2.1.3 兄黑,双侄黑
      • 删除-2.单个黑结点 2.2兄红
      • 测试删除
  • 附录源码
  • 最后

前言

2024-3-30 10:52:57

昨天晚上B站看到的视频
00:00~01:00

以下内容源自《【数据结构】》
仅供学习交流使用

版权

禁止其他平台发布时删除以下此话
本文首次发布于CSDN平台
作者是CSDN@日星月云
博客主页是https://jsss-1.blog.csdn.net
禁止其他平台发布时删除以上此话

推荐

我红黑树那么牛,你们凭什么不用我?

手写红黑树

一、理论知识

红黑树的特征

红黑树是一种二叉平衡树,只不过这个平衡不是那么严苛,只需黑平衡就行

  1. 结点分为两种颜色
  2. 根节点是黑色
  3. 叶子结点是黑色的,叶子结点是不存储数据的空结点
  4. 两个红结点不能相连,即红父亲的孩子都是黑色的
  5. 对于任意一个结点,其到叶子结点的路径上的黑色结点数量是一致的

增加

视频

插入结点的颜色是红色的
因为是黑平衡,所以插入红结点有概率不会破坏这个规则

  1. 父为黑,直接插入
  2. 父叔为红,颜色调换
  3. 父红叔黑,颜色调换,再移动
  4. 父子不同向,先掰直,再执行第3种情况

删除

视频

https://www.processon.com/view/link/6550422f54fca5688e143664
在这里插入图片描述

二、手写代码

为了实现简单,加入parent的指针,和isLeaf的标记

可以使用HashMap用来记录每一个叶子结点的父亲结点,即键是叶子,值是父亲;
也可以直接在Node结点中加入双亲指针。
根节点的父亲结点是null
特别注意的是,parent的维护

如果是叶子结点,它的isLeaf的值为true。

初始-树结点

//结点
class TreeNode {


    //true是黑色,false是红色
    boolean color;

    //数据
    Integer data;


    TreeNode left;
    TreeNode right;

    private static final boolean RED = false;
    private static final boolean BLACK = true;

    //是否是叶子结点
    boolean isLeaf;

    //方便实现
    TreeNode parent;

    public TreeNode() {
    }

    public TreeNode(int data) {
        this.data = data;

    }

    public TreeNode(boolean color, Integer data) {
        this.color = color;
        this.data = data;

    }

    public TreeNode(boolean color, Integer data, TreeNode parent) {
        this.color = color;
        this.data = data;
        this.parent = parent;
    }

    public TreeNode(boolean color, Integer data, TreeNode parent, boolean isLeaf) {
        this.color = color;
        this.data = data;
        this.parent = parent;
        this.isLeaf = isLeaf;
    }

//    public TreeNode(Integer data,TreeNode left, TreeNode right) {
//        this.data = data;
//        this.left = left;
//        this.right = right;
//    }


    public boolean isBlack() {
        return color == BLACK;
    }

    @Override
    public String toString() {
        return "TreeNode{" +
                "color=" + color +
                ", data=" + data +
                '}';
    }

}

初始-红黑树


package test;


import java.util.LinkedList;
import java.util.Queue;

public class RedBlackTree {

    private static final boolean RED = false;
    private static final boolean BLACK = true;

    //根结点
    TreeNode root;

    public void initRoot(int data) {
        root = new TreeNode(BLACK, data, null, false);
        TreeNode nil = new TreeNode(BLACK, null, root, true);
        root.left = nil;
        root.right = nil;
    }


    /**
     * 增加
     * 1. 父为黑,直接插入
     * 2. 父叔为红,颜色调换
     * 3. 父红叔黑,颜色调换,再移动
     * 4. 父子不同向,先掰直,再执行第3种情况
     *
     * @param data 数据
     */
    public void add(int data) {
       
    }

    /**
     *  删除
     * 1.单个红节点  直接删除
     * 2.单个黑节点
     *      2.1兄黑
     *         2.1.1 对侄红 (指方向相反的侄节点)
     *               父兄交替旋转、然后按父红兄弟黑换色 (最后一步的换色,父红两兄弟黑,是按交替旋转之后的关系处理。)
     *         2.1.2 顺侄红(指方向相同的侄节点)
     *              兄侄交替旋转,并调换颜色,就会变成对侄红,然后按2.1.1处理
     *         2.1.3 双侄黑
     *              兄变红父变黑,如果父本身就是黑,就以父亲角度按情况2处理
     *
     *      2.2 兄红
     *              父兄交替旋转,并调换颜色,新的兄节点将变黑,在按2.1处理
     * 3.带有一个子节点(当一个节点只有一个子节点时(空叶子除外),该节点必定是黑节点,其子节点必定是红色)
     *      用红子节点值替换,然后直接删除红子节点
     * 4.带有两个子节点!
     *      找到左子树中最靠右的子节点,用该节点值替换,并删除该节点按情况1,2,3处理(左子树中最大的值,也是离其最近的值)
     *
     * @param data 数据
     */
    public void delete(int data) {

    }

    public static void main(String[] args) {
        RedBlackTree tree = new RedBlackTree();
        tree.inorder(tree.root);
//        tree.levelOrder(tree.root);

    }

}


初始-遍历

 //中序遍历
    public void inorder(TreeNode root) {
        if (root == null) {
            return;
        }

        if (!root.left.isLeaf) {
            inorder(root.left);
        }
        System.out.println(root);
        if (!root.right.isLeaf) {
            inorder(root.right);
        }
    }

    //层序遍历
    public void levelOrder(TreeNode root) {
        if (root == null) {
            return;
        }

        Queue<TreeNode> queue = new LinkedList<>();
        queue.add(root);

        while (!queue.isEmpty()) {
            int size = queue.size();
            for (int i = 0; i < size; i++) {
                TreeNode cur = queue.poll();
                System.out.print(cur + "\t");
                if (cur.left != null) {
                    queue.add(cur.left);
                }
                if (cur.right != null) {
                    queue.add(cur.right);
                }
            }
            System.out.println();

        }
    }

 

初始-判断红黑树是否有效

    //判断是否是有效的红黑树
    public static boolean isValidRedBlackTree(TreeNode root) {
        if (root == null) {
            return true;
        }

        // 检查根节点是否是黑色
        if (root.color != BLACK) {
            return false;
        }

        // 计算黑高度,并检查红黑平衡
        blackHeight = -1;
        if (!checkBlackBalance(root, 0)) {
            return false;
        }

        // 递归检查每个节点
        return isValidRedBlackSubtree(root);
    }

    private static boolean checkBlackBalance(TreeNode node, int currentBlackHeight) {
        if (node.isLeaf) {
            if (blackHeight == -1) {
                blackHeight = currentBlackHeight;
                return true;
            } else {
                return currentBlackHeight == blackHeight;
            }
        }

        if (node.color == BLACK) {
            currentBlackHeight++;
        }

        return checkBlackBalance(node.left, currentBlackHeight) && checkBlackBalance(node.right, currentBlackHeight);
    }

    private static boolean isValidRedBlackSubtree(TreeNode node) {
        if (node == null) {
            return true;
        }

        // 检查红黑树性质
        if (node.color == RED) {
            if ((node.left != null && node.left.color != BLACK) || (node.right != null && node.right.color != BLACK)) {
                return false;
            }
        }

        // 递归检查左右子树
        return isValidRedBlackSubtree(node.left) && isValidRedBlackSubtree(node.right);
    }

查找

 	/**
     * 查找
     *
     * @param data 数据
     * @return 查找结点,如果差不到就会返回叶子结点
     */
    public TreeNode find(int data) {
        TreeNode find = root;

        while (!find.isLeaf) {
            if (data < find.data) {
                find = find.left;
            } else if(data==find.data){
                return find;
            } else if (data > find.data) {
                find = find.right;
            }
        }

        return find;
    }


增加-1.父为黑,直接插入

   /**
     * 增加
     * 1. 父为黑,直接插入
     * 2. 父叔为红,颜色调换
     * 3. 父红叔黑,颜色调换,再移动
     * 4. 父子不同向,先掰直,再执行第3种情况
     *
     * @param data 数据
     */
    public void add(int data) {
        if (root == null) {
            initRoot(data);
            return;
        }

        TreeNode find = find(data);

        if (!find.isLeaf) {
            System.out.println("不能插入相同数据的结点");
            return;
        }

        TreeNode parent = find.parent;

        TreeNode newNode = new TreeNode(RED, data, parent);
        TreeNode nil = new TreeNode(BLACK, null, newNode, true);
        newNode.left = nil;
        newNode.right = nil;

        if (data < parent.data) {
            parent.left = newNode;
        } else {
            parent.right = newNode;
        }

        //1.父为黑,直接插入
        if (parent.isBlack()) {
            //不需要额外的操作
        } else {
        	//跳转2...
        }

增加-2. 父叔为红,颜色调换

    TreeNode grandpa = parent.parent;
            TreeNode uncle = grandpa.left != parent ? grandpa.left : grandpa.right;
            //2. 父叔为红,颜色调换
            if (!uncle.isBlack()) {
                parent.color = BLACK;
                uncle.color = BLACK;
                grandpa.color = RED;

                //如果爷爷是根节点
                if (grandpa == root) {
                    grandpa.color = BLACK;
                    return;
                }

                //爷爷变成红色后,它的父叔可能为红
                TreeNode cur=grandpa;
                parent=cur.parent;
                grandpa=parent.parent;
                
                if (parent==null||grandpa==null){
                    return;
                }
                uncle=grandpa.left != parent ? grandpa.left : grandpa.right;

                while (!cur.isBlack()&&!parent.isBlack()&&!uncle.isBlack()){
                    parent.color = BLACK;
                    uncle.color = BLACK;
                    grandpa.color = RED;
                    //如果爷爷是根节点
                    if (grandpa == root) {
                        grandpa.color = BLACK;
                        break;
                    }

                    cur=grandpa;
                    parent=cur.parent;
                    grandpa=parent.parent;
                    uncle=grandpa.left != parent ? grandpa.left : grandpa.right;
                }
            } else {
            	//跳转3...
            }
                

增加-3. 父红叔黑,颜色调换,再移动


				//跳转3...
                boolean right1 = data > parent.data;
                boolean right2 = parent.data > grandpa.data;
                boolean direct1 = right1 && right2;
                boolean left1 = data < parent.data;
                boolean left2 = parent.data < grandpa.data;
                boolean direct2 = left1 && left2;
                //3. 父红叔黑,颜色调换,再移动
                if (direct1 || direct2) {
                    op(data, parent, grandpa);
                } else {
                	//跳转4...
                }


    public void op(int data, TreeNode parent, TreeNode grandpa) {
        boolean right1 = data > parent.data;
        boolean right2 = parent.data > grandpa.data;
        boolean direct1 = right1 && right2;
        boolean left1 = data < parent.data;
        boolean left2 = parent.data < grandpa.data;
        boolean direct2 = left1 && left2;

        boolean tmp = grandpa.color;
        grandpa.color = parent.color;
        parent.color = tmp;

        TreeNode grandpaPa = grandpa.parent;
        if (parent.data < grandpaPa.data) {
            grandpaPa.left = parent;
        } else {
            grandpaPa.right = parent;
        }
        parent.parent = grandpaPa;

        if (direct1) {
            parent.left = grandpa;
            grandpa.parent = parent;
        } else if (direct2) {
            parent.right = grandpa;
            grandpa.parent = parent;
        }
        grandpa.left = new TreeNode(BLACK, null, grandpa, true);
        grandpa.right = new TreeNode(BLACK, null, grandpa, true);
    }

增加-4. 父子不同向,先掰直,再执行第3种情况

					//跳转4...
 					//4. 父子不同向,先掰直,再执行第3种情况
                    if (left1) {
                        grandpa.right = newNode;
                        newNode.right = parent;
                    }
                    if (right1) {
                        grandpa.left = newNode;
                        newNode.left = parent;
                    }
                    newNode.parent = grandpa;
                    parent.parent = newNode;

                    TreeNode newNil = new TreeNode(BLACK, null, parent, true);
                    parent.left = newNil;
                    parent.right = newNil;

                    op(parent.data, newNode, grandpa);

测试增加

    public static void main(String[] args) {
        RedBlackTree tree = new RedBlackTree();
        testAdd(tree);
    }

    private static void testAdd(RedBlackTree tree) {
        tree.add(157);//0
        tree.add(12);//1
        tree.add(200);//1

        tree.add(250);//2
        tree.add(260);//3
        tree.add(220);//2
        tree.add(210);//4

        tree.add(11);//1
        tree.add(10);//3
        tree.add(7);//2
        tree.add(9);//4


        tree.inorder(tree.root);
//        tree.levelOrder(tree.root);

        System.out.println(isValidRedBlackTree(tree.root));
    }

11、10、7、9的插入图如下
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

2024-3-30 15:35:56

删除-0 初始化数据

在这里插入图片描述

    public static void main(String[] args) {
        RedBlackTree tree = new RedBlackTree();
//        testAdd(tree);

        initData(tree);

    }

    private static void initData(RedBlackTree tree) {
        int[] nums={430,261,636,
        95,344,559,822,
        37,131,330,369,499,594,705,981,
        262,345,485,664,818};

        for (int i = 0; i < nums.length; i++) {
            tree.add(nums[i]);
        }

//        tree.inorder(tree.root);
        tree.levelOrder(tree.root);
        System.out.println(isValidRedBlackTree(tree.root));
    }

删除-1.单个红节点 直接删除

 /**
     *  删除
     * 1.单个红节点  直接删除
     * 2.单个黑节点
     *      2.1兄黑
     *         2.1.1 对侄红 (指方向相反的侄节点)
     *               父兄交替旋转、然后按父红兄弟黑换色 (最后一步的换色,父红两兄弟黑,是按交替旋转之后的关系处理。)
     *         2.1.2 顺侄红(指方向相同的侄节点)
     *              兄侄交替旋转,并调换颜色,就会变成对侄红,然后按2.1.1处理
     *         2.1.3 双侄黑
     *              兄变红父变黑,如果父本身就是黑,就以父亲角度按情况2处理
     *
     *      2.2 兄红
     *              父兄交替旋转,并调换颜色,新的兄节点将变黑,在按2.1处理
     * 3.带有一个子节点(当一个节点只有一个子节点时(空叶子除外),该节点必定是黑节点,其子节点必定是红色)
     *      用红子节点值替换,然后直接删除红子节点
     * 4.带有两个子节点
     *      找到左子树中最靠右的子节点,用该节点值替换,并删除该节点按情况1,2,3处理(左子树中最大的值,也是离其最近的值)
     *
     * @param data 数据
     */
    public void delete(int data) {
        TreeNode find = find(data);
        if (find.isLeaf){
            System.out.println("没有找到");
            return;
        }
        //1.单个红节点
        if (!find.isBlack()){
            if (find.left.isLeaf&&find.right.isLeaf){
                TreeNode parent = find.parent;
                TreeNode nil=new TreeNode(BLACK,null,parent,true);
                if (data<parent.data){
                    parent.left=nil;
                }else {
                    parent.right=nil;
                }
            }else {
                //4.带有两个子节点
               
            }

        }else {
            //3.带有一个子节点,用红子节点值替换,然后直接删除红子节点
            

        }


    }

删除-3.带有一个子节点

			//3.带有一个子节点,用红子节点值替换,然后直接删除红子节点
            if (find.left.isLeaf&&!find.right.isBlack()){
                find.data=find.right.data;
                find.right= new TreeNode(BLACK,null,find,true);
            }else if (find.right.isLeaf&&!find.left.isBlack()){
                find.data=find.left.data;
                find.left= new TreeNode(BLACK,null,find,true);
            } 


删除-4.带有两个子节点

			else if (!find.left.isLeaf&&!find.right.isLeaf){//4.带有两个子节点
                TreeNode replace = findReplace(find);
                delete(replace.data);
                find.data= replace.data;
            }else {//2.单个黑结点
            
    /**
     * 查找替代的结点
     * 中序遍历线索树的直接前驱结点
     * @param node 删除的结点
     * @return 查找替代
     */
    public TreeNode findReplace(TreeNode node) {
        TreeNode left = node.left;
        while (!left.isLeaf) {
            left=left.right;
        }
        return left.parent;
    }

删除-2.单个黑结点

2.1.1兄黑,对侄红
  				TreeNode parent=find.parent;
                TreeNode brother=parent.left!=find?parent.left:parent.right;
                boolean left=find.data<parent.data;

                //对侄
                TreeNode duiNephew=left?brother.right:brother.left;
                //顺侄
                TreeNode shunNephew=left?brother.left:brother.right;

                if (brother.isBlack()){//2.1兄黑
                    //2.1.1 对侄红
                    TreeNode duiNephew=left?brother.right:brother.left;
                    if (!duiNephew.isBlack()){
                        //父兄交替旋转
                        TreeNode grandpa=parent.parent;
                        if (brother.data<grandpa.data){
                            grandpa.left=brother;
                        }else {
                            grandpa.right=brother;
                        }
                        brother.parent=grandpa;

                        if (left){
                            brother.left=parent;
                        }else {
                            brother.right=parent;
                        }
                        parent.parent=brother;

                        TreeNode nil=new TreeNode(BLACK,null,parent,true);
                        parent.left=nil;
                        parent.right=nil;

                        //并调换颜色
                        brother.color=RED;
                        duiNephew.color=BLACK;
                        parent.color=BLACK;


                    }else if (!shunNephew.isBlack()){//2.1.2 顺侄红
                    
                    }else if (brother.left.isBlack()&&brother.right.isBlack()){//2.1.3 双侄黑
                    
                    }
                else{//兄红
                
                }
     
                   
2.1.2兄黑,顺侄红
                        //兄侄交替旋转,并调换颜色,就会变成对侄红,
                        if (brother.data< parent.data){
                            parent.left=shunNephew;
                            shunNephew.left=brother;
                        }else {
                            parent.right=shunNephew;
                            shunNephew.right=brother;

                        }
                        shunNephew.parent=parent;
                        brother.parent=shunNephew;

                        TreeNode nil=new TreeNode(BLACK,null,brother,true);
                        brother.left=nil;
                        brother.right=nil;

                        brother.color=RED;
                        shunNephew.color=BLACK;

                        delete(data);

2.1.3 兄黑,双侄黑
                        //兄变红父变黑,如果父本身就是黑,就以父亲角度按情况2处理
                        brother.color=RED;
                        parent.color=BLACK;
                        TreeNode nil=new TreeNode(BLACK,null,parent,true);
                        if (find.data< parent.data){
                            parent.left=nil;
                        }else {
                            parent.right=nil;
                        }

删除-2.单个黑结点 2.2兄红

					//父兄交替旋转,并调换颜色,新的兄节点将变黑,在按2.1处理
                    TreeNode grandpa=parent.parent;
                    if (brother.data<grandpa.data){
                        grandpa.left=brother;
                    }else {
                        grandpa.right=brother;
                    }
                    brother.parent=grandpa;

                    TreeNode tmp;
                    if (data<parent.data){
                        tmp=brother.left;
                        brother.left=parent;
                    }else {
                        tmp=brother.right;
                        brother.right=parent;

                    }
                    parent.parent=brother;


                    if (data<parent.data){
                        parent.left=find;
                        parent.right=tmp;
                    }else {
                        parent.left=tmp;
                        parent.right=find;
                    }

                    brother.color=BLACK;
                    parent.color=RED;

                    delete(data);

测试删除

    public static void main(String[] args) {
        RedBlackTree tree = new RedBlackTree();
//        testAdd(tree);

        initData(tree);

        testDelete(tree);
        
        tree.levelOrder(tree.root);
        System.out.println(isValidRedBlackTree(tree.root));

    }

    private static void testDelete(RedBlackTree tree) {
        tree.delete(262);//1
        tree.delete(818);//1

        tree.delete(705);//3
        tree.delete(369);//3

        tree.add(346);
        tree.delete(430);//4

        tree.delete(594);//2.1.1

        tree.add(570);
        tree.delete(485);//2.1.1


        tree.add(565);
        tree.delete(499);//2.1.2

        tree.add(335);
        tree.delete(345);//2.1.2
        tree.delete(559);//2.1.3

        tree.delete(570);
        tree.delete(565);//2.2

        tree.delete(37);
        tree.delete(131);
        tree.delete(95);//2.2
    }

在这里插入图片描述

附录源码

package test;


import java.util.LinkedList;
import java.util.Queue;

public class RedBlackTree {

    private static final boolean RED = false;
    private static final boolean BLACK = true;


    //根结点
    TreeNode root;


    public void initRoot(int data) {
        root = new TreeNode(BLACK, data, null, false);
        TreeNode nil = new TreeNode(BLACK, null, root, true);
        root.left = nil;
        root.right = nil;
    }


    /**
     * 增加
     * 1. 父为黑,直接插入
     * 2. 父叔为红,颜色调换
     * 3. 父红叔黑,颜色调换,再移动
     * 4. 父子不同向,先掰直,再执行第3种情况
     *
     * @param data 数据
     */
    public void add(int data) {
        if (root == null) {
            initRoot(data);
            return;
        }

        TreeNode find = find(data);

        if (!find.isLeaf) {
            System.out.println("不能插入相同数据的结点");
            return;
        }

        TreeNode parent = find.parent;

        TreeNode newNode = new TreeNode(RED, data, parent);
        TreeNode nil = new TreeNode(BLACK, null, newNode, true);
        newNode.left = nil;
        newNode.right = nil;

        if (data < parent.data) {
            parent.left = newNode;
        } else {
            parent.right = newNode;
        }

        //1.父为黑,直接插入
        if (parent.isBlack()) {
            //不需要额外的操作
        } else {
            TreeNode grandpa = parent.parent;
            TreeNode uncle = grandpa.left != parent ? grandpa.left : grandpa.right;
            //2. 父叔为红,颜色调换
            if (!uncle.isBlack()) {
                parent.color = BLACK;
                uncle.color = BLACK;
                grandpa.color = RED;

                //如果爷爷是根节点
                if (grandpa == root) {
                    grandpa.color = BLACK;
                    return;
                }

                //爷爷变成红色后,它的父叔可能为红
                TreeNode cur=grandpa;
                parent=cur.parent;
                grandpa=parent.parent;

                if (parent==null||grandpa==null){
                    return;
                }
                uncle=grandpa.left != parent ? grandpa.left : grandpa.right;

                while (!cur.isBlack()&&!parent.isBlack()&&!uncle.isBlack()){
                    parent.color = BLACK;
                    uncle.color = BLACK;
                    grandpa.color = RED;
                    //如果爷爷是根节点
                    if (grandpa == root) {
                        grandpa.color = BLACK;
                        break;
                    }

                    cur=grandpa;
                    parent=cur.parent;
                    grandpa=parent.parent;
                    uncle=grandpa.left != parent ? grandpa.left : grandpa.right;
                }

            } else {
                boolean right1 = data > parent.data;
                boolean right2 = parent.data > grandpa.data;
                boolean direct1 = right1 && right2;
                boolean left1 = data < parent.data;
                boolean left2 = parent.data < grandpa.data;
                boolean direct2 = left1 && left2;
                //3. 父红叔黑,颜色调换,再移动
                if (direct1 || direct2) {
                    op(data, parent, grandpa);
                } else {
                    //4. 父子不同向,先掰直,再执行第3种情况
                    if (left1) {
                        grandpa.right = newNode;
                        newNode.right = parent;
                    }
                    if (right1) {
                        grandpa.left = newNode;
                        newNode.left = parent;
                    }
                    newNode.parent = grandpa;
                    parent.parent = newNode;

                    TreeNode newNil = new TreeNode(BLACK, null, parent, true);
                    parent.left = newNil;
                    parent.right = newNil;

                    op(parent.data, newNode, grandpa);


                }

            }


        }


    }

    public void op(int data, TreeNode parent, TreeNode grandpa) {
        boolean right1 = data > parent.data;
        boolean right2 = parent.data > grandpa.data;
        boolean direct1 = right1 && right2;
        boolean left1 = data < parent.data;
        boolean left2 = parent.data < grandpa.data;
        boolean direct2 = left1 && left2;

        boolean tmp = grandpa.color;
        grandpa.color = parent.color;
        parent.color = tmp;

        TreeNode grandpaPa = grandpa.parent;
        if (parent.data < grandpaPa.data) {
            grandpaPa.left = parent;
        } else {
            grandpaPa.right = parent;
        }
        parent.parent = grandpaPa;

        if (direct1) {
            parent.left = grandpa;
            grandpa.parent = parent;
        } else if (direct2) {
            parent.right = grandpa;
            grandpa.parent = parent;
        }
        grandpa.left = new TreeNode(BLACK, null, grandpa, true);
        grandpa.right = new TreeNode(BLACK, null, grandpa, true);
    }


    /**
     *  删除
     * 1.单个红节点  直接删除
     * 2.单个黑节点
     *      2.1兄黑
     *         2.1.1 对侄红 (指方向相反的侄节点)
     *               父兄交替旋转、然后按父红兄弟黑换色 (最后一步的换色,父红两兄弟黑,是按交替旋转之后的关系处理。)
     *         2.1.2 顺侄红(指方向相同的侄节点)
     *              兄侄交替旋转,并调换颜色,就会变成对侄红,然后按2.1.1处理
     *         2.1.3 双侄黑
     *              兄变红父变黑,如果父本身就是黑,就以父亲角度按情况2处理
     *
     *      2.2 兄红
     *              父兄交替旋转,并调换颜色,新的兄节点将变黑,在按2.1处理
     * 3.带有一个子节点(当一个节点只有一个子节点时(空叶子除外),该节点必定是黑节点,其子节点必定是红色)
     *      用红子节点值替换,然后直接删除红子节点
     * 4.带有两个子节点
     *      找到左子树中最靠右的子节点,用该节点值替换,并删除该节点按情况1,2,3处理(左子树中最大的值,也是离其最近的值)
     *
     * @param data 数据
     */
    public void delete(int data) {
        TreeNode find = find(data);
        if (find.isLeaf){
            System.out.println("没有找到");
            return;
        }
        //1.单个红节点
        if (!find.isBlack()){
            if (find.left.isLeaf&&find.right.isLeaf){
                TreeNode parent = find.parent;
                TreeNode nil=new TreeNode(BLACK,null,parent,true);
                if (data<parent.data){
                    parent.left=nil;
                }else {
                    parent.right=nil;
                }
            }else {
                //4.带有两个子节点
                TreeNode replace = findReplace(find);
                delete(replace.data);
                find.data= replace.data;
            }

        }else {
            //3.带有一个子节点,用红子节点值替换,然后直接删除红子节点
            if (find.left.isLeaf&&!find.right.isBlack()){
                find.data=find.right.data;
                find.right= new TreeNode(BLACK,null,find,true);
            }else if (find.right.isLeaf&&!find.left.isBlack()){
                find.data=find.left.data;
                find.left= new TreeNode(BLACK,null,find,true);
            } else if (!find.left.isLeaf&&!find.right.isLeaf){//4.带有两个子节点
                TreeNode replace = findReplace(find);
                delete(replace.data);
                find.data= replace.data;
            }else {//2.单个黑结点
                TreeNode parent=find.parent;
                TreeNode brother=parent.left!=find?parent.left:parent.right;
                boolean left=find.data<parent.data;

                //对侄
                TreeNode duiNephew=left?brother.right:brother.left;
                //顺侄
                TreeNode shunNephew=left?brother.left:brother.right;

                if (brother.isBlack()){//2.1兄黑
                    //2.1.1 对侄红
                    if (!duiNephew.isBlack()){
                        //父兄交替旋转
                        TreeNode grandpa=parent.parent;
                        if (brother.data<grandpa.data){
                            grandpa.left=brother;
                        }else {
                            grandpa.right=brother;
                        }
                        brother.parent=grandpa;

                        if (left){
                            brother.left=parent;
                        }else {
                            brother.right=parent;
                        }
                        parent.parent=brother;

                        TreeNode nil=new TreeNode(BLACK,null,parent,true);
                        parent.left=nil;
                        parent.right=nil;

                        //并调换颜色
                        brother.color=RED;
                        duiNephew.color=BLACK;
                        parent.color=BLACK;


                    } else if (!shunNephew.isBlack()){//2.1.2 顺侄红
                        //兄侄交替旋转,并调换颜色,就会变成对侄红,
                        if (brother.data< parent.data){
                            parent.left=shunNephew;
                            shunNephew.left=brother;
                        }else {
                            parent.right=shunNephew;
                            shunNephew.right=brother;

                        }
                        shunNephew.parent=parent;
                        brother.parent=shunNephew;

                        TreeNode nil=new TreeNode(BLACK,null,brother,true);
                        brother.left=nil;
                        brother.right=nil;

                        brother.color=RED;
                        shunNephew.color=BLACK;

                        delete(data);

                    } else if (brother.left.isBlack()&&brother.right.isBlack()){//2.1.3 双侄黑
                        //兄变红父变黑,如果父本身就是黑,就以父亲角度按情况2处理
                        brother.color=RED;
                        parent.color=BLACK;
                        TreeNode nil=new TreeNode(BLACK,null,parent,true);
                        if (find.data< parent.data){
                            parent.left=nil;
                        }else {
                            parent.right=nil;
                        }
                    }

                }else {//2.2兄红
                    //父兄交替旋转,并调换颜色,新的兄节点将变黑,在按2.1处理
                    TreeNode grandpa=parent.parent;
                    if (brother.data<grandpa.data){
                        grandpa.left=brother;
                    }else {
                        grandpa.right=brother;
                    }
                    brother.parent=grandpa;

                    TreeNode tmp;
                    if (data<parent.data){
                        tmp=brother.left;
                        brother.left=parent;
                    }else {
                        tmp=brother.right;
                        brother.right=parent;

                    }
                    parent.parent=brother;


                    if (data<parent.data){
                        parent.left=find;
                        parent.right=tmp;
                    }else {
                        parent.left=tmp;
                        parent.right=find;
                    }

                    brother.color=BLACK;
                    parent.color=RED;

                    delete(data);

                }
            }

        }


    }

    /**
     * 查找
     *
     * @param data 数据
     * @return 查找结点,如果差不到就会返回叶子结点
     */
    public TreeNode find(int data) {
        TreeNode find = root;

        while (!find.isLeaf) {
            if (data < find.data) {
                find = find.left;
            } else if(find.data.equals(data)){
                return find;
            } else if (data > find.data) {
                find = find.right;
            }
        }

        return find;
    }

    /**
     * 查找替代的结点
     * 中序遍历线索树的直接前驱结点
     * @param node 删除的结点
     * @return 查找替代
     */
    public TreeNode findReplace(TreeNode node) {
        TreeNode left = node.left;
        while (!left.isLeaf) {
            left=left.right;
        }
        return left.parent;
    }

    //中序遍历
    public void inorder(TreeNode root) {
        if (root == null) {
            return;
        }

        if (!root.left.isLeaf) {
            inorder(root.left);
        }
        System.out.println(root);
        if (!root.right.isLeaf) {
            inorder(root.right);
        }
    }

    //层序遍历
    public void levelOrder(TreeNode root) {
        if (root == null) {
            return;
        }

        Queue<TreeNode> queue = new LinkedList<>();
        queue.add(root);

        while (!queue.isEmpty()) {
            int size = queue.size();
            for (int i = 0; i < size; i++) {
                TreeNode cur = queue.poll();
                System.out.print(cur + "\t");
                if (cur.left != null) {
                    queue.add(cur.left);
                }
                if (cur.right != null) {
                    queue.add(cur.right);
                }
            }
            System.out.println();

        }
    }

    private static int blackHeight = -1;

    //判断是否是有效的红黑树
    public static boolean isValidRedBlackTree(TreeNode root) {
        if (root == null) {
            return true;
        }

        // 检查根节点是否是黑色
        if (root.color != BLACK) {
            return false;
        }

        // 计算黑高度,并检查红黑平衡
        blackHeight = -1;
        if (!checkBlackBalance(root, 0)) {
            return false;
        }

        // 递归检查每个节点
        return isValidRedBlackSubtree(root);
    }

    private static boolean checkBlackBalance(TreeNode node, int currentBlackHeight) {
        if (node.isLeaf) {
            if (blackHeight == -1) {
                blackHeight = currentBlackHeight;
                return true;
            } else {
                return currentBlackHeight == blackHeight;
            }
        }

        if (node.color == BLACK) {
            currentBlackHeight++;
        }

        return checkBlackBalance(node.left, currentBlackHeight) && checkBlackBalance(node.right, currentBlackHeight);
    }

    private static boolean isValidRedBlackSubtree(TreeNode node) {
        if (node == null) {
            return true;
        }

        // 检查红黑树性质
        if (node.color == RED) {
            if ((node.left != null && node.left.color != BLACK) || (node.right != null && node.right.color != BLACK)) {
                return false;
            }
        }

        // 递归检查左右子树
        return isValidRedBlackSubtree(node.left) && isValidRedBlackSubtree(node.right);
    }

    
    public static void main(String[] args) {
        RedBlackTree tree = new RedBlackTree();
//        testAdd(tree);

        initData(tree);

        testDelete(tree);

        tree.levelOrder(tree.root);
        System.out.println(isValidRedBlackTree(tree.root));

    }

    private static void testDelete(RedBlackTree tree) {
        tree.delete(262);//1
        tree.delete(818);//1

        tree.delete(705);//3
        tree.delete(369);//3

        tree.add(346);
        tree.delete(430);//4

        tree.delete(594);//2.1.1

        tree.add(570);
        tree.delete(485);//2.1.1


        tree.add(565);
        tree.delete(499);//2.1.2

        tree.add(335);
        tree.delete(345);//2.1.2
        tree.delete(559);//2.1.3

        tree.delete(570);
        tree.delete(565);//2.2

        tree.delete(37);
        tree.delete(131);
        tree.delete(95);//2.2
    }

    private static void initData(RedBlackTree tree) {
        int[] nums={430,261,636,
        95,344,559,822,
        37,131,330,369,499,594,705,981,
        262,345,485,664,818};

        for (int i = 0; i < nums.length; i++) {
            tree.add(nums[i]);
        }

//        tree.inorder(tree.root);
//        tree.levelOrder(tree.root);
//        System.out.println(isValidRedBlackTree(tree.root));
    }


    private static void testAdd(RedBlackTree tree) {
        tree.add(157);//0
        tree.add(12);//1
        tree.add(200);//1

        tree.add(250);//2
        tree.add(260);//3
        tree.add(220);//2
        tree.add(210);//4

        tree.add(11);//1
        tree.add(10);//3
        tree.add(7);//2
        tree.add(9);//4


        tree.inorder(tree.root);
//        tree.levelOrder(tree.root);

        System.out.println(isValidRedBlackTree(tree.root));
    }

}

//结点
class TreeNode {


    //true是黑色,false是红色
    boolean color;

    //数据
    Integer data;


    TreeNode left;
    TreeNode right;

    private static final boolean RED = false;
    private static final boolean BLACK = true;

    //是否是叶子结点
    boolean isLeaf;

    //方便实现
    TreeNode parent;

    public TreeNode() {
    }

    public TreeNode(int data) {
        this.data = data;

    }

    public TreeNode(boolean color, Integer data) {
        this.color = color;
        this.data = data;

    }

    public TreeNode(boolean color, Integer data, TreeNode parent) {
        this.color = color;
        this.data = data;
        this.parent = parent;
    }

    public TreeNode(boolean color, Integer data, TreeNode parent, boolean isLeaf) {
        this.color = color;
        this.data = data;
        this.parent = parent;
        this.isLeaf = isLeaf;
    }

//    public TreeNode(Integer data,TreeNode left, TreeNode right) {
//        this.data = data;
//        this.left = left;
//        this.right = right;
//    }


    public boolean isBlack() {
        return color == BLACK;
    }

    @Override
    public String toString() {
        return "TreeNode{" +
                "color=" + color +
                ", data=" + data +
                '}';
    }

}

最后

2024-3-30 23:05:13

写了一天

迎着日光月光星光,直面风霜雨霜雪霜。

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

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

相关文章

相机标定学习记录

相机标定是计算机视觉和机器视觉领域中的一项基本技术&#xff0c;它的主要目的是通过获取相机的内部参数&#xff08;内参&#xff09;和外部参数&#xff08;外参&#xff09;&#xff0c;以及镜头畸变参数&#xff0c;建立起现实世界中的点与相机成像平面上对应像素点之间准…

WPF中继承ItemsControl子类控件数据模板获取选中属性

需求场景 列表类控件&#xff0c;如 ListBox、ListView、DataGrid等。显示的行数据中&#xff0c;部分内容依靠选中时触发控制&#xff0c;例如选中行时行记录复选&#xff0c;部分列内容控制显隐。 案例源码以ListView 为例。 Xaml 部分 <ListView ItemsSource"{Bi…

【Node.js】图片验证码识别

现在越来越多的网站采取图片验证码&#xff0c;防止机器恶意向服务端发送请求。但是常规的图片验证码也不是非常安全了。有非常多第三方库可以对图片上的数字文字等进行识别。 代码实现 首先安装依赖&#xff1a; npm install node-native-ocrnpm&#xff1a;(node-native-oc…

HCIA网络基础11-静态路由

文章目录 自治系统LAN和广播域路由选择路由表数据包转发最长匹配原则路由优先级路由度量静态路由配置静态路由负载分担路由备份缺省路由 以太网交换机工作在数据链路层&#xff0c;用于在网络内进行数据转发。而企业网络的拓扑结构一般会比较复杂&#xff0c;不同的部门&#x…

Mistral 7B v0.2 基础模型开源,大模型微调实践来了

Mistral AI在3月24日突然发布并开源了 Mistral 7B v0.2模型&#xff0c;有如下几个特点&#xff1a; 和上一代Mistral v0.1版本相比&#xff0c;上下文窗口长度从8k提升到32k&#xff0c;上下文窗口&#xff08;context window&#xff09;是指语言模型在进行预测或生成文本时&…

设计模式6--抽象工厂模式

定义 案例一 案例二 优缺点

重新温习广软puthon爬虫技术。

下面是我不断试错的一个过程&#xff0c;好多知识点全忘记了&#xff0c;只能不断调实例&#xff0c;不断优化&#xff0c;重构&#xff0c;实现自己的需求。下面是我的运行截图。还是导包的问题。 个人感觉关键的还是这几部&#xff0c;被划了下划线的&#xff0c;存在问题&a…

最优算法100例之17- 环形连续子数组的最大和

专栏主页:计算机专业基础知识总结(适用于期末复习考研刷题求职面试)系列文章https://blog.csdn.net/seeker1994/category_12585732.html 题目描述 给定一个长度为 nn 的环形整数数组,请你求出该数组的 非空 连续子数组 的最大可能和 。 环形数组 意味着数组的末端将会与开…

设计模式9--单例模式

定义 案例一 案例二 优缺点

Windows中忘记MySQL ROOT密码的解决方法

在需要ROOT身份登录MySQL但又忘记密码时&#xff0c;可以先已管理员身份运行cmd命令窗口,输入以下命令停止MySQL服务 net stop mysql 随后cd到MySQL安装目录下的bin目录下 D: //我的安装在D盘 cd D:\MySQL\bin 使用跳过权限验证的方式起启动MySQL mysqld --console --skip-g…

从零开始机器学习(机器学习 监督学习之线性回归 损失函数及可视化 梯度下降 线性回归的平方误差损失函数 lab实验)

文章目录 机器学习定义监督学习之线性回归损失函数及可视化梯度下降线性回归的平方误差损失函数lab实验 机器学习定义 机器学习就是机器通过不断训练数据集从逐渐知道正确的结果 机器学习包括监督学习和非监督学习 监督学习&#xff1a;需要输入数据和结果数据来不断训练学习…

大数据面试专题 -- kafka

1、什么是消息队列&#xff1f; 是一个用于存放数据的组件&#xff0c;用于系统之间或者是模块之间的消息传递。 2、消息队列的应用场景&#xff1f; 主要是用于模块之间的解耦合、异步处理、日志处理、流量削峰 3、什么是kafka&#xff1f; kafka是一种基于订阅发布模式的…

Linux 著名的sudo、su是什么?怎么用?

一、su 什么是su&#xff1f; su命令&#xff08;简称是&#xff1a;substitute 或者 switch user &#xff09;用于切换到另一个用户&#xff0c;没有指定用户名&#xff0c;则默认情况下将以root用户登录。 为了向后兼容&#xff0c;su默认不改变当前目录&#xff0c;只设…

专升本-云计算

被誉为第三次信息技术革命 什么是云计算&#xff1f; 云计算是一种商业的计算模式&#xff0c;它将任务分布在大量计算机构成的资源池上&#xff0c;用户可以按需通过网络存储空间&#xff0c;计算能力和信息等服务 云计算的产生和发展&#xff1a; 起源&#xff1a;上世纪6…

【力扣刷题日记】1173.即时食物配送I

前言 练习sql语句&#xff0c;所有题目来自于力扣&#xff08;https://leetcode.cn/problemset/database/&#xff09;的免费数据库练习题。 今日题目&#xff1a; 1173.即时食物配送I 表&#xff1a;Delivery 列名类型delivery_idintcustomer_idintorder_datedatecustomer…

Qt使用opencv打开摄像头

1.效果图 2.代码 #include "widget.h"#include <QApplication>#include <opencv2/core/core.hpp> #include <opencv2/highgui/highgui.hpp> #include <opencv2/imgproc/imgproc.hpp>#include <QImage> #include <QLabel> #incl…

实现 Element UI el-table 树形数据的懒加载

当面对大量数据时&#xff0c;一次性加载所有数据可能会导致性能问题。为了解决这一问题&#xff0c;我们可以实现树形数据的懒加载。本文将介绍如何在使用 Element UI 的 Vue 应用中为 el-table 组件的树形数据添加懒加载功能。 懒加载的基本概念 懒加载是一种优化网页或应用…

http和https的工作原理是什么?

HTTP&#xff08;HyperText Transfer Protocol&#xff09;和HTTPS&#xff08;HyperText Transfer Protocol Secure&#xff09;是两种用于在互联网上传输数据的主要协议&#xff0c;它们均用于在客户端&#xff08;通常是Web浏览器&#xff09;与服务器之间交换信息。尽管它们…

【自动装箱以及包装类的缓存】⭐️通过具体案例看下每种包装类的不同结果

目录 前言 一、自动装箱与拆箱&#xff08;以 Integer 包装类为例&#xff09; 二、再来看看几个示例 ​编辑三、Double ,Float 类型亦是如此吗&#xff1f; 前言 小伙伴们大家好&#xff0c;日常使用业务层方面的代码居多&#xff0c;但也不可忘了基本的一些代码格式以及原…

QA:ubuntu22.04.4桌面版虚拟机鼠标丢失的解决方法

前言 在Windows11中的VMWare Workstation17.5.1 Pro上安装了Ubuntu22.04.4&#xff0c;在使用过程中发现&#xff0c;VM虚拟机的鼠标的光标会突然消失&#xff0c;但鼠标其他正常&#xff0c;就是光标不见了&#xff0c;下面是解决办法。 内容 如下图&#xff0c;输入mouse&a…