手写红黑树【数据结构】

手写红黑树【数据结构】

前言

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。

初始-树结点

java 复制代码
//结点
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 +
                '}';
    }

}

初始-红黑树

java 复制代码
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);

    }

}

初始-遍历

java 复制代码
 //中序遍历
    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();

        }
    }

 

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

java 复制代码
    //判断是否是有效的红黑树
    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);
    }

查找

java 复制代码
 	/**
     * 查找
     *
     * @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.父为黑,直接插入

java 复制代码
   /**
     * 增加
     * 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. 父叔为红,颜色调换

java 复制代码
    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. 父红叔黑,颜色调换,再移动

java 复制代码
				//跳转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种情况

java 复制代码
					//跳转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);

测试增加

java 复制代码
    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 初始化数据

java 复制代码
    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.单个红节点 直接删除

java 复制代码
 /**
     *  删除
     * 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.带有一个子节点

java 复制代码
			//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.带有两个子节点

java 复制代码
			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兄黑,对侄红
java 复制代码
  				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兄黑,顺侄红
java 复制代码
                        //兄侄交替旋转,并调换颜色,就会变成对侄红,
                        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 兄黑,双侄黑
java 复制代码
                        //兄变红父变黑,如果父本身就是黑,就以父亲角度按情况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兄红

java 复制代码
					//父兄交替旋转,并调换颜色,新的兄节点将变黑,在按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);

测试删除

java 复制代码
    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
    }

附录源码

java 复制代码
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

写了一天

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

相关推荐
嵌入式AI的盲1 小时前
数组指针和指针数组
数据结构·算法
reyas3 小时前
B树系列解析
数据结构·b树
Indigo_code3 小时前
【数据结构】【顺序表算法】 删除特定值
数据结构·算法
阿史大杯茶4 小时前
Codeforces Round 976 (Div. 2 ABCDE题)视频讲解
数据结构·c++·算法
不穿格子衬衫5 小时前
常用排序算法(下)
c语言·开发语言·数据结构·算法·排序算法·八大排序
aqua35357423585 小时前
蓝桥杯-财务管理
java·c语言·数据结构·算法
韬. .6 小时前
树和二叉树知识点大全及相关题目练习【数据结构】
数据结构·学习·算法
野草y6 小时前
数据结构(7.4_1)——B树
数据结构·b树
Word码6 小时前
数据结构:栈和队列
c语言·开发语言·数据结构·经验分享·笔记·算法
代码雕刻家6 小时前
数据结构-3.10.队列的应用
服务器·数据结构