数据结构 - 8( AVL 树和红黑树 10000 字详解 )
一:二叉搜索树
1.1 回顾二叉搜索树
我们在树的章节中学习了二叉搜索树的概念。二叉搜索树满足以下性质:如果它的左子树存在,则左子树所有节点的值均小于根节点的值;如果右子树存在,则右子树所有节点的值均大于根节点的值。同时,左右子树也必须分别是二叉搜索树。这些特性使得二叉搜索树能够高效地执行查找、插入和删除操作。
从上述概念以及图中可以看出,二叉搜索树具有以下特性:
- 二叉搜索树中最左侧的节点是树中最小的节点,最右侧节点一定是树中最大的节点
- 采用中序遍历遍历二叉搜索树,可以得到一个有序的序列
1.2 二叉搜索树的查找
既然将其称之为二叉搜索树,所以这棵树最主要的作用肯定是进行查询,而且其查询原理特别简单,插入和删除操作也都依赖于查找来完成。那么我们可以思考一下今天:二叉搜索树的查找效率究竟有多高呢?
插入和删除操作都必须基于查找,因此查找效率直接影响二叉搜索树中各项操作的性能。在一个包含 n 个节点的二叉搜索树中,如果每个元素被查找的概率相同,那么节点越深,所需的比较次数就越多。此外,对于相同的关键码集合,插入顺序的不同可能导致生成不同结构的二叉搜索树,从而影响树的性能表现。
在最优情况下,二叉搜索树呈完全二叉树结构,此时平均比较次数最低;而在最差情况下,二叉搜索树则可能退化为单支树,导致平均比较次数显著增加,性能大幅下降。因此,我们不禁思考:是否存在一种改进方案,使得无论关键码的插入顺序如何,都能够保持二叉搜索树的最佳性能?由此我们就引入了二叉平衡树,而二叉平衡树又包含了 AVL 树和红黑树
二:AVL 树
尽管二叉搜索树能够提高查找效率,但当数据有序或接近有序时,二叉搜索树会退化为单支树,这使得查找元素的效率下降,类似于在顺序表中搜索。
为解决这一问题,一名俄罗斯数学家发明了一种自平衡的二叉搜索树 —— AVL 树。AVL 树保持每个节点左右子树的高度差的绝对值不超过1,从而降低树的高度,减少平均搜索长度,我们把每个节点左右子树的高度差的绝对值叫做平衡因子,AVL 树的平衡因子只能为 -1、0 或 1。
2.1 AVL 树节点的定义
为了能够更简单的实现一个 AVL 树,我们在定义节点的时候维护一个平衡因子,具体节点定义如下:
class AVLTreeNode {public AVLTreeNode left = null; // 节点的左孩子public AVLTreeNode right = null; // 节点的右孩子public AVLTreeNode parent = null; // 节点的双亲public int val = 0; // 节点的值public int bf = 0; // 当前节点的平衡因子 = 右子树高度 - 左子树的高度public AVLTreeNode(int val) {this.val = val;}
}
2.2 AVL 树的插入
AVL 树是在二叉搜索树的基础上引入平衡因子的,因此也可以视为一种特殊的二叉搜索树。AVL 树的插入过程可以分为两步:第一步是按照二叉搜索树的规则插入新节点;第二步是插入新节点后,pParent 的平衡因子需要进行调整。在插入之前,pParent 的平衡因子有三种情况:-1、0 和 1,根据插入的方向,可以分为以下两种情况:
- 如果 pCur 被插入到 pParent 的左侧,则将 pParent 的平衡因子减 1。
- 如果 pCur 被插入到 pParent 的右侧,则将 pParent 的平衡因子加 1。
此时,pParent 的平衡因子可能出现以下三种情况:
-
平衡因子为 0:这表示在插入之前,pParent 的平衡因子为 -1 或 1,插入后更新为 0,此时满足 AVL 树的平衡性质,插入成功。
-
平衡因子为 +1 或 -1:这表明插入之前,pParent 的平衡因子一定为 0,插入后更新为 +1 或 -1,此时以 pParent 为根的树的高度增加,需要继续向上更新父节点的平衡因子,因为子树的平衡因子由 0 变化成 - 1 和 1 说明树的高度增加了,所以插入新节点可能会影响父节点的高度。
-
平衡因子为 +2 或 -2:这种情况违反了 AVL 树的平衡性质,因此需要对 pParent 进行旋转处理,以恢复平衡。
boolean insert(int val) {// 按照二叉搜索树的规则将节点插入到 AVL 树中。// 此处省略代码// cur插入后,parent 的平衡因子一定会发生改变,此时必须对 parent 的平衡因子进行调整while (null != parent) {// 更新双亲节点的平衡因子if (cur == parent.left) {parent.bf--;} else {parent.bf++;}if (parent.bf == 0) {break;} else if (parent.bf == -1 || parent.bf == 1) {// 向上调整cur = parent;parent = cur.parent;} else {if (-2 == parent.bf) {// 右单旋// 此处代码省略} else {// 左旋// 此处代码省略}break;}}return true;
}
2.3 AVL 树的旋转
在原本平衡的 AVL 树中插入一个新节点可能导致树的不平衡。为了恢复平衡,树的结构需要进行调整。根据新节点插入的位置不同,AVL 树的旋转可以分为以下四种情况:
- 新节点插入到较高左子树的左侧:执行右单旋转。
- 新节点插入到较高右子树的右侧:执行左单旋转。
- 新节点插入到较高左子树的右侧:先进行左单旋转,然后再进行右单旋转(左右双旋)。
- 新节点插入到较高右子树的左侧:先进行右单旋转,然后再进行左单旋转(右左双旋)。
2.3.1 右单旋
在插入新节点之前,AVL 树是平衡的。当新节点被插入到 30 的左子树时,30 的左子树高度增加了一层,导致以 60 为根的二叉树失去了平衡。为了恢复平衡,必须将 60 的左子树高度减少一层,并增加右子树的高度,即将左子树的根往上提。
由于 60 大于 30,因此可以将 60 插入到 30 的右子树,以实现左子树高度的减少和右子树高度的增加。如果 30 已经有右孩子,右孩子的值一定大于 30 小于 60,因此新节点将放在 60 的左子树中。此外,由于 30 的右子树已经存在,此时又增加了一个 60 右子树,此时它就有 3 个分支了,这就不符合搜索树的特性了,所以要把一个子树进行转移,原右子树的节点需要转移至 60 的左子树,以保持 AVL 树的特性。旋转完成后,需要更新相关节点的平衡因子。
在旋转过程中,还需要考虑以下几种情况:
- 30 节点的右孩子可能存在,也可能不存在。
- 60 可能是树的根节点,也可能是子树的一部分。
- 如果 60 是根节点,旋转完成后需要更新根节点。
- 如果 60 是某个子树的根,可能是其父节点的左子树或右子树之一。
// 右单旋代码
private void rotateRight(TreeNode parent) {// 获取 parent 的左子节点和左子节点的右子节点TreeNode subL = parent.left;TreeNode subLR = subL.right;// 将 parent 的左子树指向 subL 的右子树parent.left = subLR;// 如果 subLR 不为空,更新其 parent 指针if (subLR != null) {subLR.parent = parent;}// 将 subL 变为新的根节点,并将 parent 设为 subL 的右子树subL.right = parent;// 记录当前 parent 的父节点TreeNode parParent = parent.parent; parent.parent = subL; // 更新 parent 的父节点为 subL// 如果 parent 是树的根节点,则更新根节点为 subLif (parent == root) {root = subL; // 更新全局根节点root.parent = null; // subL 的父节点设为 null} else {// 如果 parent 不是根节点,则调整其在父节点中的位置if (parParent.left == parent) {parParent.left = subL; // 如果 parent 是左子树,则更新左子树} else {parParent.right = subL; // 如果 parent 是右子树,则更新右子树}subL.parent = parParent; // 更新 subL 的父节点为 parParent}// 更新节点的平衡因子subL.bf = 0; // 新根节点的平衡因子设为 0parent.bf = 0; // 旧根节点的平衡因子设为 0
}
2.3.2 左单旋
左单旋的思路和实现可以参照右单旋的思路和实现:
// 左单旋
private void rotateLeft(AVLTreeNode parent) {// 获取 parent 的右子节点和右子节点的左子节点TreeNode subR = parent.right;TreeNode subRL = subR.left;// 1. 将 parent 的右子树指向 subRLparent.right = subRL;// 2. 如果 subRL 不为空,更新其父指针if (subRL != null) {subRL.parent = parent;}// 3. 将 subR 设为新的根节点,parent 作为 subR 的左子节点subR.left = parent;// 记录 parent 的父节点TreeNode parentParent = parent.parent;// 4. 更新 parent 的父节点为 subRparent.parent = subR;// 5. 如果 parent 是树的根节点,则更新根节点为 subRif (parent == root) {root = subR;root.parent = null; // subR 的父节点设为 null} else {// 更新父节点的指针,调整子树位置if (parent == parentParent.left) {parentParent.left = subR; // 更新左子树} else {parentParent.right = subR; // 更新右子树}subR.parent = parentParent; // 更新 subR 的父指针}// 更新平衡因子subR.bf = 0; // 新根节点的平衡因子设为 0parent.bf = 0; // 旧根节点的平衡因子设为 0
}
2.3.3 左右双旋
将双旋变成单旋后再旋转,即:先对 30 进行左单旋,然后再对 90 进行右单旋,旋转完成后再考虑平衡因子的更新。
注意:左右单旋和右左单旋的单旋和纯左单旋和纯右单旋不同,纯左单旋和纯右单旋拿走的是低的树,而左右单旋和右左单旋拿走的是高的树
private void rotateLR(TreeNode parent) {// 获取 parent 的左子节点和左子节点的右子节点TreeNode subL = parent.left;TreeNode subLR = subL.right;// 记录 subLR 的平衡因子int bf = subLR.bf;// 先对 subL 进行左旋,调整 parent 的左子树rotateLeft(parent.left);// 再对 parent 进行右旋,完成 LR 旋转rotateRight(parent);// 根据 subLR 的平衡因子更新节点的平衡因子if (bf == -1) {// 情况 1: subLR 的平衡因子为 -1subL.bf = 0; // subL 的平衡因子更新为 0parent.bf = 1; // parent 的平衡因子更新为 1subLR.bf = 0; // subLR 的平衡因子更新为 0} else if (bf == 1) {// 情况 2: subLR 的平衡因子为 1subL.bf = -1; // subL 的平衡因子更新为 -1parent.bf = 0; // parent 的平衡因子更新为 0subLR.bf = 0; // subLR 的平衡因子更新为 0}
}
2.3.4 右左双旋
将双旋变成单旋后再旋转,即:先对 90 进行右单旋,然后再对 60 进行左单旋,旋转完成后再考虑平衡因子的更新。
private void rotateRL(TreeNode parent) {// 获取 parent 的右子节点和右子节点的左子节点TreeNode subR = parent.right;TreeNode subRL = subR.left;// 记录 subRL 的平衡因子int bf = subRL.bf;// 先对 subR 进行右旋,调整 parent 的右子树rotateRight(parent.right);// 再对 parent 进行左旋,完成 RL 旋转rotateLeft(parent);// 根据 subRL 的平衡因子更新节点的平衡因子if (bf == -1) {// 情况 1: subRL 的平衡因子为 -1subR.bf = 0; // subR 的平衡因子更新为 0parent.bf = 1; // parent 的平衡因子更新为 1subRL.bf = 0; // subRL 的平衡因子更新为 0} else if (bf == 1) {// 情况 2: subRL 的平衡因子为 1subR.bf = -1; // subR 的平衡因子更新为 -1parent.bf = 0; // parent 的平衡因子更新为 0subRL.bf = 0; // subRL 的平衡因子更新为 0}
}
2.3.5 总结
新节点插入后,如果以 pParent 为根的子树不平衡了,即 pParent 的平衡因子为 2 或 -2,此时需要进行相应的旋转调整:
-
当 pParent 的平衡因子为 2:这意味着 pParent 的右子树较高,此时设 pParent 的右子树根为 pSubR。
- 如果 pSubR 的平衡因子为 1,那么执行左单旋转。
- 如果 pSubR 的平衡因子为 -1,那么执行右左双旋转。
-
当 pParent 的平衡因子为 -2:这意味着 pParent 的左子树较高,此时设 pParent 的左子树根为 pSubL。
- 如果 pSubL 的平衡因子为 -1,执行右单旋转。
- 如果 pSubL 的平衡因子为 1,执行左右双旋转。
总结来说,当 pParent 与其较高子树节点的平衡因子同号时,执行单旋转;当异号时,执行双旋转。旋转完成后,原 pParent 为根的子树的高度降低,并已经恢复平衡,因此不需要再向上更新。
2.4 AVL 树的验证.
要验证一棵树是否为AVL树,可以分为两个步骤:
- 验证其为二叉搜索树:如果对树进行中序遍历得到的序列是有序的,那么可以确定该树是二叉搜索树。
- 验证其平衡性:每个节点的左右子树高度差的绝对值不得超过 1。如果节点中没有显式存储平衡因子,可以通过计算每个节点的子树高度来判断。
private boolean isBalance(TreeNode root) {// 如果节点为空,返回平衡if (root == null) {return true;}// 计算左右子树的高度int leftH = height(root.left);int rightH = height(root.right);// 检查平衡因子是否正确if (rightH - leftH != root.bf) {System.out.println(root.val + " 平衡因子异常!");return false;}// 检查当前节点的平衡性以及左右子树的平衡性return Math.abs(leftH - rightH) <= 1 && isBalance(root.left) && isBalance(root.right);
}
2.5 AVL 树性能分析
AVL树是一种自平衡的二叉搜索树,它要求每个节点的左右子树高度差的绝对值不超过1,从而确保其查询操作具有高效的时间复杂度。然而,AVL树在进行结构修改时,如插入和删除操作,性能会显著下降。这是因为在插入时需要调整树的平衡,可能导致频繁的旋转,而在删除时,旋转调整甚至可能持续到根节点。因此,如果需要频繁高效的查询,AVL树是一个不错的选择;但如果需要频繁修改,此时 AVL 树并不适合。
三:红黑树
3.1 红黑树的概念
红黑树是一种自平衡的二叉搜索树,它在每个节点上增加了一个颜色属性,可以是红色或黑色。通过对从根节点到每个叶子节点路径上节点颜色的限制,红黑树确保没有任何一条路径的长度会超过其他路径长度的两倍,从而保持接近平衡。
3.2 红黑树的性质
- 最长路径的节点数最多是最短路径节点数的两倍。
- 每个节点要么是红色,要么是黑色。
- 根节点始终是黑色。
- 如果一个节点是红色,则其两个子节点必须是黑色,即不存在连续的红色节点,但是可以存在连续的黑色节点。
- 对于每个节点,从该节点到其所有后代叶子节点的简单路径上,必须包含相同数量的黑色节点。
- 每个叶子节点都是黑色,此处的叶子节点指的是空节点。
为何上述性质能够确保红黑树的最长路径中的节点数不会超过最短路径节点数的两倍? 由于红黑树的定义确保了从根到叶子的每条路径上的黑色节点数量相同,因此最短路径和最长路径之间的黑色节点数量是相等的。而最长路径上的红色节点只能插入在黑色节点之间,且最多只能插入一个红色节点,这样意味着最长路径最多会比最短路径多出一倍的节点。因此,红黑树保持了接近平衡,确保了其最长路径的节点数不会超过最短路径节点数的两倍。
3.3 红黑树节点的定义
class RBTreeNode {// 左子节点RBTreeNode left = null;// 右子节点RBTreeNode right = null;// 父节点RBTreeNode parent = null;// 节点的颜色,默认为红色COLOR color = RED; // 节点的值int val;public RBTreeNode(int val) {this.val = val;}
}
注意!节点默认颜色为红色,这样可以让黑红树在插入节点和维护平衡的时候更加高效
3.4 红黑树的插入
红黑树是在二叉搜索树的基础上增加了平衡性质的限制,因此红黑树的插入过程可以分为两个步骤:
- 按照二叉搜索树的规则插入新节点。
- 检查新节点插入后是否破坏了红黑树的性质。
由于新插入的节点默认为红色,如果其父节点的颜色为黑色,则不会违反红黑树的任何性质,因此无需调整。但当新插入节点的父节点为红色时,就违反了不能有连续的红色节点的性质。此时,需要根据不同情况进行调整。约定如下:cur 为当前节点,p 为父节点,g 为祖父节点,u 为叔叔节点。
- cur 为红,p 为红,g 为黑,u 存在且为红
当当前节点 cur 和父节点 p 均为红色时,违反了红黑树的性质三。在这种情况下,不能直接将父节点 p 改为黑色,因为这样可能会影响树的其他性质,因为你这样会直接导致黑色节点的个数加一,但是你要记住,所有路径上的黑色节点的个数要一样,你这样平白无故加一肯定是不行的。
解决方案:将父节点 p 和叔叔节点 u 的颜色改为黑色,同时将祖父节点 g 的颜色改为红色。然后,将祖父节点 g 视为新的当前节点 cur,继续向上调整树的结构,以维护红黑树的性质,这样就可以保证这条路径上的红色节点个数和黑色节点个数的相对数量不变。
- cur 为红,p 为红,g 为黑,u 不存在或者 u 为黑
- 如果父节点 p 是祖父节点 g 的左孩子,并且当前节点 cur 是 p 的左孩子,则进行右单旋转
- 如果父节点 p 是祖父节点 g 的右孩子,并且当前节点 cur 是 p 的右孩子,则进行左单旋转。
在旋转之后,将父节点 p 和祖父节点 g 的颜色进行调整:将 p 的颜色改为黑色,将 g 的颜色改为红色。
- cur 为红,p 为红,g 为黑,u 不存在或者 u 为黑
- 如果父节点 p 是祖父节点 g 的左孩子,并且当前节点 cur 是 p 的右孩子,则对 p 进行左单旋转。
- 如果父节点 p 是祖父节点 g 的右孩子,并且当前节点 cur 是 p 的左孩子,则对 p 进行右单旋转。
public boolean insert(int val) {// 插入逻辑的代码省略// 新节点插入后,如果父节点的颜色是红色,则一定违反了红黑树的性质三(不能有两个连续的红色节点)while (parent != null && parent.color == COLOR.RED) {RBTreeNode grandFather = parent.parent; // 获取祖父节点if (parent == grandFather.left) { // 情况一:父节点是祖父节点的左孩子RBTreeNode uncle = grandFather.right; // 获取叔叔节点if (uncle != null && uncle.color == COLOR.RED) { // 叔叔节点存在且为红色// 情况一处理:将叔叔和父节点改为黑色,祖父节点改为红色parent.color = COLOR.BLACK;uncle.color = COLOR.BLACK;grandFather.color = COLOR.RED;// 更新当前节点为祖父节点,继续往上调整cur = grandFather;parent = cur.parent;} else {// 如果叔叔节点不存在或为黑色,则进入情况二或情况三if (cur == parent.right) { // 情况三:当前节点是父节点的右孩子// 针对父节点进行左单旋转,将当前节点调整为父节点rotateLeft(parent);RBTreeNode temp = parent; // 临时存储父节点parent = cur; // 当前节点变为父节点cur = temp; // 继续处理之前的父节点}// 情况二:当前节点是父节点的左孩子,进行右单旋转rotateRight(grandFather);parent.color = COLOR.BLACK; // 将父节点颜色设为黑色grandFather.color = COLOR.RED; // 将祖父节点颜色设为红色}} else { // 情况一的反情况:父节点是祖父节点的右孩子// 获取叔叔节点RBTreeNode uncle = grandFather.left; if (uncle != null && uncle.color == COLOR.RED) { // 叔叔节点存在且为红色// 处理情況一parent.color = COLOR.BLACK;uncle.color = COLOR.BLACK;grandFather.color = COLOR.RED;// 更新当前节点为祖父节点,继续往上调整cur = grandFather;parent = cur.parent;} else {// 如果叔叔节点不存在或为黑色,则进入情况二或情况三if (cur == parent.left) { // 情况三:当前节点是父节点的左孩子// 针对父节点进行右单旋转,将当前节点调整为父节点rotateRight(parent);RBTreeNode temp = parent; // 临时存储父节点parent = cur; // 当前节点变为父节点cur = temp; // 继续处理之前的父节点}// 情况二:当前节点是父节点的右孩子,进行左单旋转rotateLeft(grandFather);parent.color = COLOR.BLACK; // 将父节点颜色设为黑色grandFather.color = COLOR.RED; // 将祖父节点颜色设为红色}}}// 最后,确保根节点始终是黑色,以维持红黑树的性质一root.color = COLOR.BLACK;return true;
}
3.5 红黑树验证
红黑树的检测分为两步:
- 检测其是否满足二叉搜索树,即中序遍历是否为有序序列
- 检测其是否满足红黑树的性质
// 中序遍历
public void inorder(RBTreeNode root) {if (root == null) {return;}inorder(root.left);System.out.print(root.val + " ");inorder(root.right);
}
// 检测是否满足红黑树的性质
public boolean isValidRBTree() {// 空树也是红黑树if (root == null) {return true;}// 检查根节点颜色是否为黑色if (root.color != COLOR.BLACK) {System.out.println("违反了性质2:根节点不是黑色");return false;}// 获取从根节点到叶子节点路径中的黑色节点数量int blackCount = 0;RBTreeNode cur = root;while (cur != null) {if (cur.color == COLOR.BLACK) {blackCount++;}cur = cur.left; // 遍历左子树}// 递归检查红黑树的有效性return _isValidRBtree(root, 0, blackCount);
}private boolean _isValidRBtree(RBTreeNode root, int pathCount, int blackCount) {if (root == null) {return true; // 空节点视为合法}// 统计当前路径中黑色节点的数量if (root.color == COLOR.BLACK) {pathCount++;}// 验证性质4:检查父节点是红色且当前节点也是红色RBTreeNode parent = root.parent;if (parent != null && parent.color == COLOR.RED && root.color == COLOR.RED) {System.out.println("违反了性质4:有连在一起的红色节点");return false;}// 检查是否为叶子节点if (root.left == null && root.right == null) {// 验证路径中黑色节点的总数是否一致if (pathCount != blackCount) {System.out.println("违反了性质5:路径中黑色节点数量不一致");return false;}}// 递归检查左右子树return _isValidRBtree(root.left, pathCount, blackCount) &&_isValidRBtree(root.right, pathCount, blackCount);
}
3.6 AVL 树和红黑树的比较
对于 AVL 树和红黑树的删除代码这里不做涉及,下面对这两种树进行比较:红黑树和 AVL 树都是高效的平衡二叉树,增、删、改和查的时间复杂度均为 O(log n)。与 AVL 树不同,红黑树并不追求绝对平衡,而是确保最长路径不超过最短路径的两倍。这种相对宽松的平衡策略降低了插入和旋转的次数,因此在频繁进行增删操作的场景中,红黑树的性能通常优于 AVL 树。此外,红黑树的实现相对简单,因此在实际应用中更为常见,在 Java 集合框架中的 TreeMap、TreeSet 底层使用的就是红黑树。
相关文章:
数据结构 - 8( AVL 树和红黑树 10000 字详解 )
一:二叉搜索树 1.1 回顾二叉搜索树 我们在树的章节中学习了二叉搜索树的概念。二叉搜索树满足以下性质:如果它的左子树存在,则左子树所有节点的值均小于根节点的值;如果右子树存在,则右子树所有节点的值均大于根节点…...
Tcp 通信简单demo思路
Server 端 -------------------------- 初始化部分 ------------------------------- 1.创建监听套接字: 使用socket(协议家族,套接字的类型,0) 套接字类型有 SOCK_STREAM:表示面向连接的套接字(Tcp协议)&…...
Cesium 导航控件(指南针 + 缩放按钮),自定义放置位置
Cesium 导航控件(指南针 缩放按钮) Cesium 导航控件(指南针 缩放按钮)的功能实现,从技术角度来看,可以整理出一整套实现流程和技术结构。这套流程结合了以下几个核心技术点: 1、整体功能目标 …...
MySQL的索引和事务
目录 1、索引 1.1 查看索引 1.2 创建索引 1.3 删除索引 1.4 索引的实现 2、事务 1、索引 索引等同于目录,属于针对查询操作的一个优化手段,可以通过索引来加快查询的速度,避免针对表进行遍历。 主键、unique和外键都是会自动生成索引的…...
【Fifty Project - D25】
今日完成记录 TimePlan完成情况9:00 - 11:30大论文修改修改情况书小论文修改√16:00 - 17 :00Leetcode√ Leetcode 每日一题 到达最后一个房间的最小时间II:和昨天的每日一题大致一样,增加一个条件&…...
pip下载tmp不够
问题描述 今天遇到一个小问题,在用pip安装的时候提示 ERROR: Could not install packages due to an OSError: [Errno 28] No space left on device 但我们单位用于生产环境的机器磁盘都是基本是论TB的,怎么会不够呢? 原因分析:…...
一种机载扫描雷达实时超分辨成像方法——论文阅读
一种机载扫描雷达实时超分辨成像方法 1. 专利的研究目标与产业意义1.1 研究目标与实际问题1.2 产业意义2. 专利的创新方法:滑窗递归优化与实时更新2.1 核心模型与公式2.2 与传统方法对比优势3. 实验设计与验证3.1 仿真参数3.2 实验结果4. 未来研究方向与挑战4.1 学术挑战4.2 技…...
nginx 会话保持(cookie的配置)
nginx会话保持主要有以下几种实现方式。 1. ip_hash ip_hash使用源地址哈希算法,将同一客户端的请求总是发往同一个后端服务器,除非该服务器不可用。 ip_hash语法: upstream backend { ip_hash; server backend1.example.com; server backend2.example.com; …...
nginx 实现动静分离
环境 : 三个机器,准备一个nginx代理 两个http 分别处理动态和静态 知识点--expires expires功能说明---(为客户端配置缓存时间) nginx缓存的设置可以提高网站性能,对于网站的图片,尤其是新闻网站,图片一旦发布,改动的可能是非常小的,为了减小对服务器请求的压力,提高…...
k8s的pod挂载共享内存
k8s的pod挂载共享内存,限制不生效问题: 注:/dev/shm 是 Linux 系统中用于共享内存的特殊路径。通过将 emptyDir 的 medium 设置为 Memory,可以确保 /dev/shm 正确地挂载到一个基于内存的文件系统,从而实现高效的共享内…...
Java高频面试之并发编程-14
hello啊,各位观众姥爷们!!!本baby今天又来报道了!哈哈哈哈哈嗝🐶 面试官:指令重排有限制没有?happens-before 又是什么? 在并发编程中,指令重排(…...
Linux基础(最常用基本命令)
1.查看文件ls 1.1 格式 ls 选项 参数,如:ls -lah ~/ 1.2 选项设置: -l:list 以列表方式显示文件 -h:human-readable 以人类可读的方式显示文件大小(会将纯数字转换为kb,mb) -a:all 显示所有的…...
【Python 日期和时间】
Python 中处理日期和时间主要依赖 datetime 模块,结合 dateutil 和 pytz 等第三方库可实现更复杂的需求。以下是日期和时间处理的核心知识点: 一、基础模块 1. datetime 模块 核心类:datetime, date, time, timedelta安装依赖:p…...
C#简易Modbus从站仿真器
C#使用NModbus库,编写从站仿真器,支持Modbus TCP访问,支持多个从站地址和动态启用/停用从站(模拟离线),支持数据变化,可以很方便实现,最终效果如图所示。 项目采用.net framework 4.…...
FPGA图像处理(四)------ 图像裁剪
timescale 1ns / 1ps // // Description: 图像裁剪算法 // module image_crop(input wire clk,input wire reset,input wire [10:0] img_width,input wire [10:0] img_height,input wire [10:0] img_x_start,input wire [10:0] img_x_end,input wire [10:0] img_y_start,input…...
1.MySQL数据库初体验
1.1数据库简介 1.1.1使用数据库的必要性 使用数据库可以高效且条理分明地存储数据,使人们能够更加迅速、方便地管理数据。 数据库特点: a.可以结构化存储大量地数据信息,方便用户进行有效的检索 b.可以有效地保持数据信息的一致性、完整…...
量子密码的轻量级通信协议笔记
代码笔记 本文档提供了项目代码的详细说明,包括代码结构、关键算法实现和重要的代码片段。 代码结构 . ├── Makefile # 构建系统配置 ├── coap_client.c # CoAP客户端实现 ├── coap_server.c # CoAP服务端实现 ├─…...
探索 C++ 在行业应用与技术融合中的核心价值
引言 在科技飞速发展的今天,C 作为一门兼具高性能与灵活性的编程语言,正深度融入游戏开发、人工智能、区块链等多个关键领域。其高效的内存管理、底层控制能力以及对现代硬件架构的深度优化,使其成为复杂系统开发的首选语言。本文将深入探讨…...
雷赛伺服电机
ACM0经济 编码器17位: ACM1基本 编码器23位磁编, ACM2通用 编码器24位光电, 插头定义:...
word文档基本操作: 编辑页眉页脚和插入目录
文章目录 引言I 编辑页眉页脚II 插入目录III 知识扩展基于axure画架构图基于Knife4j导出接口文档基于PDManer导出数据库设计文档引言 背景: 信息安全认证需要准备相关文件用于审核 一般的开发设计包含总体设计、概要设计、详细设计、接口设计、数据库设计、部署结构设计、原型…...
数据结构(二)——线性表的链式表示和实现
一、单链表 1.单链表的定义 如图所示每个节点包含两个域:数据域和指针域。数据域存储数据元素,指针域存储下一个节点的地址,因此指针指向的类型也是节点类型。每个指针都指向下一个节点,都是朝一个方向的,这样的链表称为单向链表…...
HTML10:iframe内联框架
iframe内部框架 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>内联框架学习</title> </head> <body> <!--iframe内联框架 src:地址 width-height:高度宽度 --> <iframe…...
C++代码随想录刷题知识分享-----数组交集—LeetCode 349
1 题目描述 给定两个整型数组 nums1 和 nums2,请返回它们的交集。 交集中 每个元素必须是唯一的。输出结果的顺序可以任意。 示例输入输出说明1nums1 [1,2,2,1], nums2 [2,2][2]2 只出现一次2nums1 [4,9,5], nums2 [9,4,9,8,4][4,9] 或 [9,4]顺序不作要求…...
Wireshark基本使用
本文会对Wireshark做简单介绍,带大家熟悉一下Wireshark的界面,以及如何使用过滤器。 接着会带大家查看TCP五层模型下,带大家回顾各层首部的格式。 最后会演示 Wireshark 如何抓取三次握手和四次挥手包的过程。 目录 一.Wireshark简介 二…...
学习c语言的链表的概念、操作(另一篇链表的笔记在其他的栏目先看这个)
在学习Linux之间我们先插入一下链表的知识 学习链表(一种数据结构思想) 链表和数组的区别和实现: 链表(链表是个好东西) 链表概念(什么是链表)? 链表就是数据结构->数据的存储…...
快速上手Pytorch Lighting框架 | 深度学习入门
快速上手Pytorch Lighting框架 | 深度学习入门 前言参考官方文档 介绍快速上手基本流程常用接口LightningModule\_\_init\_\_ & setup()\*\_step()configure_callbacks()configure_optimizers()load_from_checkpoint Trainer常用参数 可选接口LoggersTensorBoard Logger Ca…...
ffmpeg多媒体(音视频)处理常用命令
概览 总结一些音视频常用的ffmpeg处理命令,会不断更新,涉及一些重要命令,各位读者也可在评论区不断更新,维护起来,希望可以帮助大家快速解决问题! 1、音频相关 1.1 音频信息查看 ffmpeg -i test.wav 该命…...
QT中的网络请求
一、主程序(main.cpp) #include <QCoreApplication> #include <QNetworkAccessManager> #include <QNetworkReply> #include <QNetworkRequest> #include <QUrlQuery> #include <QJsonDocument> #include <QJso…...
Nacos源码—6.Nacos升级gRPC分析二
大纲 1.Nacos 2.x版本的一些变化 2.客户端升级gRPC发起服务注册 3.服务端进行服务注册时的处理 4.客户端服务发现和服务端处理服务订阅的源码分析 4.客户端服务发现和服务端处理服务订阅的源码分析 (1)Nacos客户端进行服务发现的源码 (2)Nacos服务端处理服务订阅请求的源…...
如何选择自己喜欢的cms
选择内容管理系统cms what is cms1.whatcms.org2.IsItWP.com4.Wappalyzer5.https://builtwith.com/6.https://w3techs.com/7. https://www.netcraft.com/8.onewebtool.com如何在不使用 CMS 检测器的情况下手动检测 CMS 结论 在开始构建自己的数字足迹之前,大多数人会…...
前端面经 作用域和作用域链
含义:JS中变量生效的区域 分类:全局作用域 或者 局部作用域 局部作用域:函数作用域 和 块级作用域ES6 全局作用域:在代码中任何地方都生效 函数中定义函数中生效,函数结束失效 块级作用域 使用let或const 声明 作用域链:JS查…...
开启智能Kubernetes管理新时代:kubectl-ai让操作更简单!
在如今的科技世界中,Kubernetes 已经成为容器编排领域的标杆,几乎所有现代应用的基础设施都离不开它。然而,面对复杂的集群管理和日常运维,许多开发者常常感到无所适从。今天,我们将为大家介绍一款结合了人工智能的强大工具——kubectl-ai。它不仅能帮助开发者更加顺畅地与…...
STM32 ADC
目录 ADC简介 逐次逼近型ADC STM32 ADC框图 输入通道 转换模式 •单次转换,非扫描模式 •连续转换,非扫描模式 •单次转换,扫描模式 •连续转换,扫描模式 触发控制 数据对齐 转换时间 校准 硬件电路 A…...
nextjs站点地图sitemap添加
app/sitemap.xml/route.ts (主站点地图索引) sitemap.xml 为文件夹名称 route.ts代码如下: import { NextResponse } from next/server; import { url } from /config/navigation; export async function GET() {// const entries generateMonthlyEntries();con…...
TCP/IP和OSI对比
TCP/IP模型的实际特性 网络层(IP层) 仅提供无连接的不可靠服务:TCP/IP模型的网络层核心协议是IP(Internet Protocol),其设计是无连接且不可靠的。IP数据包独立传输,不保证顺序、不确认交…...
【hadoop】Hbase java api 案例
代码实现: HBaseConnection.java package com.peizheng.bigdata;import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.hbase.HBaseConfiguration; import org.apache.hadoop.hbase.client.Connection; import org.apache.hadoop.hbase.client…...
深入理解Spring缓存注解:@Cacheable与@CacheEvict
在现代应用程序开发中,缓存是提升系统性能的重要手段。Spring框架提供了一套简洁而强大的缓存抽象,其中Cacheable和CacheEvict是两个最常用的注解。本文将深入探讨这两个注解的工作原理、使用场景以及最佳实践。 1. Cacheable注解 基本概念 Cacheable…...
[git]如何关联本地分支和远程分支
主题 本文总结如何关联git本地分支和远程分支的相关知识点。 详情 查看本地分支 git branch 查看远程分支 git branch -r 查看所有分支(本地远程) git branch -a 查看本地分支及其关联的远程分支(如有) git branch -vv 关联本地分支到远程分支: git branch …...
Linux58 ssh服务配置 jumpserver 测试双网卡 为何不能ping通ip地址
判断为NAT模式网卡 能ping 通外网 ens34为仅主机模式网卡 [rootlocalhost network-scripts]# ip route show default default via 10.1.1.254 dev ens33 proto static metric 100 10.0.0.0/8 dev ens33 proto kernel scope link src 10.1.1.37 metric 100 11.0.0.0/8 dev…...
chart.js 柱状图Y轴数据设置起始值
事情的起因, 我以为是: chart.js 柱状图Y轴数据显示不全, 因为数据是浮点数, 换了整数测试还不行, 多次更换数据, 数据显示不全仍然存在, 而且是不固定位置的不显示。 直到相同数据换了折…...
算法题(142):木材加工
审题: 本题需要我们找到可以将木头切割至少k段的单段长度最长值 思路: 方法一:暴力解法 首先我们知道单段长度的最长值就是数组中数据的最大值max,所以我们可以遍历1~max的数据,将他们确定为l,然后计算出当…...
嵌入式学习--江协51单片机day3
今天学的东西挺多的,包括:自己设计的小应用,矩阵键盘,矩阵键盘密码锁,控制按键led流水灯,定时器时钟 (那个视频真的煎熬,连续两个1小时的简直要命,那个时钟也是听的似懂…...
Linux命令行参数注入详解
本文主要聚焦 Linux 系统及其 ELF 二进制文件,深入探讨参数注入的原理与防范措施。 核心术语解析 在进入主题之前,我们先厘清几个关键术语,以确保理解的准确性: 参数解析器 当程序被调用时,操作系统会向程序的 main…...
【HCIP】----OSPF综合实验
实验题目 实验需求: 1,R5为ISP,其上只能配置IP地址;R4作为企业边界路由器, 出口公网地址需要通过PPP协议获取,并进行chap认证 2,整个OSPF环境IP基于172.16.0.0/16划分; 3࿰…...
C++模板笔记
Cpp模板笔记 文章目录 Cpp模板笔记1. 为什么要定义模板2. 模板的定义2.1 函数模板2.1.1 函数模板的重载2.1.2 头文件与实现文件形式(重要)2.1.3 模板的特化2.1.4 模板的参数类型2.1.5 成员函数模板2.1.6 使用模板的规则 2.2 类模板2.3 可变参数模板 模板…...
二极管的动态特性
主要内容 二极管的单向导电特性并不十分理想,这是因为二极管的本质是有P型半导体和N型半导体接触形成的PN结。 PN结除了除了构成单向到点的二极管外,还存在一个结电容,这个结电容会导致"双向"导电。 也就是说,这会让二…...
WSL(Windows Subsystem for Linux)入门
目录 1.简介2.安装与配置3.常用命令4.进阶使用4.1 文件系统交互4.2 网络互通4.3 配置代理4.4 运行 GUI 程序4.5 Docker 集成 1.简介 WSL 是 Windows 系统内置的 Linux 兼容层,允许直接在 Windows 中运行 Linux 命令行工具和应用程序,无需虚拟机或双系统…...
k8s术语之secret
在kubernetes中,还存在一种和ConfigMap非常类似的对象,称之为Secret对象。它主要用于存储敏感信息,例如密码、密钥、证书等等。 首先使用base64对数据进行编码 rootmaster pvs ]# echo -n admin | base64 YWRtaW4 实例:隐藏mysql密…...
Vue2 中 el-dialog 封装组件属性不生效的深度解析(附 $attrs、inheritAttrs 原理)
Vue2 中 el-dialog 封装组件属性不生效的深度解析(附 $attrs、inheritAttrs 原理) 在使用 Vue2 和 Element UI 进行组件封装时,我们常会遇到父组件传入的属性不生效的情况,比如在封装的 el-dialog 组件中传入 width"100%&qu…...
使用Compose编排工具搭建Ghost博客系统
序:需要提前自备一台部署好docker环境的虚拟机,了解并熟练compose编排工具 在Centos7中,在线/离线安装Docker:https://blog.csdn.net/2301_82085712/article/details/147140694 Docker编排工具---Compose的概述及使用࿱…...