【高阶数据结构】红黑树

news2024/11/15 23:56:32

文章目录

  • 前言
  • 什么是红黑树
  • 红黑树的性质
  • 红黑树结点的定义
  • 红黑树的插入
    • 情况一
    • 情况二
    • 情况三
    • 插入代码总结
  • 验证是否为红黑树
  • 红黑树的删除

前言

前面我们学习了 AVL 树——高度平衡的二叉搜索树,AVL 树保证了结点的左右子树的高度差的绝对值不超过 1,也就是结点的左右子树的高度是绝对平衡的,虽然这种结构的查询速度非常的快,但是因为它要保证左右子树的绝对平衡,所以对 AVL 树进行增加或者删除操作的时候,就需要进行多次旋转,而对树进行旋转也是需要时间的,所以 AVL 树只适合存储一些静态的不经常变化的数据。那么要想保证查询速度,也要对数据进行增加和删除操作的话,就需要使用另一个数据结构——红黑树。

什么是红黑树

红黑树是一种二叉搜索树,但在每个节点上增加了一个存储位表示节点的颜色,可以是 Red 或者 Black。通过对任何一条从根到叶子的路径上各个结点着色方式的限制,红黑树确保没有一条路径会比其他路径长出两倍,因而是接近平衡的。

在这里插入图片描述

红黑树的性质

为了保证红黑树的查找速率以及增加和删除的速度,红黑树具有以下性质:

  1. 从根节点到叶子节点的路径中,最长路径最多是最短路径的二倍
  2. 每个节点不是红色就是黑色
  3. 根节点是黑色的
  4. 如果一个节点是红色的,则如果它的两个孩子节点是黑色的(一条路径上不存在两个连续的红色节点)
  5. 对于每个节点,从该结点到其所有后代节点的简单路径上,均包含相同数目的黑色节点(黑色节点的数量包括 NULL 节点)
  6. 每个叶子节点都是黑色的(此处的叶子结点指的是空结点)

这里对一些性质进行演示:

为什么从根节点到叶子节点的路径中,最长路径最多是最短路径的二倍?

这个性质是通过性质4、5得来的:
在这里插入图片描述

红黑树结点的定义

首先通过一个枚举类来表示颜色:

public enum COLOR {
    RED, BLACK;
}

红黑树节点的定义:

class RBTreeNode {
    public RBTreeNode left;
    public RBTreeNode right;
    public RBTreeNode parent;
    public rbtree.COLOR color = rbtree.COLOR.RED; //结点的颜色

    public int val;

    public RBTreeNode(int val) {
        this.val = val;
        this.color = COLOR.RED;
    }
}

在这里我们将节点的颜色默认设置为了红色,这是为什么呢?

因为红黑树的性质:从任一节点到其子树中每个叶子节点的路径都包含相同数量的黑色节点。将节点默认设置为红色,插入时不会违反红黑树的性质,因为红色的节点不会影响路径上黑色节点的数量。而如果默认颜色设置为黑色,每次插入新节点都可能违反红黑树的性质,需要频繁调整树的结构,导致效率降低。因此,将红黑树的结点默认颜色设置为红色是为了保持树的平衡,提高插入操作的效率。

红黑树的插入

因为红黑树也是属于特殊的二叉搜索树,所以在插入数据的时候,还是按照二叉搜索树插入的做法一样,当数据插入之后,我们需要做的就是检测新节点插入之后,红黑树的性质是否被破坏。

这里的破坏性质通常是指:因为新插入的节点的颜色默认是红色,如果新插入的节点的双亲节点的颜色是黑色的话,就没有破坏红黑树的性质,不需要做出修改;而如果插入的节点的双亲结点也是红色的话,就不符合红黑树的性质——红色结点的左右孩子节点的颜色都是黑色(在一条路径中不存在两个连续的红色节点),此时就需要对红黑树的结构进行修改。

这里我们将插入节点后需要调整红黑树结构的情况给列举出来:

