Java中的红黑树(如果想知道Java中有关红黑树的知识点,那么只看这一篇就足够了!)

news2024/9/21 19:58:35

        前言:红黑树作为一种自平衡的二叉搜索树,在计算机科学领域具有极其重要的地位。它通过颜色约束和旋转操作保持树的高度平衡,从而保证了查找、插入、删除等操作的高效性。红黑树广泛应用于操作系统的调度算法、数据库索引、Java集合框架等领域。


✨✨✨这里是秋刀鱼不做梦的BLOG

✨✨✨想要了解更多内容可以访问我的主页秋刀鱼不做梦-CSDN博客

写在前面


        Java中红黑树是一种高级的数据结构,相较于前边的线性表,二叉树,哈希表难上许多,如何读者没有二叉搜索树、AVL树的基础是很难理解红黑树的,这里我们推荐读者先将二叉搜索树与AVL树理解之后,在来学习红黑树!!!

二叉搜索树知识:Java中的二叉搜索树(如果想知道Java中有关二叉搜索树的知识点,那么只看这一篇就足够了!)-CSDN博客

AVL树知识:

Java中的AVL树(如果想知道Java中有关AVL树的知识点,那么只看这一篇就足够了!)-CSDN博客

那么在开始学习之前,先让我们看一下本文大致的讲解内容:

目录

写在前面

1.红黑树的概念与性质

        (1)红黑树的概念

        (2)红黑树的性质

2.红黑树的节点的定义

3.红黑树的插入

        (1)情况一: cur为红,p为红,g为黑,u存在且为红

        (2)情况二: cur为红,p为红,g为黑,u不存在/u为黑

        (3)情况三: cur为红,p为红,g为黑,u不存在/u为黑

4.红黑树的验证

1. 检测其是否满足二叉搜索树(中序遍历是否为有序序列)

2.检测其是否满足红黑树的性质

解释:

5.红黑树与AVL树的比较

6.红黑树的应用

1. Java中的TreeMap和TreeSet

2. 数据库索引

3. 操作系统调度器


1.红黑树的概念与性质

        (1)红黑树的概念

        在开始正式的学习Java中的红黑树之前,先让我们了解一下红黑树的概念:

        红黑树是一种自平衡的二叉搜索树。二叉搜索树的性质决定了左子树的所有节点都小于根节点,右子树的所有节点都大于根节点。红黑树在此基础上,通过对节点着色及特定的规则来维持树的平衡。其核心特点在于红黑树通过颜色限制和旋转操作来避免二叉搜索树退化成线性链表,从而使得插入、删除和查找操作的时间复杂度始终保持在 O(log n)。

当然根据上述枯燥乏味的文字读者可能立即不了什么是红黑树,这里我们附上一张红黑树的图片:

当然,我们从这张图片中,我们就可以发现红黑树的一些性质

        (2)红黑树的性质

        从上图中,我们可以发现:

  1. 最长路径做多是最短路径的2倍

  2. 每个结点不是红色就是黑色

  3. 根节点是黑色的

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

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

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

这里读者可以简单的理解一下,下面我们在自行实现红黑树的时候,能更好的理解这些性质。

2.红黑树的节点的定义

        在了解完了红黑树的基本概念之后,我们发现,红黑树就是一棵带有颜色的近似平衡的二叉搜索树,那么现在让我们看一下如何去定义红黑树的节点。

        红黑树的节点定义与普通二叉树节点相似,但多了一个颜色属性,用于记录节点的颜色(红或黑),节点的基本定义如下:

public static class TreeNode {
    public TreeNode left;    // 左子节点
    public TreeNode right;   // 右子节点
    public TreeNode parent;  // 父节点
    public int val;          // 节点值
    public Color color;      // 节点颜色

    // 枚举类型定义红色和黑色
    public enum Color {
        RED, BLACK
    }

    // 构造函数,默认节点颜色为红色
    public TreeNode(int val) {
        this.val = val;
        this.color = Color.RED; // 新插入节点初始为红色
    }
}

