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

【C++】二叉搜索树+变身 = AVL树

头像
🚀个人主页:@小羊
🚀所属专栏:C++
很荣幸您能阅读我的文章,诚请评论指点,欢迎欢迎 ~

动图描述

目录

  • 前言
  • 一、AVL树
  • 二、AVL树的实现
    • 2.1 平衡因子
    • 2.2 旋转处理
      • 2.2.1 左单旋:插入新节点后单纯的右边高
      • 2.2.2 右单旋:插入新节点后单纯的左边高
      • 2.2.3 左右旋:插入新节点后不是单纯的左边高
      • 2.2.4 右左旋:插入新节点后不是单纯的右边高
    • 2.3 验证AVL树的平衡
  • 三、完整代码


前言

本文仅适合了解二叉搜索树,但不了解AVL树底层原理的同学阅读哦。

本篇文章不会带你从头到尾实现AVL树,但会带你深入理解AVL树是怎么实现平衡的,怎么通过旋转变换实现保持平衡,以及实现平衡过程中的细节应该怎么处理等。


一、AVL树

前面的文章中我们分析过二叉搜索树的性能,得到的结果是理想情况下二叉搜索树的时间复杂度为O(LogN),但在极端情况下(即树蜕化为单边树时),这些操作的时间复杂度会退化为O(n),即使情况不那么极端,效率也不是特别高。

为了防止二叉搜索树出现一边偏高的情况,就需要想办法让二叉搜索树尽量保持平衡,所以两位苏联数学家(或称为俄罗斯数学家)G.M. Adelson-Velsky和E.M. Landis就发明了AVL树,其任何节点的两个子树的高度最大差别为1。

AVL树是具有一下性质的二叉搜索树:

  • 其左右子树都是AVL树
  • 左右子树高度差不超过1

二、AVL树的实现

本篇文章将沿用之前文章中Key-Value模型的代码,不再从底层开始实现,主要介绍在插入新节点后如何保持二叉搜索树的平衡问题。

2.1 平衡因子

如何保证AVL树的左右子树高度差不超过1?在AVL树的每个节点中存一个平衡因子,本文我们定义平衡因子 = 此节点右子树的高度 - 左子树的高度

  • 插入在左子树,平衡因子 - -
  • 插入在右子树,平衡因子++

更新祖先节点的平衡因子时,我们首先需要找到祖先节点,因此每个节点中还需要增加一个指向父节点的指针。
按照我们的需求,其AVL树的节点可以定义为:

template<class K, class V>
struct AVLTreeNode
{pair<K, V> _kv;AVLTreeNode<K, V>* _left;AVLTreeNode<K, V>* _right;AVLTreeNode<K, V>* _parent;int _bf;//平衡因子//构造AVLTreeNode(const pair<K, V>& kv):_kv(kv),_left(nullptr),_right(nullptr),_parent(nullptr),_bf(0){}
}

是否继续往上更新祖先节点的平衡因子,要看parent所在子树的高度是否发生变化。

插入新节点后其父节点的平衡因子有以下几种情况:

  1. parent的平衡因子 == 0
    parent的平衡因子更新前是 -1 / 1,新节点插入在矮的那边,高度不变,不再往上更新
  2. parent的平衡因子 == 1 / -1
    parent的平衡因子更新前是0,parent所在子树高度都变化了,需要往上更新
  3. parent的平衡因子 == 2 / -2
    parent的平衡因子更新前是 -1 / 1,插入新节点后树不再平衡,需要旋转处理
pcur = new Node(kv);
if (parent->_kv.first > kv.first)//判断新节点应该插入左还是右
{parent->_left = pcur;
}
else
{parent->_right = pcur;
}
pcur->_parent = parent;//与父节点链接关系while (parent)//有可能更新到根节点去
{parent->_bf = parent->_left == pcur ? parent->_bf - 1 : parent->_bf + 1;if (parent->_bf == 0)//插入前后高度不变{break;}else if (parent->_bf == 1 || parent->_bf == -1){//高度变了,继续往上更新pcur = parent;parent = parent->_parent;}else if (parent->_bf == 2 || parent->_bf == -2){//插入节点后二叉树不平衡了,需要旋转处理}else{assert(false);//检测AVL树是否异常}
}

2.2 旋转处理

当二叉搜索树出现不平衡的情况时,需要旋转处理,对应插入后二叉搜索树的各种情况,主要有四种旋转的方式来保持平衡。

