JavaDS —— 红黑树

news2025/1/10 3:26:03

前言

还是一样,这里的红黑树重点讲述插入代码的实现,如果对红黑树的删除感兴趣,可以去翻阅其他资料。
在数据结构专栏中已经对 AVL 树的旋转调整做了分析和讲解,这里红黑树也会使用到旋转调整的代码,就不讲述旋转代码的实现,大家如果对旋转不熟悉,可以打开这个文章 JavaDS —— AVL 树

概念

红黑树是一种二叉搜索树每一个结点增加了一个存储位置来表示结点的颜色(RED 红色,BLACK 黑色),通过对任何一条从根结点到叶子的路径上各个结点的着色方式的限制,红黑树确保没有一条路径会比其他路劲长出两倍,这样就保证红黑树是接近平衡的

在这里插入图片描述

性质

1.根节点是黑色的
2.每个结点不是红色就是黑色

3.如果一个节点是红色的,则它的两个孩子结点是黑色的【没有2个连续的红色节点

注意是不会存在两个连续的红色结点,但是可以存在连续的黑色结点

4.对于每个结点,从该结点到其所有后代叶结点的简单路径上,均包含相同数目的黑色结点

这个路径的终点条件是最后一个非空结点的左右孩子为空

5.每个叶子结点都是黑色的(此处的叶子结点指的是空结点)

分析

为什么红黑树能保证最长路劲的结点个数一定不会超过最短路径的结点个数的两倍?

首先由于红黑树的性质三,我们可以知道不会存在两个连续的红色结点,但是黑色结点可以连续存在,并且由于性质四每条路劲上的黑色结点的数量是相同的,所以我们可以这样推断出,最短的路径上所有的结点都是黑色的,最长的路径上结点是红黑交替出现的。
在这里插入图片描述

例如上图中 root 到 A1 这条路径就是最短路径, root 到 B1 这条路径就是最长路径,最长路径确实最多是最短路劲的两倍

数学角度分析,因为最短路径的黑色结点都是连在一起的,加上最短路径一共有 x 个黑色结点,x 也是最短路径长度,最长路径就是每一个黑色结点接上一个红色结点,那么最长路径的长度为 2x
其实现实情况中不太可能出现一条路径上全是黑色结点,或多或少都会带一些红色结点,这也就导致最短路径其实比 x 要大,但是最长路径最长也就是 2x ,所以红黑树能保证最长路劲的结点个数一定不会超过最短路径的结点个数的两倍


查找的时间复杂度为 O(log N)

假设一颗红黑树一共有N 个结点,红黑树最极端的情况就是全是黑色结点,设黑色结点个数为 x,即x = N,那么极端情况下查找的时间复杂度为O(log x) = O(log N),这也是最快的时间复杂度
红黑树假设能实现红黑交替,也就是全部路径都处于最长形态,由于是二叉树,先除去根节点和根节点左右两个红色结点,剩下的结点总数为 2(x-1) ,总结点数为 2(x-1) + 3 = 2x +1,则查找的时间复杂度为O(log N) = O(log (2x + 1)) = O(log x)

结点定义

因为红黑树会涉及到旋转,所以要存储父亲结点(还有一个原因是判断颜色,下面会提到)
还有一个存储位置来存放颜色

这里要注意构造方法中颜色应该先定义为红色,因为红黑树的性质四中每条路径的黑色结点数量是相同的,如果插入的是黑色结点,那和它同一层的也要插入黑色结点依此来维持红黑树的性质,所以为了简化难题,我们就将插入的结点先设置成红色,这样就能保证红黑树的性质四不被破坏。

public class TreeNode {
    int val;
    TreeNode left;
    TreeNode right;
    TreeNode parent;
    Color color;

    public TreeNode(int val) {
        this.val = val;
        this.color = Color.RED;
    }
}

颜色使用枚举来定义

public enum Color {
    RED,BLACK
}

插入实现