其中的属性为:

  • left:指向左子节点。
  • right:指向右子节点。
  • parent:指向父节点。
  • val:节点的值(整数类型)。
  • color:节点的颜色,表示为Color类型(红色或黑色)。

通过上述的讲解,现在我们就了解了红黑树的节点的定义了!!!

3.红黑树的插入

        当我们了解完了什么是红黑树以及红黑树的节点如何定义之后,现在让我们自行实现一个红黑树的代码,创建一棵红黑树的大致流程为:

1. 按照二叉搜索的树规则插入新节点;
2. 检测新节点插入后,红黑树的性质是否造到破坏;

而对于红黑树中节点的插入(插入节点之后),有以下几种情况:约定:cur为当前节点,p为父节点,g为祖父节点,u为叔叔节点)

        (1)情况一: cur为红,p为红,g为黑,u存在且为红

        ——解决方式:将p,u改为黑,g改为红,然后把g当成cur,继续向上调整。

        (2)情况二: cur为红,p为红,g为黑,u不存在/u为黑

        ——解决方式:p为g的左孩子,cur为p的左孩子,则进行右单旋转;相反,p为g的右孩子,cur为p的右孩子,则进行左单旋转,p、g变色--p变黑,g变红

        (3)情况三: cur为红,p为红,g为黑,u不存在/u为黑

——解决方式:p为g的左孩子,cur为p的右孩子,则针对p做左单旋转;相反,p为g的右孩子,cur为p的左孩子,则针对p做右单旋转,则转换成了情况2

        这样我们只需要对每种情况进行处理即可!!!以下为整体实现的代码:

public void insert(int val) {
    // 如果当前树为空,直接插入根节点,并将根节点设为黑色
    if (root == null) {
        root = new TreeNode(val);
        root.color = Color.BLACK;
        return;
    }

    TreeNode prev = null;
    TreeNode cur = root;

    // 寻找插入位置
    while (cur != null) {
        if (cur.val < val) {
            prev = cur;
            cur = cur.right;  // 继续在右子树查找
        } else if (cur.val > val) {
            prev = cur;
            cur = cur.left;   // 继续在左子树查找
        } else {
            return;  // 如果已存在相同值的节点,直接返回,不进行插入
        }
    }

    // 创建新节点,并根据比较结果插入到合适位置
    TreeNode node = new TreeNode(val);
    if (prev.val < val) {
        prev.right = node;  // 插入到右子节点
    } else {
        prev.left = node;   // 插入到左子节点
    }

    cur = node;
    TreeNode parent = cur.parent;

    // 修正红黑树的性质
    while (parent != null && parent.color == Color.RED) {
        TreeNode grandParent = parent.parent;

        // 情况1:父节点是祖父节点的左子节点
        if (grandParent.left == parent) {
            TreeNode uncle = grandParent.right;

            // 情况1.1:叔叔节点为红色
            if (uncle != null && uncle.color == Color.RED) {
                parent.color = Color.BLACK;  // 将父节点设为黑色
                uncle.color = Color.BLACK;   // 将叔叔节点设为黑色
                grandParent.color = Color.RED;  // 将祖父节点设为红色
                cur = grandParent;  // 将当前节点指向祖父节点,继续修正
                parent = cur.parent;  // 更新父节点
            } else {
                // 情况1.2:叔叔节点为黑色,当前节点是右子节点
                if (parent.right == cur) {
                    rotateLeft(parent);  // 左旋父节点
                    TreeNode temp = parent;
                    parent = cur;
                    cur = temp;  // 更新当前节点和父节点
                }
                // 情况1.3:叔叔节点为黑色,当前节点是左子节点
                rotateRight(grandParent);  // 右旋祖父节点
                parent.color = Color.BLACK;  // 将父节点设为黑色
                grandParent.color = Color.RED;  // 将祖父节点设为红色
            }
        } else {
            // 情况2:父节点是祖父节点的右子节点
            TreeNode uncle = grandParent.left;

            // 情况2.1:叔叔节点为红色
            if (uncle != null && uncle.color == Color.RED) {
                parent.color = Color.BLACK;  // 将父节点设为黑色
                uncle.color = Color.BLACK;   // 将叔叔节点设为黑色
                grandParent.color = Color.RED;  // 将祖父节点设为红色
                cur = grandParent;  // 将当前节点指向祖父节点,继续修正
                parent = cur.parent;  // 更新父节点
            } else {
                // 情况2.2:叔叔节点为黑色,当前节点是左子节点
                if (parent.left == cur) {
                    rotateRight(parent);  // 右旋父节点
                    TreeNode temp = parent;
                    parent = cur;
                    cur = temp;  // 更新当前节点和父节点
                }
                // 情况2.3:叔叔节点为黑色,当前节点是右子节点
                rotateLeft(grandParent);  // 左旋祖父节点
                grandParent.color = Color.RED;  // 将祖父节点设为红色
                parent.color = Color.BLACK;  // 将父节点设为黑色
            }
        }
    }

    // 保证根节点始终是黑色
    root.color = Color.BLACK;
}

