当前位置: 首页 > news >正文

【C++修炼之路】20.手撕红黑树

在这里插入图片描述
每一个不曾起舞的日子都是对生命的辜负

红黑树实现:RBTree

  • 前言
  • 一.红黑树的概念及性质
    • 1.1 红黑树的概念
    • 1.2 红黑树的性质
  • 二.红黑树的结构
    • 2.1 红黑树节点的定义
    • 2.2 红黑树类的封装
  • 三.红黑树的插入
    • 情况1:只变色
    • 情况2:变色+单旋
    • 情况3:双旋
    • 插入的代码实现:
  • 四.红黑树的验证
  • 五.红黑树实现代码(完整)
    • RBTree.h
    • Test.cpp
  • 六.红黑树与AVL树的比较

前言

在上一节中我们学到了AVL树的结构及其相关性质,对于平衡树来说,AVL无疑是最完美的结构,但实际上AVL树由于完美的结构因此也要花费一定的代价,由于AVL树的结构很敏感,查找虽然最快,但插入节点后一旦偏离AVL结构就会进行旋转,而旋转就会花费一定的性能。因此我们提到的map和set的底层为了防止这种性能上的缺失,即便AVL是非常完美的结构,也不采用AVL。而是采用我们这节所要描述的红黑树。

一.红黑树的概念及性质

1.1 红黑树的概念

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

可见红黑树的这种结构,虽然在不满足条件时会发生旋转,但敏感程度相比AVl树大大降低。

image-20230215124244019

1.2 红黑树的性质

  1. 每个结点不是红色就是黑色
  2. 根节点是黑色的
  3. 如果一个节点是红色的,则它的两个孩子结点是黑色的
  4. 对于每个结点,从该结点到其所有后代叶结点的简单路径上,均包含相同数目的黑色结点
  5. 每个叶子结点都是黑色的(此处的叶子结点指的是空结点)

需要注意的是:对于性质3,意思就是没有连续的红色结点。

思考: 为什么满足上面的性质,红黑树就能保证:其最长路径中节点个数不会超过最短路径节点个数的两倍?

就上面的这颗红黑树来说,有11条路径(需算到空)我们只观察黑色结点,发现是接近满二叉树的。对于红黑树的最短路径,最短的极限情况就是一条路径全是黑色的结点,因为其他路径也会有相同数目的黑节点加上一定数量的红色结点,而最长路径就是一黑一红相间的路径。可见,最长的结点个数是最短结点个数的二倍。

image-20230215125646671

这样就是红黑树的结构,且存在最长和最短的情况,最短为最长的二分之一。

由于红黑树确保没有一条路径会比其他路径长出俩倍,因此可以看成近似平衡,对于这种不确定的结构,自然就有最好的情况和最坏的情况。

  • 最好情况:(左右平衡)

全黑或者每条路径都是一黑一红相间的路径,接近满二叉树。那么此时设全黑的路径长度为h结点数量为N,则2^h - 1 + _ = N(_代表红色结点,由于一条路径红色结点数量一定小于等于黑色,所以可以忽略计算)此时,h = logN。

  • 最坏情况:(左右极其不平衡)

左子树全黑,且右子树一黑一红。那么此时最长路径为2*logN。

总结: 可以看出,在对数的加持下,即便是最坏情况,对于计算机来说相差也不是很大。虽不如AVL的极度平衡,但红黑树明显在插入节点时显得张弛有力。对于AVL来说,说是过刚易折也不为过,往往像红黑树那样增加一定的柔韧性,才是最后的选择。

二.红黑树的结构

2.1 红黑树节点的定义

相比较AVL树,红黑树的结点仍是三叉链这个结构,为后续不满足结构的条件旋转做准备。此外,和AVL树结点不同的是,红黑树不再有平衡因子这一变量,而是多了一个定义颜色的变量,颜色变量通过枚举实现。下面看看结点定义的代码:

enum Color//颜色采用枚举,但STL库采用的是特殊的bool值,后续会看
{RED,//0BLACK,//1
};template<class K, class V>
struct RBTreeNode
{pair<K, V> _kv;RBTreeNode<K, V>* _left;RBTreeNode<K, V>* _right;RBTreeNode<K, V>* _parent;Color _col;RBTreeNode(const pair<K, V>& kv):_kv(kv),_left(nullptr),_right(nullptr),_parent(nullptr),_col(RED)//默认哪种颜色都可以,因为后续会改{}};

2.2 红黑树类的封装

enum Color//颜色采用枚举,但STL库采用的是特殊的bool值,后续会看
{RED,//0BLACK,//1
};template<class K, class V>
struct RBTreeNode
{pair<K, V> _kv;RBTreeNode<K, V>* _left;RBTreeNode<K, V>* _right;RBTreeNode<K, V>* _parent;Color _col;RBTreeNode(const pair<K, V>& kv):_kv(kv),_left(nullptr),_right(nullptr),_parent(nullptr),_col(RED)//默认哪种颜色都可以,因为后续会改{}};template<class K, class V>
class RBTree
{typedef RBTreeNode<K, V> Node;
public://成员函数bool Insert(const pair<K, V>& kv){}
private:Node* _root = nullptr;
};

三.红黑树的插入

与AVL树一样,在插入中需要考虑的因素众多,但相比AVL,红黑树在插入时的情况少很多,因为其结构没有AVL树的高度平衡。


在红黑树的性质中,第三,第四条尤为重要。即:

  • 如果一个节点是红色的,则它的两个孩子结点是黑色的
  • 对于每个结点,从该结点到其所有后代叶结点的简单路径上,均包含相同数目的黑色结点

如果我们新插入的结点的颜色为黑的话,插入之后一定会打破上述第二个结构,导致每条路径的黑结点数量不同;如果新插入的节点的颜色为红色的话,也有可能不满足情况,如果插入节点的父节点为黑的话还好,但如果父节点为红,也就不满足这个条件了。因此,插入节点的颜色尤为重要,经过上述考虑,插入节点的颜色为红无疑是最好的选择,因为有可能满足条件,有可能不满足条件,但插入黑色一定不满足。如果插入红色的也不满足,那就后期处理就好了。

  • 插入节点的颜色必须为红

检测新节点插入后,红黑树的性质是否造到破坏
因为新节点的默认颜色是红色,因此:如果其双亲节点的颜色是黑色,没有违反红黑树任何性质,则不需要调整;但当新插入节点的双亲节点颜色为红色时,就违反了性质三不能有连在一起的红色节点,此时需要对红黑树分情况来讨论:
约定: cur为当前(插入)节点,p为父节点,g为祖父节点,u为叔叔节点

对于新节点的插入,与父节点和父节点的兄弟节点也有关,下面就看看各种情况:

情况1:只变色

  • cur为红,p为红,g为黑,u存在且为红

image-20230215145901714

通过上面图的步骤,我们大致知道了思路,如果红色结点连续,为了满足红黑树的不能有连续红色结点的性质,我们需要将其双亲也就是父亲结点变成黑色,如果这棵树是子树,那么为了保证黑色结点数量不变,我们需要将g变红,如果g的双亲仍是红色,那么就一直往上更新迭代。对于上图,实际上同样是个抽象图,因为a,b,c,d,e可以使任意的红黑树子树,情况也会随着这几个抽象树节点的增加而变化的非常多,因此我们只需要牢记这种抽象图即可。

cur和p均为红,违反了性质三,此处能否将p直接改为黑?
解决方式:将p,u改为黑,g改为红,然后把g当成cur,继续向上调整。

可以看出,红色结点的增加是因为插入节点,黑色节点的增加是为了保证红黑树结构的性质从红色结点变化而来的。

情况2:变色+单旋