开始时就和二叉搜索树的插入结点一样,要注意根节点的黑色是的,由于设置插入的结点都是红色,所以直接插入到根节点的时候需要调成黑色。

    public TreeNode root;

    public boolean insert(int val) {
        TreeNode node = new TreeNode(val);
        if(root == null) {
            root = node;
            root.color = Color.BLACK;
            return true;
        }

        TreeNode cur = root;
        TreeNode parent = null;

        while(cur != null) {
            if(cur.val == val) {
                //数值相同的结点不能插入。
                return false;
            } else if(cur.val > val) {
                parent = cur;
                cur = cur.left;
            } else {
                parent = cur;
                cur = cur.right;
            }
        }

        if(parent.val > val) {
            parent.left = node;
        } else {
            parent.right = node;
        }
        node.parent = parent;

        cur = node;

调整颜色

我们在插入结点后,要验证一下结点是否会破坏红黑树的性质,那会破坏什么性质呢?
很简单,你插入的是红结点,就可能会出现两个连续的结点是红色的情况

如何判断?
也很简单,我们有一个父亲结点的引用域,我们只要查看这个结点的父亲结点是不是也是红色,如果是就需要进行调整,在此之前,还要避免出现空指针异常,所以要先判断父亲结点是否为空


什么时候需要调整颜色?
答: 当插入的结点的父亲结点也是红色就需要调整.

那这时候会有多少种情况?
插入的结点有四种情况,可以类比AVL 树,左左型,右右型,左右型,右左型,此时 其父亲结点的兄弟结点有两种情况存在或者不存在,那么一共有 8 中情况。
但是有一点可以确定的是父亲结点的父亲结点(grandparent)一定是黑色的,那我们可以这样子,先讨论简单的情况,就是兄弟结点存在,如果兄弟结点存在就一定是红色的,现在就分成了两大类父亲结点是grandparent 的左孩子还是右孩子,我们先这样子分,然后在细分。

  
            TreeNode grandparent = parent.parent; //一定存在

            if(parent == grandparent.left) {
 
            } else {
                //parent == grandparent.right
        

大类一

假设父亲结点是grandparent 的左孩子,并且兄弟结点存在且为红色:
在这里插入图片描述
因为要调整颜色, 必须保证每条路径上的黑色结点数据是相同的,这时候无论cur 的位置是哪里,将 grandparent 变成红色,然后将 parent 变成黑色,uncle 变成黑色就可以,由于 grandparent 变成红色,我们不确定 grandparent 有没有 父亲结点,如果有并且也是红色那就需要继续调整,所以我们需要将 cur 置为 grandparent ,再将 parent 置为 现在的 cur 的父亲结点。
在这里插入图片描述

看到这里想必大家知道要使用循环了,这里可以类比AVL 树的向上调整。

while(parent != null && parent.color == Color.RED) {
	TreeNode grandparent = parent.parent; //一定存在
}

如果经过循环,根节点会不会被调成红色?

答案是会的,所以循环的最后,我们要确保根节点调成黑色,这也就是一行代码的事情。


在这个大前提下:我们还有一种情况就是 兄弟结点不存在:这也有两种情况:
在这里插入图片描述

如果 cur 位于 parent 的 右孩子:我们需要先对parent 进行 左旋,然后是不是就会变成类似右边的情况,这时候我们交换 parent 和 cur ,这样完全就是右边的情况。然后我们在处理右边的情况,先对grandparent 进行右旋,然后将 grandparent 调成红色,将 parent 调成黑色,这样就不会破坏路径上的黑色结点数目相同这一个性质。
在这里插入图片描述
这时候红黑树调整完成,直接退出循环即可。


有没有可能 uncle 存在,并且 uncle 还是黑色的?

不要认为这违反了所有的路径上黑色结点的数目是相同的性质,你可能会认为这明显少一个黑色结点,其实没有少,因为你认为少的黑色结点其实在 cur 的下面,这里给你一个例图:
在这里插入图片描述

其实有可能的,在第一个大类的情况下的第一个小情况,我们是不是要准备使用循环:将 cur 置为 grandparent ,再将 parent 置为 现在的 cur 的父亲结点,这时候就又可能会出现 uncle 存在且 uncle 是黑色的情况:
在这里插入图片描述
这时候也很好处理,左边的情况,先对 parent 进行左旋,然后交换 cur 和 parent 两个结点,最后就变成右边的情况。最后就是处理右边的情况:对 grandparent 进行右旋,将 grandparent 置为红色,再将 parent 置为黑色。
在这里插入图片描述

上面这些话是不是很耳熟,是不是和上面的 uncle 结点不存在的处理方法一模一样,这样我们就合并同类项,将它们合并成同一种情况uncle 结点不存在或者uncle 结点为黑色

这种合并之后的过程图如下:
在这里插入图片描述

在这里插入图片描述

			else {
                    // uncle 不存在或者 uncle 是 黑色结点,那就需要进行旋转调整

                    if(cur == parent.right) {
                        //先对 parent 进行左旋,然后交换 cur 与 parent ,之后就会变成下面的代码的情况
                        rotateLeft(parent);
                        TreeNode tmp = parent;
                        parent = cur;
                        cur = parent;
                    }

                    // cur 是 parent 的右孩子,对grandparent 进行右旋
                    rotateRight(grandparent);
                    parent.color = Color.BLACK;
                    grandparent.color = Color.RED;

                    //红黑树已经调整完成,直接退出循环,不用继续调整颜色
                    break;
                }

大类二

父亲结点是grandparent 的右孩子,并且 uncle 结点存在且为红色,还是一样的处理方法,将 uncle 结点 和 parent 结点调成黑色,再将 grandparent 调成红色,然后 cur 置为 grandparent ,parent 置为现在 cur 的父亲结点,向上调整颜色即可。

这其实就是大类一的相反情况,只需要修改一些代码即可,这里就给出例图,不进行详细的文字说明。

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

            } else {
                //parent == grandparent.right
                TreeNode uncle = grandparent.left;
                if(uncle != null && uncle.color == Color.RED) {
                    uncle.color = Color.BLACK;
                    parent.color = Color.BLACK;
                    grandparent.color = Color.RED;

                    //继续向上调整
                    cur = grandparent;
                    parent = cur.parent;
                } else {
                    // uncle 不存在或者 uncle 是 黑色结点,那就需要进行旋转调整

                    if(cur == parent.left) {
                        //先对 parent 进行右旋,然后交换 cur 与 parent变成下面的情况
                        rotateRight(parent);

                        TreeNode tmp = parent;
                        parent = cur;
                        cur = tmp;
                    }

                    //此时 cur 是 parent 的 右孩子
                    rotateLeft(grandparent);
                    grandparent.color = Color.RED;
                    parent.color = Color.BLACK;

                    //红黑树已经调整完成,直接退出循环,不用继续调整颜色
                    break;
                }

            }
        }

