【C++进阶】AVL树
0.前言
前面我们已经学习过二叉搜索树了,但如果我们是用二叉搜索树来封装map和set等关联式容器是有缺陷的,很可能会退化为单分支的情况,那样效率就极低了,那么有没有方法来弥补二叉搜索树的缺陷呢?
那么AVL树就出现了,通过AVL树的右单旋、左单旋、左右单旋,右左单旋等操作来防止二叉搜索树退化为单分支的情况出现。
因此,两位俄罗斯的数学家G.M.Adelson-Velskii和E.M.Landis在1962年发明了一种解决上述问题的方法:当向二叉搜索树中插入新结点后,如果能保证每个结点的左右子树高度之差的绝对值不超过1(需要对树中的结点进行调整),即可降低树的高度,从而减少平均搜索长度。
AVL树也是以这两位大佬名字的首字母来命名的。
1.AVL树的概念
一棵AVL树或者是空树,或者是具有以下性质的二叉搜索树是AVL树:
- 它的左右子树都是AVL树
- 左右子树高度之差(简称平衡因子)的绝对值不超过1(-1/0/1)
我们这里使用的平衡因子是右子树高度 - 左子树高度。
2.AVL树节点的定义
代码如下:
template<class K, class V>
struct AVLTreeNode
{AVLTreeNode<K, V>* _left; //该节点的左孩子AVLTreeNode<K, V>* _right; //该节点的右孩子AVLTreeNode<K, V>* _parent; //该节点的双亲pair<K, V> _kv; //该节点的key和valueint _bf; //balance factor 平衡因子AVLTreeNode(const pair<K, V>& kv) //该节点初始化:_left(nullptr) , _right(nullptr), _parent(nullptr), _kv(kv) , _bf(0){}
};
3.AVL树的插入
AVL树就是在二叉搜索树的基础上引入了平衡因子,因此AVL树也可以看成是二叉搜索树。那么
AVL树的插入过程可以分为两步:
- 按照二叉搜索树的方式插入新节点
- 调整节点的平衡因子
插入思路:
1.按照搜索树规则插入
2.更新插入节点的祖先节点的平衡因子
a.插入父亲的右边,父亲的平衡因子--
b.插入父亲的左边,父亲的平衡因子++
c.父亲平衡因子 == 0, 父亲所在子树高度不变,不再继续往上更新。插入结束
d.父亲平衡因子 == 1 or -1, 父亲所在子树高度变了,继续往上更新。
e.父亲平衡因子 == 2 or -2, 父亲所在子树已经不平衡了,需要旋转处理。
更新中不可能出现其他值,插入之前树是AVL树,平衡因子要么是1 -1 0, ++ --
最多就是c/d/e三种情况。
代码如下:
bool Insert(const pair<K, V>& kv){if (_root == nullptr){_root = new Node(kv);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);if (parent->_kv.first < kv.first){parent->_right = cur;}else{parent->_left = cur;}cur->_parent = parent;//更新平衡因子while (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 = parent->_parent;}else if (parent->_bf == -2 || parent->_bf == 2){//当前子树出现了问题,需要旋转平衡一下if (parent->_bf == -2 && cur->_bf == -1){//右单旋RotateR(parent);}else if (parent->_bf == 2 && cur->_bf == 1){//左单旋RotateL(parent);}else if (parent->_bf == -2 && cur->_bf == 1){//左右双旋RotateLR(parent);}else if (parent->_bf == 2 && cur->_bf == -1){//右左双旋//RotateRL(parent);}break;}else{//理论上不会出现这种情况,但仍要判断,大佬都不能保证自己的代码没有bugassert(false);}}return true;}
4.AVL树的旋转
4.1 右单旋
插入新节点要插入较高左子树的左侧(左左)-》右单旋
图形解析:
实现代码如下:
void RotateR(Node* parent){Node* subL = parent->_left;Node* subLR = subL->_right;parent->_left = subLR;if (subLR) //subLR有可能为空,不为空时才能调整subLR的父节点subLR->_parent = parent;subL->_right = parent;//parent不一定就是根节点,也可能是子树,所以要设置一个ppNode来标记parent的父节点Node* ppNode = parent->_parent;parent->_parent = subL;if (parent == _root){_root = subL;_root->_parent = nullptr;}else{if (ppNode->_left == parent){ppNode->_left = subL;}else{ppNode->_right = subL;}subL->_parent = ppNode;}subL->_bf = parent->_bf = 0;}
4.2 左单旋
插入新节点要插入较高右子树的右侧(右右)-》左单旋
图形解析:
代码如下:
void RotateL(Node* parent){Node* subR = parent->_right;Node* subRL = subR->_left;parent->_right = subRL;if (subRL) //subRL有可能为空,不为空时才能调整subRL的父节点subRL->_parent = parent;subR->_left = parent;//parent不一定就是根节点,也可能是子树,所以要设置一个ppNode来标记parent的父节点Node* ppNode = parent->_parent;parent->_parent = subR;if (parent == _root){_root = subR;_root->_parent = nullptr;}else{if (ppNode->_left == parent){ppNode->_left = subR;}else{ppNode->_right = subR;}subR->_parent = ppNode;}subR->_bf = parent->_bf = 0;}
4.3 左右双旋
插入新节点要插入较高左子树的右侧(左右)-》左右双旋
图形解析:
对于插入的位置根据h的高度和插入的位置时的平衡因子出现的最终的结果可分为三种情况:
1.h == 0
60自己就是新增节点。bf == 0
此时的平衡因子为,30 60 90节点都为0。
2.h > 0
a.新增节点插入的是60的左子树中 bf == -1
此时的平衡因子为,30节点为0,90为0,60为1
b.新增节点插入的是60的右子树中bf == 1
此时的平衡因子为,30节点为-1,90为0,60为0
代码如下:
void RotateLR(Node* parent){Node* subL = parent->_left;Node* subLR = subL->_right;int bf = subLR->_bf;RotateL(parent->_left);RotateR(parent);if (bf == 0){subL->_bf = 0;subLR->_bf = 0;parent->_bf = 0;}else if (bf == -1){subL->_bf = 0;subLR->_bf = 0;parent->_bf = 1;}else if (bf == 1){subL->_bf = -1;subLR->_bf = 0;parent->_bf = 0;}else{//理论上不会出现的情况assert(false);}}
4.4 右左双旋
插入新节点要插入较高右子树的左侧(右左)-》右左双旋
与左右双旋类似
代码如下:
void RotateRL(Node* parent){Node* subR = parent->_right;Node* subRL = subR->_left;int bf = subRL->_bf;RotateR(parent->_right);RotateL(parent);if (bf == 0){parent->_bf = 0;subRL->_bf = 0;subR->_bf = 0;}else if (bf == -1){parent->_bf = 0;subRL->_bf = 0;subR->_bf = 1;}else if (bf == 1){parent->_bf = -1;subRL->_bf = 0;subR->_bf = 0;}else{//理论上不会出现的情况assert(false);}}
总结:
假如以pParent为根的子树不平衡,即pParent的平衡因子为2或者-2,分以下情况考虑
1. pParent的平衡因子为2,说明pParent的右子树高,设pParent的右子树的根为pSubR
当pSubR的平衡因子为1时,执行左单旋
当pSubR的平衡因子为-1时,执行右左双旋
2. pParent的平衡因子为-2,说明pParent的左子树高,设pParent的左子树的根为pSubL
当pSubL的平衡因子为-1是,执行右单旋
当pSubL的平衡因子为1时,执行左右双旋
旋转完成后,原pParent为根的子树个高度降低,已经平衡,不需要再向上更新。
5.AVL树的验证
5.1 验证其是否为二叉搜索树
给一个序列进行一次中序遍历,看是否有序,有序即为二叉搜索树。
void TestAVLTree1()
{int a[] = { 8, 3, 1, 10, 6, 4, 7, 14, 13 };AVLTree<int, int> t1;for (auto e : a){t1.Insert({ e,e });}t1.InOrder();
}
5.2 验证其是否为平衡树
bool _IsBalance(Node* root){if (root == nullptr){return true;}int leftHeight = _Height(root->_left);int rightHeight = _Height(root->_right);//不平衡if (abs(leftHeight - rightHeight) >= 2){cout << root->_kv.first << endl;return false;}//顺便检查一下平衡因子是否正确if (rightHeight - leftHeight != root->_bf){cout << root->_kv.first << endl;return false;}return _IsBalance(root->_left)&& _IsBalance(root->right);}
6.AVL树的删除(了解)
因为AVL树也是二叉搜索树,可按照二叉搜索树的方式将节点删除,然后再更新平衡因子,只不错与删除不同的时,删除节点后的平衡因子更新,最差情况下一直要调整到根节点的位置。
7.AVL树的性能
AVL树是一棵绝对平衡的二叉搜索树,其要求每个节点的左右子树高度差的绝对值都不超过1,这样可以保证查询时高效的时间复杂度,即logN。但是如果要对AVL树做一些结构修改的操作,性能非常低下,比如:插入时要维护其绝对平衡,旋转的次数比较多,更差的是在删除时,有可能一直要让旋转持续到根的位置。因此:如果需要一种查询高效且有序的数据结构,而且数据的个数为静态的(即不会改变),可以考虑AVL树,但一个结构经常修改,就不太适合。
8.AVL树的整体模拟代码实现
#pragma once
#include<iostream>
using namespace std;
#include<assert.h>template<class K, class V>
struct AVLTreeNode
{AVLTreeNode<K, V>* _left; //该节点的左孩子AVLTreeNode<K, V>* _right; //该节点的右孩子AVLTreeNode<K, V>* _parent; //该节点的双亲pair<K, V> _kv; //该节点的key和valueint _bf; //balance factor 平衡因子AVLTreeNode(const pair<K, V>& kv) //该节点初始化:_left(nullptr) , _right(nullptr), _parent(nullptr), _kv(kv) , _bf(0){}
};template<class K, class V>
class AVLTree
{typedef AVLTreeNode<K, V> Node;
public:bool Insert(const pair<K, V>& kv){if (_root == nullptr){_root = new Node(kv);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);if (parent->_kv.first < kv.first){parent->_right = cur;}else{parent->_left = cur;}cur->_parent = parent;//更新平衡因子while (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 = parent->_parent;}else if (parent->_bf == -2 || parent->_bf == 2){//当前子树出现了问题,需要旋转平衡一下if (parent->_bf == -2 && cur->_bf == -1){//右单旋RotateR(parent);}else if (parent->_bf == 2 && cur->_bf == 1){//左单旋RotateL(parent);}else if (parent->_bf == -2 && cur->_bf == 1){//左右双旋RotateLR(parent);}else if (parent->_bf == 2 && cur->_bf == -1){//右左双旋RotateRL(parent);}break;}else{//理论上不会出现这种情况,但仍要判断,大佬都不能保证自己的代码没有bugassert(false);}}return true;}void RotateR(Node* parent){Node* subL = parent->_left;Node* subLR = subL->_right;parent->_left = subLR;if (subLR) //subLR有可能为空,不为空时才能调整subLR的父节点subLR->_parent = parent;subL->_right = parent;//parent不一定就是根节点,也可能是子树,所以要设置一个ppNode来标记parent的父节点Node* ppNode = parent->_parent;parent->_parent = subL;if (parent == _root){_root = subL;_root->_parent = nullptr;}else{if (ppNode->_left == parent){ppNode->_left = subL;}else{ppNode->_right = subL;}subL->_parent = ppNode;}subL->_bf = parent->_bf = 0;}void RotateL(Node* parent){Node* subR = parent->_right;Node* subRL = subR->_left;parent->_right = subRL;if (subRL) //subRL有可能为空,不为空时才能调整subRL的父节点subRL->_parent = parent;subR->_left = parent;//parent不一定就是根节点,也可能是子树,所以要设置一个ppNode来标记parent的父节点Node* ppNode = parent->_parent;parent->_parent = subR;if (parent == _root){_root = subR;_root->_parent = nullptr;}else{if (ppNode->_left == parent){ppNode->_left = subR;}else{ppNode->_right = subR;}subR->_parent = ppNode;}subR->_bf = parent->_bf = 0;}void RotateLR(Node* parent){Node* subL = parent->_left;Node* subLR = subL->_right;int bf = subLR->_bf;RotateL(parent->_left);RotateR(parent);if (bf == 0){subL->_bf = 0;subLR->_bf = 0;parent->_bf = 0;}else if (bf == -1){subL->_bf = 0;subLR->_bf = 0;parent->_bf = 1;}else if (bf == 1){subL->_bf = -1;subLR->_bf = 0;parent->_bf = 0;}else{//理论上不会出现的情况assert(false);}}void RotateRL(Node* parent){Node* subR = parent->_right;Node* subRL = subR->_left;int bf = subRL->_bf;RotateR(parent->_right);RotateL(parent);if (bf == 0){parent->_bf = 0;subRL->_bf = 0;subR->_bf = 0;}else if (bf == -1){parent->_bf = 0;subRL->_bf = 0;subR->_bf = 1;}else if (bf == 1){parent->_bf = -1;subRL->_bf = 0;subR->_bf = 0;}else{//理论上不会出现的情况assert(false);}}Node* Find(const K& key){Node* cur = _root;while (cur){if (cur->_kv.first < key){cur = cur->_right;}else if (cur->_kv.first > key){cur = cur->_left;}else{return cur;}}return nullptr;}void InOrder(){_InOrder(_root);cout << endl;}bool IsBalance(){return _IsBalance(_root);}int Height(){return _Height(_root);}int Size(){return _Size(_root);}
private:int _Size(Node* root){return root == nullptr ? 0 : _Size(root->_left) + _Size(root->_right) + 1;}int _Height(Node* root){if (root == nullptr){return 0;}return max(_Height(root->_left), _Height(root->_right)) + 1;}bool _IsBalance(Node* root){if (root == nullptr){return true;}int leftHeight = _Height(root->_left);int rightHeight = _Height(root->_right);//不平衡if (abs(leftHeight - rightHeight) >= 2){cout << root->_kv.first << endl;return false;}//顺便检查一下平衡因子是否正确if (rightHeight - leftHeight != root->_bf){cout << root->_kv.first << endl;return false;}return _IsBalance(root->_left)&& _IsBalance(root->_right);}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 TestAVLTree1()
{int a[] = { 8, 3, 1, 10, 6, 4, 7, 14, 13 };AVLTree<int, int> t1;for (auto e : a){t1.Insert({ e,e });}t1.InOrder();
}
void TestAVLTree2()
{int a[] = { 8, 3, 1, 10, 6, 4, 7, 14, 13 };AVLTree<int, int> t1;for (auto e : a){t1.Insert({ e,e });}t1.InOrder();cout << t1.IsBalance() << endl;
}
相关文章:

【C++进阶】AVL树
0.前言 前面我们已经学习过二叉搜索树了,但如果我们是用二叉搜索树来封装map和set等关联式容器是有缺陷的,很可能会退化为单分支的情况,那样效率就极低了,那么有没有方法来弥补二叉搜索树的缺陷呢? 那么AVL树就出现了&…...

云部署最简单python web
最近在玩云主机,考虑将简单的web应用装上去,通过广域网访问一下,代码很简单,所以新手几乎不会碰到什么问题。 from flask import Flaskapp Flask(__name__)app.route(/) def hello_world():return Hello, World!app.route(/gree…...

【Pytorch】【MacOS】14.m1芯片使用mps进行深度模型训练
读者要先自行安装python以及anaconda,并且配置pytorch环境 第一步 测试环境 import torch # 判断macOS的版本是否支持 print(torch.backends.mps.is_available()) # 判断mps是否可用 print(torch.backends.mps.is_built())如果第一个语句为False,说明当前…...

go学习笔记-从圣经中抄录的接口值的思考
接口值 接口值,由两个部分组成,一个具体的类型和那个类型的值 下面4个语句中,变量w得到了3个不同的值。( 开始和最后的值是相同的) var w io.Writer w os.Stdout w new(bytes.Buffer) w nil var w io.Writer var…...

ICML 2024 时空数据(Spatial-Temporal)论文总结
2024ICML(International Conference on Machine Learning,国际机器学习会议)在2024年7月21日-27日在奥地利维也纳举行 (好像ICLR24现在正在维也纳开)。 本文总结了ICML 24有关时空数据(Spatial-temporal) 的相关论文…...

多线程(C++11)
多线程(C) 文章目录 多线程(C)前言一、std::thread类1.线程的创建1.1构造函数1.2代码演示 2.公共成员函数2.1 get_id()2.2 join()2.3 detach()2.4 joinable()2.5 operator 3.静态函数4.类的成员函数作为子线程的任务函数 二、call…...

HLS入门
目录 一、 内容介绍二、 理解HLS2.1 HLS是什么?与VHDL/Verilog编程技术有什么关系?2.2 HLS有哪些关键技术问题?目前存在什么技术局限性? 三、 HLS在Quartus上的实现3.1 配置环境3.2 测试 四、 参考链接 一、 内容介绍 理解HLSHLS在Quartus上…...

电信光猫的USB存储对外网开放访问
前提条件当然是要有公网IP地址了,没有的话去找电信索要,然后可以使用动态域名正常访问。 我的电信光猫发现共享访问速度还可以,会有31M/s左右的写入速度 但是有一个不方便的是,无法从外网提供访问,SMB协议所用的445端…...

世界上首位AI程序员诞生,AI将成为人类的对手吗?
3月13日,世界上第一位AI程序员Devin诞生,不仅能自主学习新技术,自己改Bug,甚至还能训练和微调自己的AI模型,表现已然远超GPT-4等“顶流选手”。 AI的学习速度如此之快,人类的教育能否跟上“机器学习”的速…...

什么是创造力?如何判断自己的创造力?
创造力,主要表现为创新思想、发现和创造新事物的能力,是知识,智力和能力的综合能力,尤其是在职业发展方面,创造力具有重要的意义,企业的核心竞争力就来源于创造力,这就需要具有创造力的员工来推…...

Elasticsearch集群搭建学习
Elasticsearch集群聚合、集群搭建 RestClient查询所有高亮算分控制 数据聚合DSL实现Bucket聚合DSL实现Metrics聚合RestAPI实现聚合 拼音分词器如何使用拼音分词器?如何自定义分词器?拼音分词器注意事项? 自动补全数据同步集群搭建ES集群结构创…...

数据库(vb.net+OleDB+Access)简易学生信息管理系统
在我们日常生活当中,数据库一词往往离不开我们的编程界,在学校、仓库等方面起着存储数据及数据关系作用的文件。相较于Excel,Access可以存储无限多的记录,内容也十分丰富,例如文本、数字、日期、T&F等。而且不需要…...

Android 自定义图片进度条
用系统的Progressbar,设置图片drawable作为进度条会出现图片长度不好控制,容易被截断,或者变形的问题。而我有个需求,使用图片背景,和图片进度,而且在进度条头部有个闪光点效果。 如下图: 找了…...

对话:用言语构建深刻的思想碰撞
对话:用言语构建深刻的思想碰撞 在写书中,对话是一种有力的工具,能与读者进行有效的沟通和交流,引发深思和反思。它不仅是信息传递的方式,更是加深情感、探讨主题和吸引读者参与的桥梁。你应从读者的角度思考…...

Linux完整版命令大全(九)
4. linux压缩备份命令 ar 功能说明:建立或修改备存文件,或是从备存文件中抽取文件。语 法:ar[-dmpqrtx][cfosSuvV][a<成员文件>][b<成员文件>][i<成员文件>][备存文件][成员文件]补充说明:ar可让您集合许多…...

solidworks画螺栓学习笔记
螺栓 单位mm 六边形 直径16mm 水平约束 拉伸 选择厚度6mm 拉伸切除 画相切圆 切除厚度6mm,反向切除 ,拔模角度45 螺栓 直径9mm,长度30mm 倒角 直径1mm,角度45 异形孔向导 螺纹线 偏移打勾,距离为2mm&#…...

【Spark】加大hive表在HDFS存的每个文件的大小
配置参数: spark.hadoop.hive.exec.orc.default.stripe.size78643200 spark.hadoop.orc.stripe.size78643200 spark.hadoopRDD.targetBytesInPartition78643200 spark.hadoop.hive.exec.dynamic.partition.modenonstrict spark.sql.sources.partitionOverwriteMode…...

2024 年 5 个 GO REST API 框架
什么是API? API是一个软件解决方案,作为中介,使两个应用程序能够相互交互。以下一些特征让API变得更加有用和有价值: 遵守REST和HTTP等易于访问、广泛理解和开发人员友好的标准。API不仅仅是几行代码;这些是为移动开…...

socket地址理解
socket介绍 套接字的基本概念 1. 套接字的定义: 套接字(socket)是计算机网络中用于通信的端点,它抽象了不同主机上应用进程之间双向通信的机制。 2. 套接字的作用: 套接字连接应用进程与网络协议栈,使…...

Gopeed的高级用法
Gopeed是一个开源全平台下载器,具体简介请参考: “狗屁下载器”?Gopeed - 开源全平台下载器 (免费轻量 / 比 Aria2 好用 / 远程下载) - 异次元软件世界 (iplaysoft.com) 这里主要介绍下自己摸索出来的 Gopeed 的高级做法。 有的网站添加的…...

OpenHarmony系统使用gdb调试init
前言 OpenAtom OpenHarmony(简称“OpenHarmony”)适配新的开发板时,启动流程init大概率会出现问题,其为内核直接拉起的第一个用户态进程,问题定位手段只能依赖代码走读和增加调试打印,初始化过程中系统崩溃…...

【SpringCloud】Spring Cloud基本介绍
目录 回顾架构分类单体架构分布式架构微服务架构什么是微服务优点缺点微服务的架构特征:微服务架构面临的挑战技术挑战微服架构的设计原则微服务概念提供者(Provider)消费者(Consumer)RPC和Restful集群分布式 总结 服务拆分和远程调用服务拆分原则服务拆分示例 思考…...

全域运营是本地生活服务的新模式吗?
最近,本地生活赛道又出现了一个新的说法,即全域运营是本地生活的下半场。事实上,这一论断并非空穴来风,而是有真凭实据。 作为多家互联网大厂重点布局的业务板块,本地生活的火爆程度早已有目共睹。根据多家互联网大厂…...

机器视觉-硬件
机器视觉-硬件 镜头焦距凸透镜焦点不止一个相机镜头由多个镜片组成对焦和变焦 镜头光圈光圈的位置光圈系数F 镜头的景深景深在光路中的几何意义 远心镜头远心镜头的种类远心镜头特性应用场景 镜头的分辨率镜头反差镜头的MTF曲线镜头的靶面尺寸镜头的几何相差相机镜头接口螺纹接…...

机器学习实验 --- 逻辑回归
第1关:逻辑回归核心思想 任务描述 本关任务:根据本节课所学知识完成本关所设置的编程题 #encodingutf8 import numpy as npdef sigmoid(t):完成sigmoid函数计算:param t: 负无穷到正无穷的实数:return: 转换后的概率值:可以考虑使用np.exp()函数#*****…...

浅谈C++函数
目录 一、函数的概念二、调用函数的两个前提三、函数传参的三种形式四、函数返回类型 一、函数的概念 函数是C程序的基本模块,通常一个C程序由一个或多个函数组成。函数可以完成用户指定的任务,一般分为库函数和用户自定义的函数。函数由函数头和函数体…...

6.小程序页面布局 - 账单明细
文章目录 1. 6.小程序页面布局 - 账单明细1.1. 竞品1.2. 布局分析1.3. 布局demo1.4. 页面实现-头部1.5. 账单明细1.5.1. 账单明细-竞品分析1.5.2. 账单明细-实现1.5.2.1. 账单明细-实现-mock数据1.5.2.2. 每日收支数据的聚合整理1.5.2.3. 页面scroll-view 1.6. TODO 1. 6.小程序…...

记录ES7.X更新数据的低级错误
背景:新项目复用之前同事遗留下的方法 问题:ES跨索引更新数据错误 排查:复用同事的方法有问题,他直接使用ES别名更新数据导致,只有一个索引时无问题,当多个索引使用同一别名时会出现异常 解决࿱…...

【简单介绍下链表基础知识】
🎥博主:程序员不想YY啊 💫CSDN优质创作者,CSDN实力新星,CSDN博客专家 🤗点赞🎈收藏⭐再看💫养成习惯 ✨希望本文对您有所裨益,如有不足之处,欢迎在评论区提出…...

leetcode 2915.和为目标值的最长子序列的长度
思路:01背包 这个背包问题很经典了,但是这里涉及到一个问题,就是我们转化问题的时候发现,这个背包需要正好装满才行。这里我们把长度作为价值,也就是说每一个数的价值都是1。 我们需要把dp初始化为全部为负数&#x…...