其中:

  • h代表子树的高度,可以是0、1、2…
  • 我们用能代表所有情况的四种类型的抽象图来研究旋转方式,单纯研究某几种情况没有意义

原则:

  1. 保持搜索树的性质
  2. 降低高度,控制平衡

2.2.1 左单旋:插入新节点后单纯的右边高

在这里插入图片描述

旋转处理过程中,我们主要关注三个节点(以上图为例):10(标记为parent)、30(标记为subR)、b(标记为subLR)。

在旋转过程中,有以下几种情况需要考虑:

  1. subR的左孩子可能存在,也可能不存在
  2. parent可能是根节点,也可能是子树。如果是根节点,旋转完成后,要更新根节点;如果是子树,可能是某个节点的左子树,也可能是右子树
//左单旋
void RotateL(Node* parent)
{Node* subR = parent->_right;Node* subRL = subR->_left;//subRL是有可能为空的parent->_right = subRL;subR->_left = parent;Node* parentparent = parent->_parent;parent->_parent = subR;if (parentparent == nullptr)//subR有可能变成根{_root = subR;}else{if (parentparent->_left == parent){parentparent->_left = subR;}else{parentparent->_right = subR;}}subR->_parent = parentparent;if (subRL){subRL->_parent = parent;}parent->_bf = subR->_bf = 0;//更新平衡因子
}

旋转处理过程中主要是处理各节点的父节点指针的指向和平衡因子的更新。


2.2.2 右单旋:插入新节点后单纯的左边高

在这里插入图片描述

其处理方式和左单旋相似,可参考左单旋。

//右单旋
void RotateR(Node* parent)
{Node* subL = parent->_left;Node* subLR = subL->_right;parent->_left = subLR;subL->_right = parent;Node* parentparent = parent->_parent;parent->_parent = subL;if (parentparent == nullptr){_root = subL;}else{if (parentparent->_left == parent){parentparent->_left = subL;}else{parentparent->_right = subL;}}subL->_parent = parentparent;if (subLR){subLR->_parent = parent;}subL->_bf = parent->_bf = 0;
}

2.2.3 左右旋:插入新节点后不是单纯的左边高

在这里插入图片描述

这种情况只用左旋或右旋只会原地打转,不能降低平衡。
我们需要先对subL进行左单旋,再对parent进行右单旋,最后更新平衡因子。

  • 双旋后平衡因子的更新要根据插入新节点后subLR的平衡因子来分情况讨论
  • 双旋最终结果是把subLR推到最上面,让其平衡因子为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){parent->_bf = 0;subL->_bf = 0;subLR->_bf = 0;}else if (bf == -1){parent->_bf = 1;subL->_bf = 0;subLR->_bf = 0;}else if (bf == 1){parent->_bf = 0;subL->_bf = -1;subLR->_bf = 0;}else{assert(false);}
}

2.2.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;subR->_bf = 0;subRL->_bf = 0;}else if (bf == -1){parent->_bf = 0;subR->_bf = 1;subRL->_bf = 0;}else if (bf == 1){parent->_bf = -1;subR->_bf = 0;subRL->_bf = 0;}else{assert(false);}
}

旋转完成后,原parent为根的子树个高度降低,已经平衡,不需要再向上更新。


2.3 验证AVL树的平衡

我们可以分别计算出其左子树和右子树的高度,将其相减的值与节点中记录的平衡因子的值比较,看是否符合我们的预期。

int _Height(Node* root)
{if (root == nullptr){return 0;}int leftheight = _Height(root->_left);int rightheight = _Height(root->_right);return leftheight > rightheight ? leftheight + 1 : rightheight + 1;
}bool _isBalanceTree(Node* root)
{if (root == nullptr){return true;}int leftheight = _Height(root->_left);int rightheight = _Height(root->_right);int bf = rightheight - leftheight;if (abs(bf) > 1){cout << root->_kv.first << "高度差异常" << endl;return false;}if (root->_bf != bf){cout << root->_kv.first << "平衡因子异常" << endl;return false;}return _isBalanceTree(root->_left) && _isBalanceTree(root->_right);
}

三、完整代码