最后,大家记得循环结束后,将 root 的颜色置为黑色。

验证红黑树

首先我们要先验证这是不是一颗二叉搜索树,也就是中序遍历是否有序,我们可以直接打印出来,也可以直接写一个方法返回 true 或者 flase

    private static void isSort(TreeNode root) {
        if(root == null) {
            return;
        }

        isSort(root.left);
        System.out.print(root.val + " ");
        isSort(root.right);
    }

接着我们要判断有没有存在两个连续的结点,我们可以使用递归,当结点为红色就看看它的父亲结点是不是也是红色的

    private static boolean isRedNodes(TreeNode root) {
        if(root == null) {
            return true;
        }

        if(root.color == Color.RED && root.parent.color == Color.RED) {
            System.out.println("出现两个连续的红色结点!!!");
            return false;
        }

        return isRedNodes(root.left) && isRedNodes(root.right);
    }

还要判断每条路径上的黑色结点数目是不是相同的,我们可以使用递归,传递三个参数,一个是root ,一个是当前计算的黑色结点数目(pathBlack),另一个参数是一条路径上的已知黑色结点数目(blackNodes),当递归的结点是黑色的时候,就让pathBlack自增,如果递归的结点左右孩子都为空则判断二者是否相同。
分别递归左数 再 递归右树

    private static  boolean isSamePathBlackNodes(TreeNode root,int pathBlack, int blackNodes) {
        if(root == null) {
            return true;
        }

        if(root.color == Color.BLACK) {
            pathBlack++;
        }

        if(root.left == null && root.right == null) {
            if(pathBlack != blackNodes) {
                System.out.println("路径上的黑色结点数目不相同!!!");
                return false;
            }
        }

        return isSamePathBlackNodes(root.left,pathBlack,blackNodes)
                && isSamePathBlackNodes(root.right,pathBlack,blackNodes);
    }

