手写红黑树【数据结构】

news2025/1/18 13:50:35

手写红黑树【数据结构】

  • 前言
  • 版权
  • 推荐
  • 手写红黑树
    • 一、理论知识
      • 红黑树的特征
      • 增加
      • 删除
    • 二、手写代码
      • 初始-树结点
      • 初始-红黑树
      • 初始-遍历
      • 初始-判断红黑树是否有效
      • 查找
      • 增加-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

写了一天

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

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

java Web洗衣店管理系统用eclipse定制开发mysql数据库BS模式java编程jdbc

一、源码特点 JSP 洗衣店管理系统是一套完善的web设计系统&#xff0c;对理解JSP java 编程开发语言有帮助&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/S模式开发。开发环境为TOMCAT7.0,eclipse开发&#xff0c;数据库为Mysql5.0&#xff0c;使用…

优化选址问题 | 基于帝国企鹅算法求解工厂-中心-需求点三级选址问题含Matlab源码

目录 问题代码问题 "帝国企鹅算法"并不是一个广为人知的优化算法,可能是一个特定领域或者特定情境下提出的方法。不过,对于工厂-中心-需求点三级选址问题,它可能是一种启发式优化方法,用于在多个候选位置中选择最优的工厂、中心和需求点位置。 这类问题通常涉及…

IPv4地址

IP v4 由32位二进制构成、可以用点分十进制表示。 例如&#xff1a;192.168.1.1 11000000101010000000000100000001 由网络位和主机位组成。为了区分网络位和主机位&#xff0c;需要用子网掩码&#xff0c;子网掩码也是由32位二进制构成&#xff0c;连续的1对应网络位&#…

PHP的定时任务框架的taskPHP3.0学习记录2(环境要求、配置Redis、crontab执行时间语法、命令操作以及Screen全屏窗口管理器)