template<class K, class V>
struct AVLTreeNode
{pair<K, V> _kv;AVLTreeNode<K, V>* _left;AVLTreeNode<K, V>* _right;AVLTreeNode<K, V>* _parent;int _bf;//平衡因子AVLTreeNode(const pair<K, V>& kv):_kv(kv),_left(nullptr),_right(nullptr),_parent(nullptr),_bf(0){}
};template<class K, class V>
class AVLTree
{typedef AVLTreeNode<K, V> Node;
public:AVLTree() = default;AVLTree(const AVLTree<K, V>& t){_root = copy(t._root);}AVLTree<K, V>& operator=(AVLTree<K, V> t){swap(_root, t._root);return *this;}~AVLTree(){Destroy(_root);_root = nullptr;}bool Find(const K& key){Node* pcur = _root;while (pcur){if (key < pcur->_kv.first){pcur = pcur->_left;}else if (key > pcur->_kv.first){pcur = pcur->_right;}else{return true;}}return false;}bool Insert(const pair<K, V>& kv){//没有节点时需要单独处理if (_root == nullptr){_root = new Node(kv);return true;}Node* pcur = _root;Node* parent = nullptr;while (pcur){if (kv.first < pcur->_kv.first){parent = pcur;pcur = pcur->_left;}else if (kv.first > pcur->_kv.first){parent = pcur;pcur = pcur->_right;}else{return false;}}pcur = new Node(kv);if (parent->_kv.first > kv.first)//判断新节点应该插入左还是右{parent->_left = pcur;}else{parent->_right = pcur;}pcur->_parent = parent;//与父节点链接关系//更新平衡因子while (parent)//有可能更新到根节点去{parent->_bf = parent->_left == pcur ? parent->_bf - 1 : parent->_bf + 1;if (parent->_bf == 0)//插入前后高度不变{break;}else if (parent->_bf == 1 || parent->_bf == -1){//高度变了,继续往上更新pcur = parent;parent = parent->_parent;}else if (parent->_bf == 2 || parent->_bf == -2){//插入节点后二叉树不平衡了,需要旋转处理if (parent->_bf == 2 && pcur->_bf == 1){RotateL(parent);}else if (parent->_bf == -2 && pcur->_bf == -1){RotateR(parent);}else if (parent->_bf == 2 && pcur->_bf == -1){RotateRL(parent);}else if (parent->_bf == -2 && pcur->_bf == 1){RotateLR(parent);}break;//不管是哪种情况,旋转完后子树的高度没有变化,所以不再调整}else{assert(false);//检测AVL树是否异常}}return true;}void InOrder(){_InOrder(_root);cout << endl;}bool IsBalanceTree(){return _isBalanceTree(_root);}private://左单旋void RotateL(Node* parent){Node* subR = parent->_right;Node* subRL = subR->_left;//subRL是有可能为空的parent->_right = subRL;subR->_left = parent;Node* parentparent = parent->_parent;parent->_parent = subR;if (parentparent == nullptr)//subR有可能变成根{_root = subR;}else{if (parentparent->_left == parent){parentparent->_left = subR;}else{parentparent->_right = subR;}}subR->_parent = parentparent;if (subRL){subRL->_parent = parent;}parent->_bf = subR->_bf = 0;//更新平衡因子}//右单旋void RotateR(Node* parent){Node* subL = parent->_left;Node* subLR = subL->_right;parent->_left = subLR;subL->_right = parent;Node* parentparent = parent->_parent;parent->_parent = subL;if (parentparent == nullptr){_root = subL;}else{if (parentparent->_left == parent){parentparent->_left = subL;}else{parentparent->_right = subL;}}subL->_parent = parentparent;if (subLR){subLR->_parent = parent;}subL->_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){parent->_bf = 0;subL->_bf = 0;subLR->_bf = 0;}else if (bf == -1){parent->_bf = 1;subL->_bf = 0;subLR->_bf = 0;}else if (bf == 1){parent->_bf = 0;subL->_bf = -1;subLR->_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;subR->_bf = 0;subRL->_bf = 0;}else if (bf == -1){parent->_bf = 0;subR->_bf = 1;subRL->_bf = 0;}else if (bf == 1){parent->_bf = -1;subR->_bf = 0;subRL->_bf = 0;}else{assert(false);}}Node* copy(Node* root){if (root == nullptr){return nullptr;}Node* copynode = new Node(root->_kv);copynode->_left = copy(root->_left);copynode->_right = copy(root->_right);return copynode;}void Destroy(Node* root){if (root == nullptr)return;Destroy(root->_left);Destroy(root->_right);delete root;}void _InOrder(Node* root){if (root == nullptr)//递归一定要有结束条件{return;}_InOrder(root->_left);cout << root->_kv.first << ":" << root->_kv.second << endl;_InOrder(root->_right);}int _Height(Node* root){if (root == nullptr){return 0;}int leftheight = _Height(root->_left);int rightheight = _Height(root->_right);return leftheight > rightheight ? leftheight + 1 : rightheight + 1;}bool _isBalanceTree(Node* root){if (root == nullptr){return true;}int leftheight = _Height(root->_left);int rightheight = _Height(root->_right);int bf = rightheight - leftheight;if (abs(bf) > 1){cout << root->_kv.first << "高度差异常" << endl;return false;}if (root->_bf != bf){cout << root->_kv.first << "平衡因子异常" << endl;return false;}return _isBalanceTree(root->_left) && _isBalanceTree(root->_right);}private:Node* _root = nullptr;
};

本篇文章的分享就到这里了,如果您觉得在本文有所收获,还请留下您的三连支持哦~

头像

相关文章:

【C++】二叉搜索树+变身 = AVL树

&#x1f680;个人主页&#xff1a;小羊 &#x1f680;所属专栏&#xff1a;C 很荣幸您能阅读我的文章&#xff0c;诚请评论指点&#xff0c;欢迎欢迎 ~ 目录 前言一、AVL树二、AVL树的实现2.1 平衡因子2.2 旋转处理2.2.1 左单旋&#xff1a;插入新节点后单纯的右边高2.2.2 …...

Flutter String 按 ,。分割

在 Flutter 中&#xff0c;如果你想将一个字符串按特定的字符&#xff08;例如中文逗号 &#xff0c; 和英文句号 .&#xff09;进行分割&#xff0c;可以使用 Dart 语言的字符串处理功能。具体来说&#xff0c;你可以使用 split 方法&#xff0c;并传入一个正则表达式来匹配这…...

Redis: 集群高可用之MOVED转向和ASK转向解决方案

MOVED转向 1 ) 问题描述 在客户端操作Redis集群的时候 MOVED转向 或 MOVED错误是经常遇到的一类问题我们先连入集群&#xff1a;$ /usr/local/redis/bin/redis-cli -a 123456 -h 192.168.10.101 -p 6371之前在Redis中存储过一些数据&#xff0c;比如下面的情况&#xff0c;当输…...