最后我们还要判断根节点是不是黑色的,因为上面的第二个和第三个方法是判断有没有违反红黑树性质的方法,于是我们可以把判断根节点的方法和这两个方法结合在一起,形成一个新方法,正好在这里直接计算出一条路径的黑色结点传递给第三个方法:

    private static boolean isRBTree (TreeNode root) {
        if(root == null) {
            return true;
        }

        if(root.color != Color.BLACK) {
            System.out.println("根节点为红色!!!");
            return false;
        }

        TreeNode cur = root;
        int blackNodes = 0;
        while(cur != null) {
            if(cur.color == Color.BLACK) {
                blackNodes++;
            }
            cur = cur.left;
        }

        return isRedNodes(root) && isSamePathBlackNodes(root,0,blackNodes);
    }

最后就是在测试类里检测自己的红黑树有没有问题,这里我提供一个测试用例,这个测试用例涵盖了红黑树的 6 中情况:24,12,13,18,25,17,30,28,大家可以根据答案去比对自己的红黑树有没有错误:

这颗红黑树的样子:
在这里插入图片描述

最终测试类

public class Test {

    private static void isSort(TreeNode root) {
        if(root == null) {
            return;
        }

        isSort(root.left);
        System.out.print(root.val + " ");
        isSort(root.right);
    }

    private static boolean isRedNodes(TreeNode root) {
        if(root == null) {
            return true;
        }

        if(root.color == Color.RED && root.parent.color == Color.RED) {
            System.out.println("出现两个连续的红色结点!!!");
            return false;
        }

        return isRedNodes(root.left) && isRedNodes(root.right);
    }

    private static  boolean isSamePathBlackNodes(TreeNode root,int pathBlack, int blackNodes) {
        if(root == null) {
            return true;
        }

        if(root.color == Color.BLACK) {
            pathBlack++;
        }

        if(root.left == null && root.right == null) {
            if(pathBlack != blackNodes) {
                System.out.println("路径上的黑色结点数目不相同!!!");
                return false;
            }
        }

        return isSamePathBlackNodes(root.left,pathBlack,blackNodes)
                && isSamePathBlackNodes(root.right,pathBlack,blackNodes);
    }

    private static boolean isRBTree (TreeNode root) {
        if(root == null) {
            return true;
        }

        if(root.color != Color.BLACK) {
            System.out.println("根节点为红色!!!");
            return false;
        }

        TreeNode cur = root;
        int blackNodes = 0;
        while(cur != null) {
            if(cur.color == Color.BLACK) {
                blackNodes++;
            }
            cur = cur.left;
        }

        return isRedNodes(root) && isSamePathBlackNodes(root,0,blackNodes);
    }

    public static void main(String[] args) {
        RedBlackTree redBlackTree = new RedBlackTree();
        int[] arr = {24,12,13,18,25,17,30,28};
        for (int i = 0; i < arr.length; i++) {
            redBlackTree.insert(arr[i]);
        }
		
		isSort(redBlackTree.root);
        System.out.println();
        System.out.println(isRBTree(redBlackTree.root));
    }
}

最终代码

public enum Color {
    RED,BLACK
}

public class TreeNode {
    int val;
    TreeNode left;
    TreeNode right;
    TreeNode parent;
    Color color;

    public TreeNode(int val) {
        this.val = val;
        this.color = Color.RED;
    }
}

public class RedBlackTree {
    public TreeNode root;