环境要求 php版本> 5.5开启socket扩展开启pdo扩展开启shmop扩展 echo <pre>; echo --; $requiredVersion 5.6.0; $currentVersion phpversion(); if (version_compare($currentVersion, $requiredVersion, >)) {echo "1.PHP版本满足要求&#xff0c;当前版…

【笔记】动⼿学深度学习(花书)|| Aston Zhang Mu Li Zachary C. LiptonAlexander J. Smola

系列文章目录 提示&#xff1a;这里可以添加系列文章的所有文章的目录&#xff0c;目录需要自己手动添加 前言 第一章 深度学习简介 第二章 P 提示&#xff1a;写完文章后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 系列文章目录前言本书…

python实战之异常篇

一. try-except语句 二. 多个except代码块 三. 多重异常捕获 四. try-except语句嵌套 五. 使用finally代码块释放资源 六. 自定义异常类 七. 手动引发异常 使用关键字 raise 手动抛异常

Android RecyclerView canScrollVertically方向与返回值,Kotlin

Android RecyclerView canScrollVertically方向与返回值&#xff0c;Kotlin import android.os.Bundle import android.util.Log import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.widget.TextView import androidx…

Node | Node.js 版本升级

目录 Step1&#xff1a;下载 Step2&#xff1a;安装 Step3&#xff1a;换源 发现其他博客说的 n 模块不太行&#xff0c;所以老老实实地手动安装 Step1&#xff1a;下载 Node 中文官网&#xff1a;https://nodejs.cn/download 点击后&#xff0c;将会下载得到一个 .msi 文件…

Go-React做一个todolist(服务端)【一】项目初始化

后端仓库地址 地址 项目依赖 # gin go get -u github.com/gin-gonic/gin # viper日志 go get -u github.com/spf13/viper # 数据库和gorm go get -u gorm.io/driver/mysql go get -u gorm.io/gorm # uuid go get -u github.com/google/uuid # token go get -u github.com/go…

蓝桥杯2015年第十三届省赛真题-三羊献瑞

一、题目 观察下面的加法算式&#xff1a; 祥 瑞 生 辉 三 羊 献 瑞 ---------------------- 三 羊 生 瑞 气 (如果有对齐问题&#xff0c;可以参看【图1】) 其中&#xff0c;相同的汉字代表相同的数字&#xff0c;不同的汉字代表不同的数字。 请你填写“三羊献瑞”所…

C语言-编译和链接

目录 1.前言2.编译2.1预处理&#xff08;预编译&#xff09;2.1.1 #define 定义常量2.1.2 #define 定义宏2.1.3带有副作用的宏参数2.1.4宏替换规则2.1.5 #和##2.1.5.1 #运算符2.1.5.2 ## 运算符 2.1.6 命名约定2.1.7 #undef2.1.8 条件编译2.1.9 头文件的包含2.1.9.1 本地文件包…

电商系列之取消订单

> 插&#xff1a;AI时代&#xff0c;程序员或多或少要了解些人工智能&#xff0c;前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到网站。 坚持不懈&#xff0c;越努力越幸运&#xff0c;大家…

每日面经分享(Spring Boot: part2 DAO层)

1. Spring Boot DAO层的作用 a. 封装数据访问逻辑&#xff1a;DAO层的主要责任是封装与数据访问相关的逻辑。负责处理与数据库的交互&#xff0c;包括数据的增删改查等操作。通过将数据访问逻辑统一封装在DAO层中&#xff0c;可以提高代码的可维护性和可重用性。 b. 解耦业务逻…

java Web 疫苗预约管理系统用eclipse定制开发mysql数据库BS模式java编程jdbc

一、源码特点 JSP 疫苗预约管理系统是一套完善的web设计系统&#xff0c;对理解JSP java 编程开发语言有帮助&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/S模式开发。开发环境为TOMCAT7.0,eclipse开发&#xff0c;数据库为Mysql5.0&#xff0c;使…

蓝桥集训之游戏

蓝桥集训之游戏 核心思想&#xff1a;博弈论 区间dp 设玩家1的最优解为A 玩家2的最优解为B 1的目标就是使A-B最大 2的目标就是使B-A最大 当玩家1取L左端点时 右边子区间结果就是玩家2的最优解B-A 即当前结果为w[L] – (B-A) 当玩家1取R右端点时 左边子区间结果就是玩家2的最…

<TensorFlow学习使用P1>——《TensorFlow教程》

一、TensorFlow概述 前言&#xff1a; 本文中一些TensorFlow综合案例的代码逻辑一般正常&#xff0c;在本地均可运行。如有代码复现运行失败&#xff0c;原因如下&#xff1a; &#xff08;1&#xff09;运行环境配置可能有误。 &#xff08;2&#xff09;由于一些数据集存储空…

算法学习——LeetCode力扣图论篇2

算法学习——LeetCode力扣图论篇2 1020. 飞地的数量 1020. 飞地的数量 - 力扣&#xff08;LeetCode&#xff09; 描述 给你一个大小为 m x n 的二进制矩阵 grid &#xff0c;其中 0 表示一个海洋单元格、1 表示一个陆地单元格。 一次 移动 是指从一个陆地单元格走到另一个相…

实施阶段(2024年3月)

本次探究的项目化学习主题为&#xff1a;校内订餐系统的分析与设计 【主题情境说明】 社会大背景&#xff1a;国家颁发《“健康中国2030”规划纲要》&#xff0c;尤其关注青少年儿童膳食质量&#xff0c;明确提出要加强孩子食育教育&#xff0c;家庭、学校和社会需要共建健康…

Linux系统---如何理解Linux中的文件系统

顾得泉&#xff1a;个人主页 个人专栏&#xff1a;《Linux操作系统》 《C从入门到精通》 《LeedCode刷题》 键盘敲烂&#xff0c;年薪百万&#xff01; 一、理解文件系统 1.ls与stat 我们使用ls -l的时候看到的除了看到文件名&#xff0c;还看到了文件元数据。 每行包含7列…

【一站式学会Kotlin】第一节 kotlin 介绍

作者介绍&#xff1a; 百度资深Android工程师T6&#xff0c;在百度任职7年半。 目前&#xff1a;成立赵小灰代码工作室&#xff0c;欢迎大家找我开发Android、微信小程序、鸿蒙项目。 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默。给大家…