idea插件市场安装没反应

https://plugins.jetbrains.com/idea重启后还是不行那就...

数据结构之排序(5)

摘要&#xff1a;本文主要讲各种排序算法&#xff0c;注意它们的时间复杂度 概念 将各元素按关键字递增或递减排序顺序重新排列 评价指标 稳定性: 关键字相同的元素经过排序后相对顺序是否会改变 时间复杂度、空间复杂度 分类 内部排序——数据都在内存中 外部排序——…...

R包的安装、加载以及如何查看帮助文档

0x01 如何安装R包 一、通过R 内置函数安装&#xff08;常用&#xff09; 1.安装CRAN的R包 install.packages()是一个用于安装 R 包的重要函数。 语法&#xff1a;install.packages(pkgs, repos getOption("repos"),...) 其中&#xff1a; pkgs&#xff1a;要安…...

【YOLO学习】YOLOv3详解

文章目录 1. 网络结构1.1 结构介绍1.2 改进 2. 训练与测试过程3. 总结 1. 网络结构 1.1 结构介绍 1. 与 YOLOv2 不同的是&#xff0c;YOLOv3 在 Darknet-19 里加入了 ResNet 残差连接&#xff0c;改进之后的模型叫 Darknet-53。在 ImageNet上 实验发现 Darknet-53 相对于 ResN…...

JDK1.0主要特性

JDK 1.0&#xff0c;也被称为Java 1&#xff0c;是Java编程语言的第一个正式版本&#xff0c;由Sun Microsystems公司在1996年发布。JDK 1.0的发布标志着Java作为一种编程语言和平台的正式诞生&#xff0c;它带来了许多创新的概念和特性&#xff0c;对后来的软件开发产生了深远…...

CSS基础-盒子模型(三)

9、CSS盒子模型 9.1 CSS常用长度单位 1、px&#xff1a;像素&#xff1b; 2、em&#xff1a;相对元素font-size的倍数&#xff1b; 3、rem&#xff1a;相对根字体的大小&#xff0c;html标签即是根&#xff1b; 4、%&#xff1a;相对于父元素进行计算。 注意&#xff1a;CSS样…...