其中的左旋操作:

public void rotateLeft(TreeNode prev) {
    // 取得当前节点的右子节点
    TreeNode subR = prev.right;
    // 取得右子节点的左子节点
    TreeNode subRL = subR.left;

    // 将右子节点的左子节点连接到当前节点的右子节点上
    subR.left = prev;
    // 将当前节点的右子节点连接到右子节点的左子节点上
    prev.right = subRL;

    // 取得当前节点的父节点
    TreeNode parParent = prev.parent;
    // 更新当前节点的父节点为右子节点
    prev.parent = subR;
    // 如果右子节点的左子节点不为空,更新它的父节点为当前节点
    if (subRL != null) {
        subRL.parent = prev;
    }

    // 如果当前节点是根节点,将右子节点设为新的根节点
    if (prev == root) {
        root = subR;
        subR.parent = null; // 更新根节点的父节点为 null
    } else {
        // 否则,根据当前节点是父节点的左子节点还是右子节点来更新父节点的子节点
        if (parParent.left == prev) {
            parParent.left = subR;
            subR.parent = parParent;
        } else {
            parParent.right = subR;
            subR.parent = parParent;
        }
    }
}

其中右旋操作:

public void rotateRight(TreeNode prev) {
    // 取得当前节点的左子节点
    TreeNode subL = prev.left;
    // 取得左子节点的右子节点
    TreeNode subLR = subL.right;

    // 将左子节点的右子节点连接到当前节点的左子节点上
    prev.left = subLR;
    // 将当前节点连接到左子节点的右子节点上
    subL.right = prev;

    // 如果左子节点的右子节点不为空,更新它的父节点为当前节点
    if (subLR != null) {
        subLR.parent = prev;
    }

    // 取得当前节点的父节点
    TreeNode parParent = prev.parent;
    // 更新当前节点的父节点为左子节点
    prev.parent = subL;

    // 如果当前节点是根节点,将左子节点设为新的根节点
    if (prev == root) {
        root = subL;
        subL.parent = null; // 更新根节点的父节点为 null
    } else {
        // 否则,根据当前节点是父节点的左子节点还是右子节点来更新父节点的子节点
        if (parParent.left == prev) {
            parParent.left = subL;
            subL.parent = parParent;
        } else {
            parParent.right = subL;
            subL.parent = parParent;
        }
    }
}

——这里的左旋与右旋和AVL树中的左旋与右旋是相同的!!!

当读者耐心的读完并理解完上述的代码之后,现在我们在总体的梳理一下上述代码的整理逻辑:

1. 查找插入位置:

        从根节点开始,比较插入的值 val 和当前节点的值,确定插入位置。如果插入值小于当前节点,则继续在左子树查找;否则在右子树查找。这样确保了插入的新节点满足二叉搜索树的性质。