    public boolean insert(int val) {
        TreeNode node = new TreeNode(val);
        if(root == null) {
            root = node;
            root.color = Color.BLACK;
            return true;
        }

        TreeNode cur = root;
        TreeNode parent = null;

        while(cur != null) {
            if(cur.val == val) {
                //数值相同的结点不能插入。
                return false;
            } else if(cur.val > val) {
                parent = cur;
                cur = cur.left;
            } else {
                parent = cur;
                cur = cur.right;
            }
        }

        if(parent.val > val) {
            parent.left = node;
        } else {
            parent.right = node;
        }
        node.parent = parent;

        cur = node;

        while(parent != null && parent.color == Color.RED){
            TreeNode grandparent = parent.parent; //一定存在

            if(parent == grandparent.left) {
                TreeNode uncle = grandparent.right;
                if(uncle != null && uncle.color == Color.RED) {
                    uncle.color = Color.BLACK;
                    parent.color = Color.BLACK;
                    grandparent.color = Color.RED;

                    //继续向上调整
                    cur = grandparent;
                    parent = cur.parent;
                } else {
                    // uncle 不存在或者 uncle 是 黑色结点,那就需要进行旋转调整

                    if(cur == parent.right) {
                        //先对 parent 进行左旋,然后交换 cur 与 parent ,之后就会变成下面的代码的情况
                        rotateLeft(parent);
                        TreeNode tmp = parent;
                        parent = cur;
                        cur = parent;
                    }

                    // cur 是 parent 的右孩子,对grandparent 进行右旋
                    rotateRight(grandparent);
                    parent.color = Color.BLACK;
                    grandparent.color = Color.RED;

                    //红黑树已经调整完成,直接退出循环,不用继续调整颜色
                    break;
                }

            } else {
                //parent == grandparent.right
                TreeNode uncle = grandparent.left;
                if(uncle != null && uncle.color == Color.RED) {
                    uncle.color = Color.BLACK;
                    parent.color = Color.BLACK;
                    grandparent.color = Color.RED;

                    //继续向上调整
                    cur = grandparent;
                    parent = cur.parent;
                } else {
                    // uncle 不存在或者 uncle 是 黑色结点,那就需要进行旋转调整

                    if(cur == parent.left) {
                        //先对 parent 进行右旋,然后交换 cur 与 parent变成下面的情况
                        rotateRight(parent);

                        TreeNode tmp = parent;
                        parent = cur;
                        cur = tmp;
                    }

                    //此时 cur 是 parent 的 右孩子
                    rotateLeft(grandparent);
                    grandparent.color = Color.RED;
                    parent.color = Color.BLACK;

                    //红黑树已经调整完成,直接退出循环,不用继续调整颜色
                    break;
                }

            }
        }
        root.color = Color.BLACK;

        return true;
    }

    //右旋
    private void rotateRight(TreeNode parent) {
        TreeNode cur = parent.left;
        TreeNode curR = cur.right;
        TreeNode Pparent = parent.parent;

        parent.left = curR;
        if(curR != null) {
            curR.parent = parent;
        }

        cur.right = parent;
        parent.parent = cur;
        cur.parent = Pparent;

        if(root == parent) {
            root = cur;
        } else if(Pparent.left == parent) {
            Pparent.left = cur;
        } else {
            Pparent.right = cur;
        }
    }


    //左旋
    private void rotateLeft(TreeNode parent) {
        TreeNode cur = parent.right;
        TreeNode curL = cur.left;
        TreeNode Pparent = parent.parent;

        parent.right = curL;
        if(curL != null) {
            curL.parent = parent;
        }

        cur.left = parent;
        parent.parent = cur;
        cur.parent = Pparent;

        if(root == parent) {
            root = cur;
        } else if(Pparent.left == parent) {
            Pparent.left = cur;
        } else {
            Pparent.right = cur;
        }
    }

}

小结

因为红黑树不追求绝对的高度平衡,而是相对平衡(只需要保证最长路径不超过最短路径的2倍),在上面的代码中,只使用了左旋和右旋代码,并且不是每次插入和删除都需要旋转,大部分还是调整颜色,这个时间和资源的消耗小于AVL 树,并且红黑树在查找的时候时间复杂度也能达到 O(log N)

红黑树在实际中有着大量的应用:例如在Java集合框架中的:TreeMap、TreeSet底层使用的就是红黑树, C++ STL库 – map/set、mutil_map/mutil_set,linux内核:进程调度中使用红黑树管理进程控制块,epoll在内核中实现时使用红黑树管理事件块,其他一些库:比如nginx中用红黑树管理timer等

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

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

相关文章

成都云飞浩容文化传媒有限公司怎么样可靠吗?