深度学习中的损失函数详解

深度学习中的损失函数详解 文章目录 深度学习中的损失函数详解损失函数的基础概念常见的损失函数类型及应用场景回归问题的损失函数分类问题的损失函数自定义损失函数 如何选择合适的损失函数&#xff1f;损失函数在深度学习中的应用 在深度学习的世界中&#xff0c;损失函数&a…...

系统架构设计师-下午案例题(2022年下半年)

1.试题-(共25分):阅读以下关于软件架构设计与评估的叙述在答题纸上回答问题1和问题2。 【说明】某电子商务公司拟升级其会员与促销管理系统&#xff0c;向用户提供个性化服务&#xff0c;提高用户的粘性。在项目立项之初&#xff0c;公司领导层一致认为本次升级的主要目标是提…...

高级图片编辑器Photopea

什么是 Photopea &#xff1f; Photopea 是一款免费的在线工具&#xff0c;用于编辑光栅和矢量图形&#xff0c;支持PSD、AI 和 Sketch文件。 功能上&#xff0c;Photopea 和 老苏之前介绍的 miniPaint 比较像 文章传送门&#xff1a;在线图片编辑器miniPaint 支持的格式 复杂…...

详解zookeeper四字命令

ZooKeeper 的四字命令&#xff08;Four-Letter Words, 4LW&#xff09;是一组简单的管理和监控命令&#xff0c;方便运维人员快速获取 ZooKeeper 集群和节点的运行状态。这些命令通常用于健康检查、性能监控、节点配置查看等操作。通过这些命令&#xff0c;可以轻松获取关于 Zo…...

docker 进入容器运行命令

要进入正在运行的Docker容器并在其中执行命令&#xff0c;你可以使用docker exec命令。以下是具体步骤和示例&#xff1a; 1. 查看正在运行的容器 首先&#xff0c;确认你的容器正在运行&#xff0c;可以使用以下命令查看所有运行中的容器&#xff1a; docker ps2. 进入容器…...

一行 Python 代码能实现什么丧心病狂的功能?圣诞树源代码

手头有 109 张头部 CT 的断层扫描图片&#xff0c;我打算用这些图片尝试头部的三维重建。基础工作之一&#xff0c;就是要把这些图片数据读出来&#xff0c;组织成一个三维的数据结构&#xff08;实际上是四维的&#xff0c;因为每个像素有 RGBA 四个通道&#xff09;。 这个…...

mit6824-01-MapReduce详解

文章目录 MapReduce简述编程模型执行流程执行流程排序保证Combiner函数Master数据结构 容错性Worker故障Master故障 性能提升定制分区函数局部性执行缓慢的worker(slow workers) 常见问题总结回顾参考链接 MapReduce简述 MapReduce是一个在多台机器上并行计算大规模数据的软件架…...

在Docker中运行微服务注册中心Eureka

1、Docker简介&#xff1a; 作为开发者&#xff0c;经常遇到一个头大的问题&#xff1a;“在我机器上能运行”。而将SpringCloud微服务运行在Docker容器中&#xff0c;避免了因环境差异带来的兼容性问题&#xff0c;能够有效的解决此类问题。 通过Docker&#xff0c;开发者可…...

白话进程>线程>协程

文章目录 概述进程线程协程区别与联系 举个栗子进程例子线程例子协程例子区别与联系的具体体现 代码示例进程例子线程例子协程&#xff08;Goroutine&#xff09;例子 概述 进程、线程和协程是计算机科学中的基本概念&#xff0c;它们在操作系统和并发编程中扮演着重要角色。以…...

论文阅读:Attention is All you Need

Abstract 贡献&#xff1a; 提出了Transformer&#xff0c;完全基于注意力机制&#xff0c;摒弃了循环和卷积网络。 结果&#xff1a; 本模型在质量上优于现有模型&#xff0c;同时具有更高的并行性&#xff0c;并且显著减少了训练时间。 1. Introduction long short-term …...

【Linux 】文件描述符fd、重定向、缓冲区(超详解)

目录 ​编辑 系统接口进行文件访问 open 接口介绍 文件描述符fd 重定向 缓冲区 1、缓冲区是什么&#xff1f; 2、为什么要有缓冲区&#xff1f; 3、怎么办&#xff1f; 我们先来复习一下&#xff0c;c语言对文件的操作&#xff1a; C默认会打开三个输入输出流&#xf…...