2. 插入节点:

        一旦找到合适的位置,就将新节点插入到树中。如果父节点的值小于插入值,新节点作为父节点的右子节点;否则作为左子节点。

3. 调整红黑树的性质:

        由于插入的新节点默认是红色,可能会违反红黑树的性质。红黑树的主要性质包括:

  • 根节点必须是黑色。

  • 红色节点的子节点必须是黑色(即红色节点不能有红色子节点)。

  • 从任意节点到其叶子节点的每条路径上黑色节点的数量必须相同。

为了恢复红黑树的平衡性,可能需要进行以下调整:

  • 重新着色:如果插入节点的父节点和叔叔节点都是红色,那么父节点和叔叔节点重新染成黑色,祖父节点染成红色,并继续检查祖父节点。

  • 旋转操作:当叔叔节点是黑色或不存在时,通过旋转来调整树的结构。左旋或右旋操作可以修正父节点和祖父节点之间的关系,使树重新达到平衡。

4. 保持根节点为黑色:

        红黑树的一个重要性质是根节点必须始终是黑色,因此在所有调整完成后,确保根节点被设置为黑色。

这样我们就完成了红黑树节点的插入操作了!!!

4.红黑树的验证

        当我们创建完了一棵红黑树之后,我们如何去验证其为一棵红黑树呢?验证一棵树为红黑树,我们需要从其定义性质入手。

红黑树的检测分为两步:

1. 检测其是否满足二叉搜索树(中序遍历是否为有序序列)

public void inorder(TreeNode root) {
    // 如果当前节点为空,则返回(递归终止条件)
    if (root == null) {
        return;
    }
    
    // 递归访问左子树
    inorder(root.left);
    
    // 访问当前节点
    System.out.println(root.val);
    
    // 递归访问右子树
    inorder(root.right);
}

2.检测其是否满足红黑树的性质

public boolean isRBTree(TreeNode root) {
    // 如果根节点为空,空树被认为是红黑树
    if (root == null) {
        return true;
    }
    
    // 根节点必须是黑色
    if (root.color == Color.RED) {
        return false;
    }

    // 计算从根节点到最左边叶子节点路径上的黑色节点数量
    int prevBlackNodeNum = calBlackNumber(root);

    // 检查红色节点是否有连续的红色节点,并检查所有路径上的黑色节点数量是否一致
    return examRedNode(root) && examBlackNode(root, 0, prevBlackNodeNum);
}

// 计算从根节点到最左边叶子节点路径上的黑色节点数量
private int calBlackNumber(TreeNode root) {
    int prevBlackNodeNum = 0;
    TreeNode cur = root;
    
    // 沿着左子树遍历,计算黑色节点数量
    while (cur.left != null) {
        if (cur.color == Color.BLACK) {
            prevBlackNodeNum++;
        }
        cur = cur.left;
    }
    return prevBlackNodeNum;
}

// 检查红色节点的父节点是否也为红色,红色节点不能连续
private boolean examRedNode(TreeNode root) {
    if (root == null) {
        return true;
    }
    
    // 如果当前节点是红色,检查其父节点是否也为红色
    if (root.color == Color.RED) {
        TreeNode parent = root.parent;
        if (parent.color == Color.RED) {
            return false;
        }
    }
    
    // 递归检查左右子树
    return examRedNode(root.left) && examRedNode(root.right);
}

// 检查从根节点到每个叶子节点路径上的黑色节点数量是否一致
private boolean examBlackNode(TreeNode root, int blackNodeNum, int prevBlackNodeNum) {
    if (root == null) {
        return true;
    }
    
    // 如果当前节点是黑色,黑色节点计数加一
    if (root.color == Color.BLACK) {
        blackNodeNum++;
    }
    
    // 如果到达叶子节点,检查路径上的黑色节点数量是否与最左边路径一致
    if (root.left == null && root.right == null) {
        if (blackNodeNum != prevBlackNodeNum) {
            return false;
        }
    }
    
    // 递归检查左右子树
    return examBlackNode(root.left, blackNodeNum, prevBlackNodeNum)
            && examBlackNode(root.right, blackNodeNum, prevBlackNodeNum);
}