在数字经济浪潮汹涌的今天&#xff0c;电商行业作为推动经济高质量发展的关键引擎&#xff0c;正以前所未有的速度重塑着商业版图。在这场变革中&#xff0c;成都云飞浩容文化传媒有限公司凭借其专业的电商服务能力和前瞻性的市场洞察&#xff0c;成为了众多品牌商家信赖的合作…

助力草莓智能自动化采摘,基于嵌入式端超轻量级模型LeYOLO全系列【n/s/m/l】参数模型开发构建果园种植采摘场景下草莓成熟度智能检测识别系统

随着科技的飞速发展&#xff0c;人工智能&#xff08;AI&#xff09;技术已经渗透到我们生活的方方面面&#xff0c;从智能家居到自动驾驶&#xff0c;再到医疗健康&#xff0c;其影响力无处不在。然而&#xff0c;当我们把目光转向中国的农业领域时&#xff0c;一个令人惊讶的…

Git分布式版本控制--2+day018 LeetCode235 701 450

基础操作: 1.分支: 几乎所有的版本控制系统都以某种形式支持分支。 使用分支意味着你可以把你的工作从开发主线上分离 开来进行重大的Bug修改、开发新的功能&#xff0c;以免影响开发主线。 2.指令操作: 2.1 查看本地分支: git branch 2.2 创建本地分支: git branch 分支名…

Spring Boot 3.3 新特性介绍

1. 引言 Spring Boot 3.1.x 停止维护了&#xff0c;而 3.3.x 作为最新发布的版本&#xff0c;带来了许多新特性和改进。本篇文章将详细介绍这些新特性&#xff0c;并通过样例代码加以解释&#xff0c;帮助开发者更好地掌握和应用这些新功能。 Spring Boot 3.3现已正式发布&…

RNN循环网络层

文章目录 1、简介2、RNN 网络原理3、PyTorch RNN 层的使用3.1、RNN送入单个数据3.2、RNN层送入批量数据 4、RNN三个维度4.1、解释4.2、输入数据的组织4.3、示例4.4、为什么需要这种格式&#xff1f;4.5、小结 &#x1f343;作者介绍&#xff1a;双非本科大三网络工程专业在读&a…

【博士每天一篇文献-算法】持续学习经典算法之LwF: Learning without forgetting

1 介绍 年份&#xff1a;2017 作者&#xff1a;Zhizhong Li&#xff0c;Amazon AWS Rekognition&#xff1b;Derek Hoiem&#xff0c;伊利诺伊大学计算机科学教授 会议&#xff1a;IEEE transactions on pattern analysis and machine intelligence 引用量&#xff1a;4325 Li…

【NXP-MCXA153】开发板救砖教程

前言 新手接触到NXP的板子时&#xff0c;一个不留神把调试的GPIO&#xff08;RXD、TXD&#xff09;改掉&#xff0c;很容易出现MDK Keil无法识别CMSIS-DAP调试器的情况&#xff1b;主控进入了莫名其妙模式导致调试器无法识别了&#xff0c;你根本无法下载程序&#xff0c;想改…

大数据-67 Kafka 高级特性 分区 分配策略 Ranger、RoundRobin、Sticky、自定义分区器

点一下关注吧&#xff01;&#xff01;&#xff01;非常感谢&#xff01;&#xff01;持续更新&#xff01;&#xff01;&#xff01; 目前已经更新到了&#xff1a; Hadoop&#xff08;已更完&#xff09;HDFS&#xff08;已更完&#xff09;MapReduce&#xff08;已更完&am…

虚拟机Centos7 minimal版本安装docker

1、在 CentOS 7 上启用 EPEL 软件包存储库&#xff1b; &#xff08;删除epel软件包和其他操作可参考&#xff1a;如何在 CentOS 7 上使用 EPEL (linux-console.net)&#xff09; 1.1&#xff1a; 要安装epel前会报错&#xff0c;如下所示&#xff1a; 先参照这个链接安装&a…

【python】OpenCV—Image Super Resolution

文章目录 1、背景介绍2、准备工作3、EDSR4、ESPCN5、FSRCNN6、LapSRN7、汇总对比8、参考 1、背景介绍 图像超分&#xff0c;即图像超分辨率&#xff08;Image Super Resolution&#xff0c;简称SR&#xff09;&#xff0c;是指由一幅低分辨率图像或图像序列恢复出高分辨率图像…