这里我们约定:cur 节点为当前节点,p为父节点,g为祖父节点,u为叔叔节点

情况一

当 cur 为红色,p 为红色,g 为黑色,u 存在且为红色:

在这里插入图片描述

这里 cur 是新插入的节点,插入之后 p 为红色,cur 为红色,一条路径上存在两个连续的红色节点,此时就需要对红黑树的结构进行调整。这种情况还是比较容易解决的:这种情况,我们需要将 p 和 u 都改为黑色,并且为了保证路径上黑色节点的数量不变,还需要将 g 节点的颜色改为 RED,这样就没有破坏红黑树的性质。

在这里插入图片描述

修改 p、u 和 g 的颜色之后,还没有结束,因为红黑树的性质中还有一条性质就是:根节点的颜色必须为黑色,所以我们在进行上面的操作了之后,不管根节点为啥颜色,都需要进行 root.color = COLOR.BLACK 的操作。

通过代码体现就是这样:

while (parent != null && parent.color == COLOR.RED) {
    RBTreeNode grandfather = parent.parent; //grandfather不可能为null,因为如果parent为红色,那么就一定存在父亲节点,因为红黑树的根节点是黑色
    RBTreeNode uncle = null;
    if (grandfather.left == parent) {
        uncle = grandfather.right;
    }else {
        uncle = grandfather.left;
    }
    if (uncle != null && uncle.color == COLOR.RED) {
        //情况一
        parent.color = COLOR.BLACK;
        uncle.color = COLOR.BLACK;
        grandfather.color = COLOR.RED;

        //当grandfather的节点颜色变为了红色之后,可能又会破坏其他树的结构,所以需要继续向上调整
        cur = grandfather;
        parent = cur.parent;
    }
}
root.color = COLOR.BLACK; //将根节点的颜色修改为黑色

情况二

当 cur 为红色,p 为红色,g 为黑色,u 不存在或者 u 存在且为黑色:

这种情况往往不是刚插入时候造成的,而是因为在调整的过程中出现的:

在这里插入图片描述

所以这种情况只可能在向上调整的过程中才会出现:

在这里插入图片描述

对于这种情况的解决方式就是对 g 的左右子树进行右旋操作之后,将 p 的颜色改为黑色,g 的颜色改为红色:

在这里插入图片描述

这是 u 不存在的情况:

在这里插入图片描述

同样的,这里是右旋,将上面的情况进行镜像处理,就需要进行左旋操作了:

在这里插入图片描述

所以当 cur 为红色,p 为红色,g 为黑色。u不存在或者 u 存在且颜色为黑色的做法就可以总结为:

  1. 当 p 为 g 的左孩子,cur 为 p 的左孩子的时候:
  • (1)将 g 节点的左右子树进行右旋操作
  • (2)将 g 节点的颜色修改为红色,p 节点的颜色修改为黑色
  1. 当 p 为 g 的右孩子,cur 为 p 的右孩子的时候:
  • (1)将 g 节点的左右子树进行左旋操作
  • (2) 将 g 节点的颜色修改为红色,p 节点的颜色修改为黑色

通过代码体现就是这样:

while (parent != null && parent.color == COLOR.RED) {
    RBTreeNode grandfather = parent.parent; //grandfather不可能为null,因为如果parent为红色,那么就一定存在父亲节点,因为红黑树的根节点是黑色
    RBTreeNode uncle = null;
    if (grandfather.left == parent) {
        uncle = grandfather.right;
        if (uncle != null && uncle.color == COLOR.RED) {
            //情况一
            parent.color = COLOR.BLACK;
            uncle.color = COLOR.BLACK;
            grandfather.color = COLOR.RED;

            //当grandfather的节点颜色变为了红色之后,可能又会破坏其他树的结构,所以需要继续向上调整
            cur = grandfather;
            parent = cur.parent;
        }else {
            //else 就表示uncle为空或者uncle不为空且uncle颜色为黑色的情况
            if (cur == parent.left) {
                rotateRight(grandfather);
                grandfather.color = COLOR.RED;
                parent.color = COLOR.BLACK;
            }
        }
    }else {
        uncle = grandfather.left;
        if (uncle != null && uncle.color == COLOR.RED) {
            //情况一
            parent.color = COLOR.BLACK;
            uncle.color = COLOR.BLACK;
            grandfather.color = COLOR.RED;

            //当grandfather的节点颜色变为了红色之后,可能又会破坏其他树的结构,所以需要继续向上调整
            cur = grandfather;
            parent = cur.parent;
        }else {
            //else 就表示uncle为空或者uncle不为空且uncle颜色为黑色的情况
            if (cur == parent.right) {
            rotateLeft(grandfather);
            grandfather.color = COLOR.RED;
            parent.color = COLOR.BLACK;
            }
        }
    }
}
root.color = COLOR.BLACK;
return true;

右旋操作:

public void rotateRight(RBTreeNode parent) {
    RBTreeNode subL = parent.left;
    RBTreeNode subLR = subL.right;
    parent.left = subLR;
    if (subLR != null) {
        subLR.parent = parent;
    }
    RBTreeNode pParent = parent.parent;
    if (root == parent) {
    	root = subL;
        subL.parent = null;
    }else {
        if (pParent.left == parent) {
            pParent.left = subLR;
        }else {
            pParent.right = subLR;
        }
        subLR.parent = pParent;
    }
    subL.right = parent;
    parent.parent = subL;
}

左旋操作:

public void rotateLeft(RBTreeNode parent) {
    RBTreeNode subR = parent.right;
    RBTreeNode subRL = subR.left;
    RBTreeNode pParent = parent.parent;
    parent.right = subRL;
    if (subRL != null) {
        subRL.parent = parent;
    }
    if (root == parent) {
       root = subR;
       root.parent = pParent;
    }else {
       if (pParent.left == parent) {
            pParent.left = subR;
       else {
            pParent.right = subR;
        }
        subR.parent = pParent;
    }
    subR.left = parent;
    parent.parent = subR;
}

情况三

第三种情况还是 cur 为红色,p 为红色,g 为黑色,u 不存在或者 u 存在且为黑色,但是呢?这种情况不是和第二种情况一样,p 位于 g 的左侧,cur 位于 p 的左侧、p 位于 g 的右侧,cur 位于 p 的右侧这种相同方向的情况,而是 p 位于 g 的左侧,cur 位于 p 的右侧、p 位于 g 的右侧,cur 位于 g 的左侧:

在这里插入图片描述
同样这种情况也是在调整的过程中才会出现的:

在这里插入图片描述

当出现这种情况的时候,通过右旋 g 节点的左右子树,然后修改 p 和 g 的颜色是无法解决的:

在这里插入图片描述

当出现这种情况的时候,需要先对 p 节点的左右子树进行左旋操作:

在这里插入图片描述

对 p 的左右子树进行左旋操作之后,就变成了第二类情况,接下来对 g 的左右子树进行右旋操作之后,将 g 节点的颜色修改为红色、p 节点的颜色修改为黑色就可以达到目的了。

这是当 p 为 g 的左子树,cur 为 p 的右子树的情况,对于 p 为 g 的右子树,cur 为 p 的左子树的解决方法是类似的:

在这里插入图片描述

通过代码展示就是这样的:

当 p 为 g 的左子树,cur 为 p 的右子树:

rotateLeft(parent);
RBTreeNode tmp = parent;
parent = cur;
cur = tmp;
rotateRight(grandfather);
grandfather.color = COLOR.RED;
parent.color = COLOR.BLACK;

当 p 为 g 的右子树,cur 为 p 的左子树:

rotateRight(parent);
RBTreeNode tmp = parent;
parent = cur;
cur = tmp;
rotateLeft(grandfather);
grandfather.color = COLOR.RED;
parent.color = COLOR.BLACK;

插入代码总结

public class RBTree {
    class RBTreeNode {
        public RBTreeNode left;
        public RBTreeNode right;
        public RBTreeNode parent;
        public rbtree.COLOR color; //结点的颜色

        public int val;

        public RBTreeNode(int val) {
            this.val = val;
            this.color = COLOR.RED;
        }
    }

    private RBTreeNode root;

    public boolean insert(int data) {
        RBTreeNode node = new RBTreeNode(data);
        if (root == null) {
            root = node;
            node.color = COLOR.BLACK;
            return true;
        }
        RBTreeNode cur = root, parent = null; //parent节点记录cur节点的双亲节点
        while (cur != null) {
            if (cur.val < data) {
                parent = cur;
                cur = cur.right;
            }else if (cur.val > data) {
                parent = cur;
                cur = cur.left;
            }else {
                return false; //二叉搜索树中不存在重复的元素
            }
        }
        if (data > parent.val) {
            parent.right = node;
        }else {
            parent.left = node;
        }
        node.parent = parent;
        cur = node;

        while (parent != null && parent.color == COLOR.RED) {
            RBTreeNode grandfather = parent.parent; //grandfather不可能为null,因为如果parent为红色,那么就一定存在父亲节点,因为红黑树的根节点是黑色
            RBTreeNode uncle = null;
            if (grandfather.left == parent) {
                uncle = grandfather.right;
                if (uncle != null && uncle.color == COLOR.RED) {
                    //情况一
                    parent.color = COLOR.BLACK;
                    uncle.color = COLOR.BLACK;
                    grandfather.color = COLOR.RED;

                    //当grandfather的节点颜色变为了红色之后,可能又会破坏其他树的结构,所以需要继续向上调整
                    cur = grandfather;
                    parent = cur.parent;
                }else {
                    //else 就表示uncle为空或者uncle不为空且uncle颜色为黑色的情况
                    if (cur == parent.right) {
                        rotateLeft(parent);
                        RBTreeNode tmp = parent;
                        parent = cur;
                        cur = tmp;
                    }
                    rotateRight(grandfather);
                    grandfather.color = COLOR.RED;
                    parent.color = COLOR.BLACK;
                }
            }else {
                uncle = grandfather.left;
                if (uncle != null && uncle.color == COLOR.RED) {
                    //情况一
                    parent.color = COLOR.BLACK;
                    uncle.color = COLOR.BLACK;
                    grandfather.color = COLOR.RED;

                    //当grandfather的节点颜色变为了红色之后,可能又会破坏其他树的结构,所以需要继续向上调整
                    cur = grandfather;
                    parent = cur.parent;
                }else {
                    //else 就表示uncle为空或者uncle不为空且uncle颜色为黑色的情况
                    if (cur == parent.left) {
                        rotateRight(parent);
                        RBTreeNode tmp = parent;
                        parent = cur;
                        cur = tmp;
                    }
                    rotateLeft(grandfather);
                    grandfather.color = COLOR.RED;
                    parent.color = COLOR.BLACK;
                }
            }
        }
        root.color = COLOR.BLACK;
        return true;
    }

    public void rotateRight(RBTreeNode parent) {
        RBTreeNode subL = parent.left;
        RBTreeNode subLR = subL.right;
        parent.left = subLR;
        if (subLR != null) {
            subLR.parent = parent;
        }
        RBTreeNode pParent = parent.parent;
        if (root == parent) {
            root = subL;
            subL.parent = null;
        }else {
            if (pParent.left == parent) {
                pParent.left = subLR;
            }else {
                pParent.right = subLR;
            }
            subLR.parent = pParent;
        }
        subL.right = parent;
        parent.parent = subL;
    }

    public void rotateLeft(RBTreeNode parent) {
        RBTreeNode subR = parent.right;
        RBTreeNode subRL = subR.left;
        RBTreeNode pParent = parent.parent;
        parent.right = subRL;
        if (subRL != null) {
            subRL.parent = parent;
        }
        if (root == parent) {
            root = subR;
            root.parent = pParent;
        }else {
            if (pParent.left == parent) {
                pParent.left = subR;
            }else {
                pParent.right = subR;
            }
            subR.parent = pParent;
        }
        subR.left = parent;
        parent.parent = subR;
    }
}

验证是否为红黑树

验证是否为红黑树,首先需要验证是否为二叉搜索树,然后验证每个路径上的黑色节点的数量是否相等,还需要验证在一条路径中是否存在两个连续的红色节点:

检验是否为二叉搜索树:

private int prev = Integer.MIN_VALUE;

public boolean isBinarySearchTree(RBTreeNode root) {
    if (root == null) return true;
    boolean l = isBinarySearchTree(root.left);
    if (!l) return false;
    if (prev < root.val) {
        prev = root.val;
        return isBinarySearchTree(root.right);
    }else return false;
}

校验所有路径中的黑色节点的数量是否相等:

//先计算出红黑树中其中一条路径中黑色节点的数量
public int blackNum(RBTreeNode root) {
    if (root == null) return 0;
    int count = 0;
    RBTreeNode cur = root;
    if (root.left != null) {
        while (cur != null) {
            if (cur.color == COLOR.BLACK) count++;
            cur = cur.left;
        }
    }else if (root.right != null){
        while (cur != null) {
            if (cur.color == COLOR.BLACK) count++;
            cur = cur.right;
        }
    }else {
        count = root.color == COLOR.BLACK ? 1 : 0;
    }
    return count;
}

//根据传入的路径中黑色节点的数量判断是否所有路径上的黑色节点的数量相同
public boolean checkBlackNum(RBTreeNode root, int pathBlackNum, int blackNum) {
    if (root == null) return true;
    if (root.color == COLOR.BLACK) pathBlackNum++;
    if (root.left == null && root.right == null) {
        if (pathBlackNum == blackNum) return true;
        else return false;
    }
    return checkBlackNum(root.left, pathBlackNum, blackNum)
            && checkBlackNum(root.right, pathBlackNum, blackNum);
}

判断一条路径上是否存在两个连续的红色节点:

public boolean checkRedColor(RBTreeNode root) {
    if (root == null) return true;
    if (root.color == COLOR.RED) {
        if (root.left != null && root.left.color == COLOR.RED) return false;
        if (root.right != null && root.right.color == COLOR.RED) return false;
    }
    return checkRedColor(root.left) && checkRedColor(root.right);
}

整理接口:

public boolean checkRBTree(RBTreeNode root) {
    int blackNum = blackNum(root);
    return isBinarySearchTree(root) && checkBlackNum(root,0,blackNum)
            && checkRedColor(root);
}

红黑树的删除

红黑树的删除操作主要涉及以下几个步骤:

  1. 定位节点:找到要删除的节点。如果要删除的节点有两个子节点,则需要找到该节点的后继节点(通常是右子树中的最小节点)来替代要删除的节点。
  2. 执行删除:执行标准的二叉搜索树(BST)的删除操作。这涉及到将后继节点的值复制到当前节点,并删除后继节点。如果后继节点有子节点,这些子节点将被转移到被删除节点的位置。
  3. 修复红黑树性质:删除节点后,可能会破坏红黑树的性质。因此,需要通过一系列的旋转和颜色更改操作来修复这些性质。这些操作包括左旋、右旋以及重新着色节点,以确保满足红黑树的五大特征。
  4. 处理特殊情况:在删除操作中,可能会遇到一些特殊情况,例如要删除的节点是黑色且拥有两个红色子节点,或者要删除的节点是根节点等。这些情况需要特殊的处理方式来确保红黑树的性质得到维护。

这里我就不为大家详细介绍了,大家下去可以自己了解了解。

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

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

相关文章

Nebula Siwi:基于图数据库的智能问答助手思路分析

本文重点分析 Nebula Siwi 智能问答思路&#xff0c;具体代码可参考[2]&#xff0c;使用的数据集为 Basketballplayer[3]。部分数据和 schema 如下所示&#xff1a; 一.智能问答可实现的功能 1.Nebula Siwi 源码整体结构 主要包括前段&#xff08;Vue&#xff09;和后端&#…

Unity3d C# 在WebGL平台加载并解析xml文件实现总结

前言 xml是可扩展标记语言&#xff0c;由一系列的元素、属性、值节点等构成的一个树形结构&#xff0c;除了可读性差一点&#xff0c;别的用于存储一些结构化的数据还是比较方便的。这个功能在Unity3d端的实现是比较方便快捷的&#xff1a; void GetXML1() {string filePath …

【力扣hot100】刷题笔记Day3

前言 以撒真是一不小心就玩太久了&#xff0c;终于解锁骨哥嘞&#xff0c;抓紧来刷题&#xff0c;今天是easy双指针&#xff01; 283. 移动零 - 力扣&#xff08;LeetCode&#xff09; 一个指针遍历&#xff0c;一个指针用于交换前面的0 class Solution(object):def moveZer…

简单说说mysql的日志

今天我们通过mysql日志了解mysqld的错误日志、慢查询日志、二进制日志&#xff0c;redolog, undolog等。揭示它们的作用和用途&#xff0c;让我们工作中更能驾驭mysql。 redo 日志 如果mysql事务提交后发生了宕机现象&#xff0c;那怎么保证数据的持久性与完整性&#xff1f;…

《计算机网络简易速速上手小册》第6章:网络性能优化(2024 最新版)

文章目录 6.1 带宽管理与 QoS - 让你的网络不再拥堵6.1.1 基础知识6.1.2 重点案例&#xff1a;提高远程办公的视频会议质量实现步骤环境准备Python 脚本示例注意事项 6.1.3 拓展案例1&#xff1a;智能家居系统的网络优化实现思路Python 脚本示例 6.1.4 拓展案例2&#xff1a;提…

挑战杯 LSTM的预测算法 - 股票预测 天气预测 房价预测

0 简介 今天学长向大家介绍LSTM基础 基于LSTM的预测算法 - 股票预测 天气预测 房价预测 这是一个较为新颖的竞赛课题方向&#xff0c;学长非常推荐&#xff01; &#x1f9ff; 更多资料, 项目分享&#xff1a; https://gitee.com/dancheng-senior/postgraduate 1 基于 Ke…

Megatron-LM源码系列(七):Distributed-Optimizer分布式优化器实现Part2

1. 使用入口 DistributedOptimizer类定义在megatron/optimizer/distrib_optimizer.py文件中。创建的入口是在megatron/optimizer/__init__.py文件中的get_megatron_optimizer函数中。根据传入的args.use_distributed_optimizer参数来判断是用DistributedOptimizer还是Float16O…

QSlider使用笔记

最近做项目使用到QSlider滑动条控件&#xff0c;在使用过的过程中&#xff0c;发现一个问题就是点滑动条上的一个位置&#xff0c;滑块并没有移动到鼠标点击的位置&#xff0c;体验感很差&#xff0c;于是研究了下&#xff0c;让鼠标点击后滑块移动到鼠标点击的位置。 1、event…

this指针详细总结 | static关键字 | 静态成员

文章目录 1.this指针引入2.this指针的特性3.静态成员3.1.C语言中static的基本用法3.2.C中的static关键字 1.this指针引入 class student { public:student(const string& name){ _name name; }void print(){// _name<>this->_name<>(*this)._name// 说一下…

【Linux】打包压缩跨系统/网络传输文件常用指令完结

Hello everybody!在今天的文章中我会把剩下的3-4个常用指令讲完&#xff0c;然后开始权限的讲解。那废话不多说&#xff0c;咱们直接进入正题&#xff01; 1.zip/unzip&tar命令 1.zip/unzip 在windows系统中&#xff0c;经常见到带有zip后缀的文件。那个东西就是压缩包。…

携程网首页案例制作

背景线性渐变 语法&#xff1a; background&#xff1a;linear-gradient&#xff08;起始方向&#xff0c;颜色1&#xff0c;颜色2&#xff0c;...&#xff09;&#xff1b; background&#xff1a;-webkit-linear-gradient&#xff08;left&#xff0c;red&#xff0c;blue&a…

使用Python的turtle模块实现简单的烟花效果

import turtle import random import math# 设置窗口大小 width, height 800, 600 screen turtle.Screen() screen.title("Fireworks Explosion") screen.bgcolor("black") screen.setup(width, height)# 定义烟花粒子类 class Particle(turtle.Turtle):…

ES6-let

一、基本语法 ES6 中的 let 关键字用于声明变量&#xff0c;并且具有块级作用域。 - 语法&#xff1a;let 标识符;let 标识符初始值; - 规则&#xff1a;1.不能重复声明let不允许在相同作用域内重复声明同一个变量2.不存在变量提升在同一作用域内&#xff0c;必须先声明才能试…

论文阅读-一种用于大规模分布式文件系统中基于深度强化学习的自适应元数据管理方案

名称&#xff1a; An Adaptive Metadata Management Scheme Based on Deep Reinforcement Learning for Large-Scale Distributed File Systems I. 引言 如今&#xff0c;大型集群文件系统的规模已达到PB甚至EB级别&#xff0c;由此产生的数据呈指数级增长。系统架构师不断设…

算法学习——华为机考题库7(HJ41 - HJ45)

算法学习——华为机考题库7&#xff08;HJ41 - HJ45&#xff09; HJ41 称砝码 描述 现有n种砝码&#xff0c;重量互不相等&#xff0c;分别为 m1,m2,m3…mn &#xff1b; 每种砝码对应的数量为 x1,x2,x3…xn 。现在要用这些砝码去称物体的重量(放在同一侧)&#xff0c;问能称…

STM32--揭秘中断(简易土货版)

抢占优先级响应优先级 视频学习--中断​​​​​​​

数据结构----队列(Queue)的概念、队列的使用、模拟实现队列、循环队列、模拟实现循环队列、双端队列、模拟实现双端队列

文章目录 1 概念2 队列的使用3 队列模拟实现4 循环队列4.1 循环队列 概念4.1 循环队列模拟实现 5. 双端队列 (Deque)6 用队列实现栈7 用栈实现队列 1 概念 队列&#xff1a;只允许在一端进行插入数据操作&#xff0c;在另一端进行删除数据操作的特殊线性表&#xff0c;队列具有…

会计的记账凭证

目录 一. 记账凭证的填制与审核1.1 收付款凭证1.2 转账凭证1.3 单式记账凭证 二. 记账凭证的编号 \quad 一. 记账凭证的填制与审核 \quad \quad 1.1 收付款凭证 \quad 注意︰ 凡是涉及货币资金之间收付款的业务如将库存现金存入银行或从银行提取现金等类经济业务。在实际工作中…

macOS的设置与常用软件(含IntelliJ IDEA 2023.3.2 Ultimate安装,SIP的关闭与开启)

目录 1 系统设置1.1 触控板1.2 键盘 2 软件篇2.1 [科学上网](https://justmysocks5.net/members/)2.1 [安装Chrome浏览器](https://www.google.cn/chrome/index.html)2.2 [安装utools](https://www.u.tools)2.3 [安装搜狗输入法](https://shurufa.sogou.com/)2.4 [安装snipaste…

Qt多语言翻译

Qt多语言翻译概述 Qt提供了非常简单易用的多语言翻译机制&#xff0c;其核心类为QTranslator.概括来说就是利用Qt的lupdate工具将项目中所有tr函数包裹的字符串提取到.ts文件中&#xff0c;然后使用Qt Linguist由专门的翻译人员对提取的.ts文件进行逐个单词短语的翻译工作. 翻译…