解释:

  • isRBTree 方法:

    • 如果根节点为空,认为是红黑树。
    • 根节点必须是黑色。
    • 计算最左边路径的黑色节点数量。
    • 检查红色节点是否有连续红色节点,并确保所有路径上的黑色节点数量一致。
  • calBlackNumber 方法:

    • 计算从根节点到最左边叶子节点路径上的黑色节点数量。
  • examRedNode 方法:

    • 确保红色节点不能有红色的父节点。
  • examBlackNode 方法:

    • 确保从根节点到每个叶子节点的路径上黑色节点数量一致。

        这样我们就知道了如何判断一棵树是否为红黑树了!

5.红黑树与AVL树的比较

        学习完了红黑树之后,读者可能会有疑问了,红黑树相较于前面所学的AVL树有什么优势吗?这里我们举出其中的差异:

  1. 平衡性:AVL树更加“严格平衡”,插入和删除操作后会尽可能保持树的高度平衡,因此查找操作效率较高。红黑树则更“松散”,允许树稍微倾斜,插入和删除操作效率更高。

  2. 旋转次数:由于红黑树的平衡要求较低,相比AVL树,红黑树在插入和删除操作中所需的旋转次数更少。AVL树为了保持高度平衡,可能会执行更多次旋转操作,因此在插入和删除时的性能稍逊于红黑树。

  3. 查找效率:由于AVL树保持更严格的平衡,它的查找操作在最坏情况下的性能比红黑树稍好。红黑树在最坏情况下的高度接近 2log(n),而AVL树的高度接近 log(n),这使得AVL树在查找操作中略有优势。

  4. 应用场景:红黑树的插入和删除操作比AVL树更快,因此在需要频繁插入和删除的场景中,红黑树更为合适。比如Java中的TreeMapTreeSet等数据结构都采用了红黑树。而AVL树则更适合用于查找频率较高且对平衡性要求较高的场景。

AVL树与红黑树的总结对比:

特性红黑树AVL树
平衡策略较为宽松严格平衡
插入删除效率较低
查找效率较低
旋转次数较少较多
应用场景插入/删除频繁查找频繁

这样我们就了解了红黑树与AVL树差异了

6.红黑树的应用

        红黑树因其高效的性能和自平衡性质,广泛应用于各种数据结构中。Java标准库中的一些重要类也基于红黑树来实现。

1. Java中的TreeMap和TreeSet

TreeMapTreeSet是Java集合框架中的重要实现,底层数据结构是红黑树。TreeMap提供了有序的键值对存储,而TreeSet则提供了有序的集合元素存储。它们都通过红黑树保证插入、删除和查找操作的时间复杂度为 O(log n)。

2. 数据库索引

在某些数据库系统中,红黑树也可以用于索引结构的实现。通过红黑树的数据平衡特性,数据库可以高效地执行增删改查操作,尤其是在处理大量数据时,红黑树能保证数据的有序性和访问效率。

3. 操作系统调度器

红黑树也被广泛用于操作系统调度器中。例如,在Linux的CFS(完全公平调度器)中,红黑树被用来高效地管理进程的优先级和时间片,确保进程调度的公平性和效率。

        这样我们就学完了有关Java中红黑树的全部知识点了!


以上就是本篇文章的全部内容了~~~

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

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

相关文章

「全球大模型竞技场」更新:DeepSeek-V2.5全面领跑国内模型

原文&#xff1a;深度求索 DeepSeek 月初&#xff0c;我们发布并开源了 DeepSeek-V2.5&#xff0c;一个融合通用与代码能力的全新模型。 近日&#xff0c;LMSYS 组织的全球大模型竞技场&#xff08;ChatBotArena&#xff09;更新结果发布&#xff0c;DeepSeek-V2.5 排名位列国…