  • cur为红,p为红,g为黑,u不存在/u存在且为黑

image-20230215151246360

如果不存在叔叔结点u,那么是这样的结构:image-20230215153215295

此时,新增的cur节点为红,那我们必须想办法将p变黑,此时就可以考虑将g为轴进行旋转的方式image-20230215153745528

这样,就可以使其满足红黑树的结构了,u结点存在且为黑和此同理。

接下来看看步骤:

p为g的左孩子,cur为p的左孩子,则g进行右单旋转;相反,
p为g的右孩子,cur为p的右孩子,则g进行左单旋转
p、g变色–p变黑,g变红

情况3:双旋

  • cur为红,p为红,g为黑,u不存在/u存在且为黑

看似与情况二一样,实际上区别是cur插入的左右不同。情况2为左,情况3为右。

image-20230215155228807

和上篇中的AVL一样,对于这种弯的结构,只进行一次旋转是不可能成功的,因此需要进行两部旋转,上图只旋转了一次,变成了情况2,因此需要再旋转一次,就能满足红黑树结构的性质。

步骤:

  1. p为g的左孩子,cur为p的右孩子,则针对p做左单旋转;相反,p为g的右孩子,cur为p的左孩子,则针对p做右单旋转。
    则转换成了情况2。转换成情况2之后,再根据情况2的步骤继续旋转:
  2. p为g的左孩子,cur为p的左孩子,则g进行右单旋转;相反,p为g的右孩子,cur为p的右孩子,则g进行左单旋转
    p、g变色–p变黑,g变红

image-20230215161337990

注意,经过左旋,变色位置不变,但对应位置的结点变了,因此在代码编写时需注意这个问题。

插入的代码实现:

注:Insert单拿出来观察,其利用的旋转函数与AVL一样,只不过去掉了平衡因子。

bool Insert(const pair<K, V>& kv)
{if (_root == nullptr){_root = new Node(kv);_root->_col = BLACK;//根节点为黑色return true;}Node* parent = nullptr;Node* cur = _root;while (cur){if (cur->_kv.first < kv.first){parent = cur;cur = cur->_right;}else if (cur->_kv.first > kv.first){parent = cur;cur = cur->_left;}else{return false;}}cur = new Node(kv);cur->_col = RED;//重要,插入的结点初始化成红色if (parent->_kv.first < kv.first){parent->_right = cur;cur->_parent = parent;}else{parent->_left = cur;cur->_parent = parent;}while (parent && parent->_col == RED)//如果父亲的颜色为红,才需要去处理{Node* grandfather = parent->_parent;//找到祖父才能找到叔叔if (parent == grandfather->_left){Node* uncle = grandfather->_right;//看叔叔颜色//情况1:uncle存在且为红if (uncle && uncle->_col == RED){parent->_col = uncle->_col = BLACK;grandfather->_col = RED;cur = grandfather;parent = cur->_parent;}else//情况2或3:不用考虑叔叔的问题,即叔叔为空还是为黑{if (cur == parent->_left)//情况2{//     g//   p// cRotateR(grandfather);parent->_col = BLACK;grandfather->_col = RED;}else//情况3{//     g//   p//     cRotateL(parent);RotateR(grandfather);cur->_col = BLACK;grandfather->_col = RED;}break;}}else//与上述代码的左右反过来了而已,步骤一样但左右相反。{Node* uncle = grandfather->_left;//情况1if (uncle && uncle->_col == RED){parent->_col = uncle->_col = BLACK;grandfather->_col = RED;cur = grandfather;parent = cur->_parent;}else//情况2和3{//  g//     p//        cif (cur == parent->_right){RotateL(grandfather);parent->_col = BLACK;grandfather->_col = RED;}else{//  g//     p//   cRotateR(parent);RotateL(grandfather);cur->_col = BLACK;grandfather->_col = RED;}}}}_root->_col = BLACK;return true;
}

此外,有插入就有删除,删除是插入的反向思维。删除太难了,不用学。真想了解看这个,但是没有代码:红黑树 - Never - 博客园 (cnblogs.com)

四.红黑树的验证

红黑树的检测分为两步:

  1. 检测其是否满足二叉搜索树(中序遍历是否为有序序列)
  2. 检测其是否满足红黑树的性质

函数代码:

bool IsBalance()//检查是否为红黑树结构
{if (_root == nullptr){return true;}if (_root->_col != BLACK){return false;}int ref = 0;Node* left = _root;while (left){if (left->_col == BLACK){++ref;}left = left->_left;}//遍历这棵树,就好了,检查是否存在连续的红结点。//检查父亲,因为孩子不一定有,但是一定有父亲return Check(_root, 0, ref);
}
bool Check(Node* root, int blackNum, int ref)
{if (root == nullptr){if (blackNum != ref){cout << "违反规则:一条路径上的黑色节点数量不同" << endl;return false;}return true;}if (root->_col == RED && root->_parent->_col == RED){cout << "违反规则,出现连续红色结点" << endl;}if (root->_col == BLACK){++blackNum;}return Check(root->_left, blackNum, ref)&& Check(root->_right, blackNum, ref);
}

五.红黑树实现代码(完整)

随机插入构建红黑树:
在这里插入图片描述
以升序插入构建红黑树:
在这里插入图片描述
以降序插入构建红黑树:
在这里插入图片描述

RBTree.h

#pragma onceenum Color//颜色采用枚举,但STL库采用的是特殊的bool值,后续会看
{RED,//0BLACK//1
};template<class K, class V>
struct RBTreeNode
{pair<K, V> _kv;RBTreeNode<K, V>* _left;RBTreeNode<K, V>* _right;RBTreeNode<K, V>* _parent;Color _col;RBTreeNode(const pair<K, V>& kv):_kv(kv), _left(nullptr), _right(nullptr), _parent(nullptr), _col(RED){}};template<class K, class V>
class RBTree
{typedef RBTreeNode<K, V> Node;
public:bool Insert(const pair<K, V>& kv){if (_root == nullptr){_root = new Node(kv);_root->_col = BLACK;//根节点为黑色return true;}Node* parent = nullptr;Node* cur = _root;while (cur){if (cur->_kv.first < kv.first){parent = cur;cur = cur->_right;}else if (cur->_kv.first > kv.first){parent = cur;cur = cur->_left;}else{return false;}}cur = new Node(kv);cur->_col = RED;//重要,插入的结点初始化成红色if (parent->_kv.first < kv.first){parent->_right = cur;cur->_parent = parent;}else{parent->_left = cur;cur->_parent = parent;}while (parent && parent->_col == RED)//如果父亲的颜色为红,才需要去处理{Node* grandfather = parent->_parent;//找到祖父才能找到叔叔if (parent == grandfather->_left){Node* uncle = grandfather->_right;//看叔叔颜色//情况1:uncle存在且为红if (uncle && uncle->_col == RED){parent->_col = uncle->_col = BLACK;grandfather->_col = RED;cur = grandfather;parent = cur->_parent;}else//情况2或3:不用考虑叔叔的问题,即叔叔为空还是为黑{if (cur == parent->_left)//情况2{//     g//   p// cRotateR(grandfather);parent->_col = BLACK;grandfather->_col = RED;}else//情况3{//     g//   p//     cRotateL(parent);RotateR(grandfather);cur->_col = BLACK;grandfather->_col = RED;}break;}}else//与上述代码的左右反过来了而已,步骤一样但左右相反。{Node* uncle = grandfather->_left;//情况1if (uncle && uncle->_col == RED){parent->_col = uncle->_col = BLACK;grandfather->_col = RED;cur = grandfather;parent = cur->_parent;}else//情况2和3{//  g//     p//        cif (cur == parent->_right){RotateL(grandfather);parent->_col = BLACK;grandfather->_col = RED;}else{//  g//     p//   cRotateR(parent);RotateL(grandfather);cur->_col = BLACK;grandfather->_col = RED;}}}}_root->_col = BLACK;return true;}//旋转代码和AVL一样,只是去掉了平衡因子void RotateL(Node* parent)//左单旋{//1.记录subR, subRLNode* subR = parent->_right;Node* subRL = subR->_left;parent->_right = subRL;if (subRL)//subRL不为空则需要连接到parent{subRL->_parent = parent;}Node* ppNode = parent->_parent;//记录保存subR->_left = parent;parent->_parent = subR;if (ppNode == nullptr)//说明根节点变化{_root = subR;_root->_parent = nullptr;}else//如果是局部子树{//判断ppNode之前是左连接还是右连接if (ppNode->_left == parent){ppNode->_left = subR;}else{ppNode->_right = subR;}subR->_parent = ppNode;}}void RotateR(Node* parent)//右单旋{Node* subL = parent->_left;Node* subLR = subL->_right;parent->_left = subLR;if (subLR){subLR->_parent = parent;}Node* ppNode = parent->_parent;subL->_right = parent;parent->_parent = subL;if (ppNode == nullptr){_root = subL;_root->_parent = nullptr;}else{if (ppNode->_left == parent){ppNode->_left = subL;}else{ppNode->_right = subL;}subL->_parent = ppNode;}}void Inorder(){_Inorder(_root);}bool IsBalance()//检查是否为红黑树结构{if (_root == nullptr){return true;}if (_root->_col != BLACK){return false;}int ref = 0;Node* left = _root;while (left){if (left->_col == BLACK){++ref;}left = left->_left;}//遍历这棵树,就好了,检查是否存在连续的红结点。//检查父亲,因为孩子不一定有,但是一定有父亲return Check(_root, 0, ref);}private:bool Check(Node* root, int blackNum, int ref){if (root == nullptr){if (blackNum != ref){cout << "违反规则:一条路径上的黑色节点数量不同" << endl;return false;}return true;}if (root->_col == RED && root->_parent->_col == RED){cout << "违反规则,出现连续红色结点" << endl;}if (root->_col == BLACK){++blackNum;}return Check(root->_left, blackNum, ref)&& Check(root->_right, blackNum, ref);}void _Inorder(Node* root){if (root == nullptr){return;}_Inorder(root->_left);cout << root->_kv.first << ":" << root->_kv.second << endl;_Inorder(root->_right);}Node* _root = nullptr;
};void TestRBTree1()
{int a[] = { 4, 2, 6, 1, 3, 5, 15, 7, 16, 14 };RBTree<int, int> t;for (auto e : a){t.Insert(make_pair(e, e));}t.Inorder();}

Test.cpp

#include<iostream>
#include<string>
#include<map>
#include<assert.h>
using namespace std;
#include"RBTree.h"
int main()
{TestRBTree1();return 0;
}

六.红黑树与AVL树的比较

红黑树和AVL树都是高效的平衡二叉树,增删改查的时间复杂度都是O(log2Nlog_2 Nlog2N),红黑树不追求绝对平衡,其只需保证最长路径不超过最短路径的2倍,相对而言,降低了插入和旋转的次数,所以在经常进行增删的结构中性能比AVL树更优,而且红黑树实现比较简单,所以实际运用中红黑树更多。

相关文章:

【C++修炼之路】20.手撕红黑树

每一个不曾起舞的日子都是对生命的辜负 红黑树实现:RBTree 前言一.红黑树的概念及性质1.1 红黑树的概念1.2 红黑树的性质二.红黑树的结构2.1 红黑树节点的定义2.2 红黑树类的封装三.红黑树的插入情况1&#xff1a;只变色情况2&#xff1a;变色单旋情况3&#xff1a;双旋插入的代…...

树状数组(高级数据结构)-蓝桥杯

一、简介树状数组 (Binary Indexed Tree,BIT)&#xff0c;利用数的二进制特征进行检索的一种树状结构。一种真正的高级数据结构&#xff1a; 二分思想、二叉树、位运算、前缀和。高效!代码极其简洁!二、基本应用数列a1,a2,....,an&#xff0c;操作&#xff1a;单点修改&#xf…...

Flink-多流转换(Union、Connect、Join)

文章目录多流转换分流基本合流操作联合&#xff08;Union&#xff09;连接&#xff08;Connect&#xff09;基于时间的合流——双流联结&#xff08;Join&#xff09;窗口联结&#xff08;Window Join&#xff09;间隔联结&#xff08;Interval Join&#xff09;窗口同组联结&a…...

kubeadmin安装k8s集群

目录 一 、环境部署 1、服务器规划 2、环境准备 二、所有节点安装docker 1、配置yum源&#xff0c;安装docker 2、配置daemon.json文件 三、所有节点安装kubeadm、kubelet 和kubectl 四、部署k8s集群 1、查看初始化需要的镜像 2、导入镜像 3、初始化kubeadm 3.1 方…...

java3月train笔记

java笔记 day01 一、jdk和idea下载及安装&#xff08;一般不建议装C盘&#xff09;&#xff1a; jdk&#xff1a;java开发环境 idea&#xff1a;开发工具&#xff08;软件&#xff09;&#xff0c;用来编写代码的 苍老师文档服务器&#xff1a;doc.canglaoshi.org jdk下载&…...

Apollo Config原理浅析

文章目录1. 简介2. 基本功能3. Apollo关键功能实现原理3.1 框架整体原理3.1.1 Apollo角色3.1.2 框架执行原理3.1.3 整体组成3.2 细节实现3.2.1 Eureka和不同角色机器的关系3.2.2 Meta Server的作用3.2.3 ReleaseMessage消息实现原理3.2.4 Client的通信方式1. 简介 apollo是携程…...

Kubernetes二 Kubernetes之实战以及pod详解

Kubernetes入门 一 Kubernetes实战 本章节将介绍如何在kubernetes集群中部署一个nginx服务&#xff0c;并且能够对其进行访问。 1.1 Namespace Namespace是kubernetes系统中的一种非常重要资源&#xff0c;它的主要作用是用来实现多套环境的资源隔离或者多租户的资源隔离。…...

机械革命黑苹果改造计划第四番-外接显示器、win时间不正确问题解决

问题 1.无法外接显示器 最大的问题就是目前无法外接显示器&#xff0c;因为机械革命大多数型号笔记本电脑的HDMI、DP接口都是直接物理接在独显上的&#xff0c;内屏用核显外接显示器接独显&#xff0c;英伟达独显也是黑苹果无法驱动的&#xff0c;而且发现机械革命tpyec接口还…...

Linux docker(03)可使用GPU渲染的x11docker实战总结

该系列文章的目的旨在之前的章节基础上&#xff0c;使用x11docker构建一个可以使用GPU的docker容器。该容器可以用于3D图形渲染/XR 等使用GPU渲染的程序调试和运行。 0 why docker 为什么非要用x11docker&#xff0c;而不是其他的docker呢&#xff1f; 因为一般的docker是不…...

【Linux操作系统】【综合实验一 Linux操作基础】

文章目录一、实验目的二、实验要求三、实验内容四、实验报告要求一、实验目的 要求掌握Linux基础操作&#xff0c;熟悉Linux行界面&#xff0c;并明白操作的原理以及目的&#xff1b;熟悉Linux系统环境。 二、实验要求 通过这个第一阶段实验&#xff0c;要求掌握以下操作与相…...

关于监控服务器指标、CPU、内存、警报的一些解决方案

文章目录关于监控服务器指标、CPU、内存、警报的一些解决方案Prometheus Grafana 配置 IRIS / Cach 监控服务器Prometheus简介特点架构图Grafana简介特点配置流程自定义Prometheus接口定义配置 Exporter 监控服务器系统资源简介配置流程使用 Alertmanager报警简介配置流程基于…...

vue3全家桶技术栈基础(一)

在认识vue3全家桶之前,先简单回顾一下vue2的全家桶 一.在vue2中&#xff0c;全家桶技术栈 技术栈: vue2 vue-cli vuex3vue-router3webpack elementUI 1.vue-cli 脚手架构建vue项目&#xff0c;CLI 服务是构建于 webpack 和 webpack-dev-server构建快速生成一个vue2的开发项…...

群晖-第2章-设置HTTPS访问

群晖-第2章-设置HTTPS访问 本章介绍如何通过HTTPS访问群晖&#xff0c;前置要求是完成群晖-第1章-IPV6的DDNS中的内容&#xff0c;可以是IPV4也可以是IPV6&#xff0c;或者你有公网IP&#xff0c;直接添加DNS解析也可以。只要能通过域名访问到nas就行。 本文参考自群晖添加SS…...

005 利用fidder抓取app的api,获得股票数据

一、下载安装fidder 百度搜索fidder直接下载&#xff0c;按提示安装即可。 二、配置fidder 1. 打开fidder&#xff0c;选择tools——options。 2. 选择HTTPS选项卡&#xff0c;勾选前三项&#xff0c;然后点击右侧【actions】&#xff0c;选择【trust root certificate】&a…...

京东测试进阶之路:初入测试碎碎念篇

1、基本的测试用例设计方法 基本的测试用例设计方法&#xff08;边界值分析、等价类划分等&#xff09;。 业务和场景的积累&#xff0c;了解测试需求以及易出现的bug的地方。 多维角度设计测试用例&#xff08;用户、业务流程、异常场景、代码逻辑&#xff09;。 2、需求分析 …...

华为OD机试 - 乘积最大值(JavaScript) | 机试题+算法思路+考点+代码解析 【2023】

乘积最大值 题目 给定一个元素类型为小写字符串的数组 请计算两个没有相同字符的元素长度乘积的最大值 如果没有符合条件的两个元素返回0 输入 输入为一个半角逗号分割的小写字符串数组 2 <= 数组长度 <= 100 0 < 字符串长度 <= 50 输出 两个没有相同字符的元…...

Java并发知识点

文章目录1. start()和run()方法的区别&#xff1f;2. volatile关键字的作用&#xff1f;使用volatile能够保证&#xff1a;防止指令重排3. sleep方法和wait方法有什么区别&#xff1f;sleep()方法4. 如何停止一个正在运行的线程&#xff1f;方法一&#xff1a;方法二&#xff1…...

前端 ES6 环境下 require 动态引入图片以及问题

前端 ES6 环境下 require 动态引入图片以及问题require 引入图片方式打包体积对比总结ES6 环境中&#xff0c;通过 require 的方式引入图片很方便&#xff0c;一直以来也没有出过什么问题&#xff0c;后来项目中&#xff0c;需要动态引入图片。 require 动态引入也容易实现&am…...

PCL 欧氏聚类分割

文章目录 一、应用背景1、点云分割算法的属性2、点云分割的挑战二、实现过程三、主要函数及代码实现1、主要函数2、核心代码3、效果实现四、参考文献一、应用背景 1、点云分割算法的属性 1)鲁棒性,比如树木是具有与汽车相区别的特征的,当点云数据的特征数量增加时,分割算…...

一台服务器最大能支持多少条TCP连接

一、一台服务器最大能打开的文件数 1、限制参数 我们知道在Linux中一切皆文件&#xff0c;那么一台服务器最大能打开多少个文件呢&#xff1f;Linux上能打开的最大文件数量受三个参数影响&#xff0c;分别是&#xff1a; fs.file-max &#xff08;系统级别参数&#xff09;&am…...

Linux应用开发之网络套接字编程(实例篇)

服务端与客户端单连接 服务端代码 #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <arpa/inet.h> #include <pthread.h> …...

shell脚本--常见案例

1、自动备份文件或目录 2、批量重命名文件 3、查找并删除指定名称的文件&#xff1a; 4、批量删除文件 5、查找并替换文件内容 6、批量创建文件 7、创建文件夹并移动文件 8、在文件夹中查找文件...

java调用dll出现unsatisfiedLinkError以及JNA和JNI的区别

UnsatisfiedLinkError 在对接硬件设备中&#xff0c;我们会遇到使用 java 调用 dll文件 的情况&#xff0c;此时大概率出现UnsatisfiedLinkError链接错误&#xff0c;原因可能有如下几种 类名错误包名错误方法名参数错误使用 JNI 协议调用&#xff0c;结果 dll 未实现 JNI 协…...

Matlab | matlab常用命令总结

常用命令 一、 基础操作与环境二、 矩阵与数组操作(核心)三、 绘图与可视化四、 编程与控制流五、 符号计算 (Symbolic Math Toolbox)六、 文件与数据 I/O七、 常用函数类别重要提示这是一份 MATLAB 常用命令和功能的总结,涵盖了基础操作、矩阵运算、绘图、编程和文件处理等…...

前端开发面试题总结-JavaScript篇(一)

文章目录 JavaScript高频问答一、作用域与闭包1.什么是闭包&#xff08;Closure&#xff09;&#xff1f;闭包有什么应用场景和潜在问题&#xff1f;2.解释 JavaScript 的作用域链&#xff08;Scope Chain&#xff09; 二、原型与继承3.原型链是什么&#xff1f;如何实现继承&a…...

【论文阅读28】-CNN-BiLSTM-Attention-(2024)

本文把滑坡位移序列拆开、筛优质因子&#xff0c;再用 CNN-BiLSTM-Attention 来动态预测每个子序列&#xff0c;最后重构出总位移&#xff0c;预测效果超越传统模型。 文章目录 1 引言2 方法2.1 位移时间序列加性模型2.2 变分模态分解 (VMD) 具体步骤2.3.1 样本熵&#xff08;S…...

vue3+vite项目中使用.env文件环境变量方法

vue3vite项目中使用.env文件环境变量方法 .env文件作用命名规则常用的配置项示例使用方法注意事项在vite.config.js文件中读取环境变量方法 .env文件作用 .env 文件用于定义环境变量&#xff0c;这些变量可以在项目中通过 import.meta.env 进行访问。Vite 会自动加载这些环境变…...

【Oracle】分区表

个人主页&#xff1a;Guiat 归属专栏&#xff1a;Oracle 文章目录 1. 分区表基础概述1.1 分区表的概念与优势1.2 分区类型概览1.3 分区表的工作原理 2. 范围分区 (RANGE Partitioning)2.1 基础范围分区2.1.1 按日期范围分区2.1.2 按数值范围分区 2.2 间隔分区 (INTERVAL Partit…...

项目部署到Linux上时遇到的错误(Redis,MySQL,无法正确连接,地址占用问题)

Redis无法正确连接 在运行jar包时出现了这样的错误 查询得知问题核心在于Redis连接失败&#xff0c;具体原因是客户端发送了密码认证请求&#xff0c;但Redis服务器未设置密码 1.为Redis设置密码&#xff08;匹配客户端配置&#xff09; 步骤&#xff1a; 1&#xff09;.修…...

R语言速释制剂QBD解决方案之三

本文是《Quality by Design for ANDAs: An Example for Immediate-Release Dosage Forms》第一个处方的R语言解决方案。 第一个处方研究评估原料药粒径分布、MCC/Lactose比例、崩解剂用量对制剂CQAs的影响。 第二处方研究用于理解颗粒外加硬脂酸镁和滑石粉对片剂质量和可生产…...