突破不可导策略的训练难题:零阶优化与强化学习的深度嵌合

强化学习&#xff08;Reinforcement Learning, RL&#xff09;是工业领域智能控制的重要方法。它的基本原理是将最优控制问题建模为马尔可夫决策过程&#xff0c;然后使用强化学习的Actor-Critic机制&#xff08;中文译作“知行互动”机制&#xff09;&#xff0c;逐步迭代求解…...

Mybatis逆向工程,动态创建实体类、条件扩展类、Mapper接口、Mapper.xml映射文件

今天呢&#xff0c;博主的学习进度也是步入了Java Mybatis 框架&#xff0c;目前正在逐步杨帆旗航。 那么接下来就给大家出一期有关 Mybatis 逆向工程的教学&#xff0c;希望能对大家有所帮助&#xff0c;也特别欢迎大家指点不足之处&#xff0c;小生很乐意接受正确的建议&…...

LeetCode - 394. 字符串解码

题目 394. 字符串解码 - 力扣&#xff08;LeetCode&#xff09; 思路 使用两个栈&#xff1a;一个存储重复次数&#xff0c;一个存储字符串 遍历输入字符串&#xff1a; 数字处理&#xff1a;遇到数字时&#xff0c;累积计算重复次数左括号处理&#xff1a;保存当前状态&a…...

SpringBoot+uniapp 的 Champion 俱乐部微信小程序设计与实现,论文初版实现

摘要 本论文旨在设计并实现基于 SpringBoot 和 uniapp 的 Champion 俱乐部微信小程序&#xff0c;以满足俱乐部线上活动推广、会员管理、社交互动等需求。通过 SpringBoot 搭建后端服务&#xff0c;提供稳定高效的数据处理与业务逻辑支持&#xff1b;利用 uniapp 实现跨平台前…...

Psychopy音频的使用

Psychopy音频的使用 本文主要解决以下问题&#xff1a; 指定音频引擎与设备&#xff1b;播放音频文件 本文所使用的环境&#xff1a; Python3.10 numpy2.2.6 psychopy2025.1.1 psychtoolbox3.0.19.14 一、音频配置 Psychopy文档链接为Sound - for audio playback — Psy…...

Redis数据倾斜问题解决

Redis 数据倾斜问题解析与解决方案 什么是 Redis 数据倾斜 Redis 数据倾斜指的是在 Redis 集群中&#xff0c;部分节点存储的数据量或访问量远高于其他节点&#xff0c;导致这些节点负载过高&#xff0c;影响整体性能。 数据倾斜的主要表现 部分节点内存使用率远高于其他节…...

【C++进阶篇】智能指针

C内存管理终极指南&#xff1a;智能指针从入门到源码剖析 一. 智能指针1.1 auto_ptr1.2 unique_ptr1.3 shared_ptr1.4 make_shared 二. 原理三. shared_ptr循环引用问题三. 线程安全问题四. 内存泄漏4.1 什么是内存泄漏4.2 危害4.3 避免内存泄漏 五. 最后 一. 智能指针 智能指…...

在 Spring Boot 项目里,MYSQL中json类型字段使用

前言&#xff1a; 因为程序特殊需求导致&#xff0c;需要mysql数据库存储json类型数据&#xff0c;因此记录一下使用流程 1.java实体中新增字段 private List<User> users 2.增加mybatis-plus注解 TableField(typeHandler FastjsonTypeHandler.class) private Lis…...

「全栈技术解析」推客小程序系统开发:从架构设计到裂变增长的完整解决方案

在移动互联网营销竞争白热化的当下&#xff0c;推客小程序系统凭借其裂变传播、精准营销等特性&#xff0c;成为企业抢占市场的利器。本文将深度解析推客小程序系统开发的核心技术与实现路径&#xff0c;助力开发者打造具有市场竞争力的营销工具。​ 一、系统核心功能架构&…...

实战设计模式之模板方法模式

概述 模板方法模式定义了一个操作中的算法骨架&#xff0c;并将某些步骤延迟到子类中实现。模板方法使得子类可以在不改变算法结构的前提下&#xff0c;重新定义算法中的某些步骤。简单来说&#xff0c;就是在一个方法中定义了要执行的步骤顺序或算法框架&#xff0c;但允许子类…...