额定剩余动作电流继电器有什么功能?

ASJ系列剩余电流动作继电器可与低压断路器或低压接触器等组装成组合式的剩余电流动作保护器&#xff0c;为现代电力系统设计&#xff0c;旨在提高电力系统的安全性和可靠性。其技术和多功能特性使其成为电力管理和监测的理想选择。主要适用于交流50Hz&#xff0c;额定电压为400…

iPhone 16系列:熟悉的味道,全新的体验

来看看iPhone 16和Plus这两个新成员&#xff0c;实话说&#xff0c;它们和之前曝光的样子几乎完全一致。下面我们就一起来细数一下这次的几大变化吧。 外观设计&#xff1a;焕然一新 首先&#xff0c;最显眼的变化就是后置镜头模组的布局调整为了垂直排列。这一改变使得整个背…

29912分页

拆分地址&#xff1a; 003FDFB0 0000 0000 0011 1111 1101 1111 1011 000000 0*8 00 0000 001 -> 1*8 1 1111 1101 -> 1FD*8 1111 1011 0000 -> FB0PROCESS 883ef7c8 SessionId: 1 Cid: 09b0 Peb: 7ffdf000 ParentCid: 0588DirBase: bf2484a0 ObjectTable: 98…

将多个pdf合并成一个文件?这几种合并方法很好用!

如何将多个pdf合并成一个文件&#xff1f;面对日益增长的PDF文档&#xff0c;我们时常陷入管理困境&#xff0c;这不仅仅关乎于时间与精力的巨大消耗&#xff0c;因为这些孤立的PDF文件如同散落的拼图碎片&#xff0c;让关键信息的搜寻变得如同大海捞针&#xff0c;严重拖慢了工…

双指针算法:快速解决问题的小技巧(Java代码实现)

“人的一生是短暂的&#xff0c;但如果卑鄙地过这短暂的一生&#xff0c;那就太长了。” 文章目录 前言文章有误敬请斧正 不胜感恩&#xff01;双指针简介对撞指针快慢指针例题聪明的小羊肖恩神奇的数组盛最多的水 总结 前言 写在开始&#xff1a; 双指针算法是一种经典且高效…

“中秋快乐”文字横幅的MATLAB代码生成

中秋快乐呀朋友们&#xff01;&#xff01;&#xff01; 给大家带来一个好玩的代码&#xff0c;能够生成“中秋快乐”的横幅文字&#xff0c;比较简单&#xff0c;当然你也可以根据自己的需求去更改文字和背景&#xff0c;废话不多说&#xff0c;直接展示。 文字会一直闪烁&…

智能BI项目第三期

AIGC AI 提问技巧 为了让 Al 更好地理解我们的输入&#xff0c;并给出预期精确的输出&#xff0c;需要严格控制我们的提问词。 1.使用系统预设 控制输入格式(便于Al精确地理解我们的需求) 你是一个数据分析师和前端开发专家&#xff0c;接下来我会按照以下固定格式给你提供…

《深度学习》PyTorch框架 优化器、激活函数讲解

目录 一、深度学习核心框架的选择 1、TensorFlow 1&#xff09;概念 2&#xff09;优缺点 2、PyTorch 1&#xff09;概念 2&#xff09;优缺点 3、Keras 1&#xff09;概念 2&#xff09;优缺点 4、Caffe 1&#xff09;概念 2&#xff09;优缺点 二、pytorch安装 1、安装 2、…

传输层协议 —— UDP协议

目录 0.前言 1.UDP协议格式 16位源端口和目的端口 16位UDP长度 16位校验和 2.UDP协议特点 无连接 不可靠 面向数据报 3.UDP的缓冲区 0.前言 首先&#xff0c;我们得明确一点&#xff0c;网络模型是分层的。自底向上分别是物理层、数据链路层、网络层、传输层、应用层…

Nginx反向代理出现502 Bad Gateway问题的解决方案