HTML基础 - HTML5

目录 一. 简介 二. 新增元素 三. 拖放 地理定位 A、HTML5 拖放&#xff08;Drag and Drop&#xff09; B.HTML5 地理定位&#xff08;Geolocation&#xff09; 四. input 五. web存储 webSQL 六. 应用程序缓存 web workers 七. web socket 可以先看上篇HTML基础再来看…

RabbitMQ、Kafka对比(超详细),Kafka、RabbitMQ、RocketMQ的区别

文章目录 一、kafka和rabbitmq全面对比分析1.1 简介1.2 kafka和rabbitmq全面对比分析1.3 影响因素 二、RabbitMQ、Kafka主要区别2.1 详解/主要区别2.1.1 设计目标和适用场景2.1.2 架构模型方面2.1.3 吞吐量和性能2.1.4 消息存储和持久化2.1.5 消息传递保证2.1.6 集群负载均衡方…

理解二分搜索算法

一.介绍 在本文中&#xff0c;我们将了解二分搜索算法。二分搜索算法是一种在排序数组中查找特定元素的高效方法。它的工作原理是将搜索间隔反复分成两半&#xff0c;从而大大减少了找到所需元素所需的比较次数。该算法的时间复杂度为 O(log n)&#xff0c;因此对于大型数据集…

CLOS架构

CLOS Networking CLOS Networking 是指使用 Clos 网络拓扑结构&#xff08;Clos Network Topology&#xff09;进行网络设计的一种方法。该方法是由贝尔实验室的工程师 Charles Clos 在1950年代提出的&#xff0c;以解决电路交换网络的可扩展性和性能问题。随着现代计算和网络…

SpringBoot基础(一):快速入门

SpringBoot基础系列文章 SpringBoot基础(一)&#xff1a;快速入门 目录 一、SpringBoot简介二、快速入门三、SpringBoot核心组件1、parent1.1、spring-boot-starter-parent1.2、spring-boot-dependencies 2、starter2.1、spring-boot-starter-web2.2、spring-boot-starter2.3、…

YOLOv10改进 | 主干篇 | YOLOv10引入CVPR2023 顶会论文BiFormer用于主干修改

1. 使用之前用于注意力的BiFormer在这里用于主干修改。 YOLOv10改进 | 注意力篇 | YOLOv10引入BiFormer注意力机制 2. 核心代码 from collections import OrderedDict from functools import partial from typing import Optional, Union import torch import torch.nn as n…

C++:vector容器

概览 std::vector是C标准模板库(STL)中的一种动态数组容器。它提供了一种类似于数组的数据结构&#xff0c;但是具有动态大小和更安全的内存管理。 定义和基本特性 std::vector是C标准库中的一 个序列容器&#xff0c;它代表了能够动态改变大小的数组。与普通数组一样&#x…

酒店智能插座在酒店智慧化中的重要性

在当今数字化和智能化的时代&#xff0c;酒店行业也在不断追求创新和提升服务品质&#xff0c;以满足客人日益增长的需求。酒店智能插座作为酒店智慧化的重要组成部分&#xff0c;发挥着不可忽视的作用。 提升客人的便利性&#xff1a; 酒店智能插座能够为客人提供更加便捷的充…

使用 Java Swing 的 IMEI 验证器

一.介绍 本文档介绍如何使用 Java Swing 创建一个简单的 IMEI 验证器应用程序。 二.什么是 IMEI 号码 IMEI 代表国际移动设备识别码。IMEI 用于在移动设备连接到网络时对其进行识别。每个 GSM、CDMA 或卫星移动设备都有唯一的 IMEI 号码。此号码将印在设备电池组件内。用户可…

Flutter GPU 是什么?为什么它对 Flutter 有跨时代的意义?

Flutter 3.24 版本引入了 Flutter GPU 概念的新底层图形 API flutter_gpu &#xff0c;还有 flutter_scene 的 3D 渲染支持库&#xff0c;它们目前都是预览阶段&#xff0c;只能在 main channel 上体验&#xff0c;并且依赖 Impeller 的实现。 Flutter GPU 是 Flutter 内置的底…