&#x1f389; 前言 前一阵子写了一篇“关于解决调用百度翻译API问题”的博客&#xff0c;近日在调用其他API时又遇到一些棘手的问题&#xff0c;于是写下这篇博客作为记录。 &#x1f389; 问题描述 在代理的遇到过很多错误码&#xff0c;其中出现频率最高的就是502&#x…

JavaEE:网络编程(套接字)

文章目录 Socket套接字TCP和UDP的区别有连接/无连接可靠传输/不可靠传输面向字节流/面向数据报全双工/半双工 UDP/TCP api的使用UDPDatagramSocketDatagramPacketInetSocketAddress练习 TCPServerSocketSocket练习 Socket套接字 Socket是计算机网络中的一种通信机制&#xff0…

驱动开发知识点

裸机开发 ——————————————linux驱动 SOC&#xff1a; 定义&#xff1a;SOC&#xff0c;全称System on Chip&#xff0c;是一种集成了多个功能模块的芯片&#xff0c;包括处理器、内存、外设、接口等。它将原本分散在多个芯片上的功能集成到一个芯片上&#xff0…

一个基于 laravel 和 amis 开发的后台框架, 友好的组件使用体验,可轻松实现复杂页面(附源码)

前言 随着互联网应用的发展&#xff0c;后台管理系统的复杂度不断增加&#xff0c;对于开发者而言&#xff0c;既要系统的功能完备&#xff0c;又要追求开发效率的提升。然而&#xff0c;传统的开发方式往往会导致大量的重复劳动&#xff0c;尤其是在构建复杂的管理页面时。有…

不限学历!这个证书在上海太香了!利于积分、落户、抵扣个税...

一、软考是什么&#xff1f; “软考”全称&#xff1a;计算机技术与软件专业技术资格&#xff08;水平&#xff09;考试&#xff0c;关于它你需要知道&#xff1a; 1、软考是全国性考试&#xff1a;软考实行全国统一规划&#xff0c;实行统一大纲&#xff0c;统一试题&#xff…

【vulhub】Os-hackNos-1

&#x1f3d8;️个人主页&#xff1a; 点燃银河尽头的篝火(●’◡’●) 如果文章有帮到你的话记得点赞&#x1f44d;收藏&#x1f497;支持一下哦 【vulhub】Os-hackNos-1 环境搭建漏洞复现 环境搭建 进入vulnhub官网 https://vulnhub.com 下载镜像 在虚拟机中打开镜像文件…

100行Python代码实现FastAPI Websocket 聊天室(纯协程方案)

本文发表于入职啦(公众号: ruzhila) 大家可以访问入职啦学习更多的编程实战。 项目地址 代码已经开源&#xff0c; fastapi_chatroom &#x1f44f; 欢迎Star 代码运行效果&#xff1a; 所有的项目都在github上开源&#xff1a;100-line-code 欢迎Star &#x1f44f; 用100行…

open sora1.1容器构建教程指南

一、介绍 Open-Sora 1.1 项目是 Colossal AI 团队开发的一个完全开源的视频生成项目&#xff0c;该项目致力于高效制作高质量视频&#xff0c;并通过开源原则实现先进视频生成技术的低成本普及。 1. 项目背景与目标 Open-Sora 项目旨在通过提供开源的模型、工具和内容&#…

idea使用阿里云服务器运行jar包

说明&#xff1a;因为我用的阿里云服务器不是自己的&#xff0c;所以一些具体的操作可能不太全面。看到一个很完整的教程&#xff0c;供参考。 0. 打包项目 这里使用的是maven打包。 在pom.xml中添加以下模块。 <build><plugins><plugin><groupId>org…

JDBC导图

思维歹徒 一、使用步骤 二、SQL注入 三、数据库查询&#xff08;查询&#xff09; 四、数据库写入&#xff08;增删改&#xff09; 五、Date日期对象处理 六、连接池使用 创建连接是从连接池拿&#xff0c;释放连接是放回连接池 七、事务和批次插入 八、Apache Commons DBUtil…