【数据结构】【C++】AVL树的模拟实现(插入、判断、旋转)

文章目录

  • 1 概念
  • 2 实现
    • 2.1 AVL树结点的定义
    • 2.2 AVL树的插入
      • 2.2.1 AVL树的插入规则
      • 2.2.2 旋转
        • 2.2.2.1 左单旋
        • 2.2.2.2 右单旋
        • 2.2.2.3 左右双旋
        • 2.2.2.4 右左双旋
      • 2.2.3 总结
  • 3 平衡判断
  • 4 删除
  • 5 源码

1 概念

二叉搜索树虽可以缩短查找的效率,但如果数据有序或接近有序二叉搜索树将退化为单支树,查找元素相当于在顺序表中搜索元素,效率低下。

因此,两位俄罗斯的数学家G.M.Adelson-Velskii和E.M.Landis在1962年发明了一种解决上述问题的方法:当向二叉搜索树中插入新结点后,如果能保证每个结点的左右子树高度之差的绝对值不超过1(需要对树中的结点进行调整),即可降低树的高度,从而减少平均搜索长度。

一棵AVL树或者是空树,或者是具有以下性质的二叉搜索树:

  • 它的左右子树都是AVL树
  • 左右子树高度之差(简称平衡因子)的绝对值不超过1(-1/0/1)

关于平衡因子:
平衡因子是左右高度之差,实现时可以定义为右子树高度减左子树高度,也可以定义为左子树高度减右子树高度(因为是用绝对值判断是否合法)

平衡因子不是AVL树中必须设计的,只是一种帮助我们控制树的方式,但是使用平衡因子会比较方便。

2 实现

2.1 AVL树结点的定义

AVL树究其本质,也是一棵二叉树,所以结点定义需要包括左右孩子指针,同时还需要一个指向父亲的指针方便之后的操作。另外,AVL树通常是一个<K, V>模型的数据结构,所以内部的数据域用一个pair存储。

template <class K,class V>
struct AVLTreeNode
{AVLTreeNode* _left;AVLTreeNode* _right;AVLTreeNode* _parent;int _bf;	// banance factor(平衡因子),这里用右子树高度减左子树高度表示pair<K, V> _kv;AVLTreeNode(const pair<K, V>& kv):_left(nullptr), _right(nullptr), _parent(nullptr), _bf(0), _kv(kv){}
};

2.2 AVL树的插入

2.2.1 AVL树的插入规则

由于AVL树也是一棵搜索二叉树,因此也需要遵循搜索二叉树的规则进行插入。

如果插入节点大于当前遍历结点,则在当前结点的右子树中继续寻找插入
如果插入节点小于当前遍历结点,则在当前结点的左子树中继续寻找插入
如果插入节点等于当前结点,不能插入,返回false

上面我们也提到,AVL树是高度平衡的二叉搜索树,所以在遵循二叉搜索树插入规则的基础上还需要进行特殊的判断和处理。

显然,在对一棵二叉树进行插入操作时,是有可能改变这棵树的高度的。而对于AVL树,如果执行插入操作后,使得某一个结点的平衡因子绝对值不再为0或者1,那么表示AVL树被破坏了,此时我们就需要执行一些处理维护AVL树。

先来思考一下,如果我们对AVL树进行了插入操作,哪些结点的平衡因子会发生变化呢?

其实很容易发现,当我插入一个结点以后,可能会导致高度发生变化。由于平衡二叉树的特性,就有可能导致该结点到根结点路径上的结点平衡因子都可能会发生变化,那么就需要分情况讨论。

如果插入结点之后,以该结点父亲为根结点的子树高度没有变化,那么对AVL树是没有破坏性的,但是一般来说,父结点的平衡因子会发生变化,如下图1情形,插入结点以后没有影响到父结点为根的子树高度,但是影响父结点的平衡因子需要更新:
Pasted image 20240329174803.png

这里设成功插入结点后该结点为c,其父结点为p。由我们给定的平衡因子的定义(右子树高度减去左子树高度),如果c为p的左孩子,那么父亲的平衡因子就会减少1;反之就会增加1。

那我们就发现,在AVL树中插入一个结点,一定会影响到的是该结点的父结点的平衡因子,因为插入会导致该父结点的左右子树高度关系发生变化,但是是否会继续向上影响祖先结点呢?这就需要根据情况讨论了。

如下图2,插入结点以后,不仅影响了p的平衡因子,也影响了p父亲的平衡因子(因为p所在子树的高度发生变化。而对于第一张图中的情形,是不会影响到p的父亲的。
![[Pasted image 20240329175508.png]]

我们会发现,上述两张图的区别是,当插入后p的平衡因子变为0时(图1),表示左右子树高度是一样的,那么肯定在插入前左右子树高度相差1,也就是插入前p的平衡因子为±1,此时没有改变p所在子树的整体高度,不会影响p的父亲。

当插入后p的平衡因子变为1时(图2),表示p之前的平衡因子一定是0,插入一个节点后p所在子树的高度一定变化,因此对于p的父亲,其左右子树高度差肯定也会变化。

为什么只能从0变成1,而不能从2变成1?
因为这是在高度平衡的搜索二叉树。如果插入前某个结点的平衡因子为2,表示插入前的树本身就不是一个AVL树。对于合法的AVL树,情况一定如上。

当插入后p的平衡因子变为2(即右子树高度比左子树高2)时,表示这时出现了不合法的AVL树,因此我们就需要对其进行一些处理使其变为AVL树。处理的方式其实就是旋转,下文中将继续叙述。

以上我们总结以下AVL树插入的规则:

  1. 按照搜索二叉树插入规则插入
  2. 更新平衡因子
    • 更新原则:
      如果c是p的左孩子, p -> _bf –
      如果c是p的右孩子, p -> _bf ++

    • 是否需要继续更新取决于p所在子树的高度是否变化:
      如果更新后,p -> _bf == 0,表示更新前p -> _bf 一定为±1,更新之后p的高度没有变化,不会影响p的父亲,结束,返回true

      如果更新后,p -> _bf == ±1,表示更新前p -> _bf 一定为0,更新之后p变得不均衡,但是并没有违反规则。p的高度一定发生变化,会影响p的父亲,那么需要继续向上更新**(最坏更新到root位置)**

      如果更新后,p -> _bf == ±2,表示p所在的子树违反了平衡规则。那么就需要进行特殊处理使其合法,这个处理就是旋转。旋转以后的树就合法了,结束更新(旋转可以让p所在子树高度回到插入之前的状态,不会对上层的平衡因子有影响)

综上,更新结束的标志有三点:

  1. 更新到root位置,结束
  2. 当前更新位置的parent的平衡因子为0,结束
  3. 执行旋转操作以后,结束

2.2.2 旋转

旋转的目的:

  1. 保持搜索规则
  2. 使当前树从不平衡到平衡
  3. 降低当前树的高度

以下是旋转的一个示例:
![[Pasted image 20240401220131.png]]

![[Pasted image 20240401220147.png]]

2.2.2.1 左单旋

左单旋的意思就是:一个结点是其父结点的右孩子,经过旋转操作以后,父亲变为该结点的左孩子,而该结点的左孩子变成父亲的右孩子

左单旋发生的场景:

parent -> _bf == 2 && cur -> _bf == 1
![[Pasted image 20240401223018.png]]

void RotateL(Node* parent){Node* subR = parent->_right;	// 父亲的右孩子Node* subRL = subR->_left;		// 父亲右孩子的左孩子parent->_right = subRL;if(subRl)subRL->_parent = parent;subR->_left = parent;Node* ppnode = parent->_parent;//记录当前父亲的父亲parent->_parent = subR;if (parent == _root){_root = subR;subR->_parent = nullptr;}else{if (parent == ppnode->_left){ppnode->_left = subR;}else{ppnode->_right = subR;}subR->_parent = ppnode;}// 更改平衡因子subR->_bf = 0;parent->_bf = 0;}

旋转的细节:

  • 更改结点指针指向时,还要更改对应_parent指针的指向。
  • subR的左指针可能为空,注意空指针解引用的问题。
  • subR要链接到parent -> _parent 的相应左 / 右指针(与parent的位置一致)
  • 如果parent是root,还需要更改root
  • 最后要记得修改平衡因子(单旋以后,parent和cur的平衡因子都是0,因此单旋是非常健康的旋转操作)
2.2.2.2 右单旋

右单旋就是左单旋的对称情况,对照左单旋的代码更改即可。

// 右单旋void RotateR(Node* parent){Node* subL = parent->_left;	// 父亲的左孩子Node* subLR = subL->_right;		// 父亲左孩子的右孩子parent->_left = subLR;// subL可能为空,要注意空指针的问题if (subLR)subLR->_parent = parent;subL->_right = parent;Node* ppnode = parent->_parent;//记录当前父亲的父亲parent->_parent = subL;if (parent == _root){_root = subL;subL->_parent = nullptr;}else{if (parent == ppnode->_left){ppnode->_left = subL;}else{ppnode->_right = subL;}subL->_parent = ppnode;}// 更改平衡因子subL->_bf = 0;parent->_bf = 0;}
2.2.2.3 左右双旋

双旋又是什么样的情况呢?我们先以左右双旋的情景来分析。假设有如下情景:
![[Pasted image 20240406205916.png]]

对于如上情形,在30的右边插入结点以后,30的平衡因子变为1,而其父亲的平衡因子变为-2,此时就会触发左右双旋。

如果我们继续按照右单旋的思路执行旋转,读者可以自行尝试,总之最后会发现旋转了个寂寞。

那么就说明,对于上述这种情况,单旋是走不通的,所以就需要新的旋转策略。为了更好的解释这个旋转策略,以以下的例子进行说明
![[Pasted image 20240406210238.png]]

上图中,我们将30的右子树划分为两部分。不难发现,无论新插入的结点在60的左子树还是右子树,都会引发90平衡因子变为-2。

首先先看30为根的这个树,先假设新插入结点在60的左子树,我们来看看对其进行左单旋会发生什么呢
![[Pasted image 20240406210559.png]]

再看这种情况,是不是有点类似于右单旋的样子?此时如果再对90进行右单旋操作
![[Pasted image 20240406210816.png]]

最后得到的树就是合法的AVL树了。

其实,无论第一步的插入是在60的左子树还是右子树,运用上述的策略得到的都是合法的AVL树,如果插入在右子树,旋转之后的树就是这样的。
![[Pasted image 20240406211042.png]]

那么我们就分析出了左右双旋操作的一部分:

前提:cur -> _bf == 1 && parent -> _bf == -2
操作:先对cur执行左单旋;再对parent执行右单旋

分析结束其实可以发现,左右双旋的旋转部分是比较容易得,但是旋转以后还需要修改平衡因子,而双旋平衡因子的修改就比较复杂了。
![[Pasted image 20240406211726.png]]
在这里插入图片描述

其实,对于情况1和2,就是上述我们给到的两种旋转示例。从上面两张图中就可以看出,更新之后60的bf永远是0,而30和90的bf就要根据更新之前60的bf来判断。

而如果更新之前,60本身就是要新增的结点,那么在双旋之后,三个结点的bf都应该是0。

所以,更新后的平衡因子如何改变,本质是取决去60这个节点,也就是parent -> _left -> _right的平衡因子是多少。

    // 左右双旋void RotateLR(Node* parent){Node* subL = parent->_left;Node* subLR = subL->_right;int bf = subLR->_bf;RotateL(parent->_left);RotateR(parent);if (bf == 1){subLR->_bf = 0;subL->_bf = -1;parent->_bf = 0;}else if (bf == -1){subLR->_bf = 0;subL->_bf = 0;parent->_bf = 1;}else if (bf == 0){subLR->_bf = 0;subL->_bf = 0;parent->_bf = 0;}else{assert(false);}}

![[Pasted image 20240405215056.png]]

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

2.2.3 总结

根据上述分析,我们就可以写出一个完善的插入函数

	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 = cur->_parent;parent = parent->_parent;}else if (parent->_bf == 2 || parent->_bf == -2){// 旋转处理if (parent->_bf == 2 && cur->_bf == 1){RotateL(parent);}else if (parent->_bf == -2 && cur->_bf == -1){RotateR(parent);}else if (parent->_bf == -2 && cur->_bf == 1){RotateLR(parent);}else{RotateRL(parent);}// 旋转完成以后树就合法了break;}else{// 插入之前AVL树已经异常assert(false);}}return true;}

3 平衡判断

判断一棵AVL树是否确实平衡其实是有必要的。这里不能单纯按照平衡因子判断,因为我们不能保证插入过程中平衡因子的改变是正确的,而且AVL树是不方便调试的,因此我们需要用最为朴素的方法,即判断树中每一个结点,其两棵子树的高度差是否等于该结点的平衡因子。

这里我们利用递归的方式实现,同时,我们可以利用后序遍历的思想,即从最低层开始判断。如果按照前序遍历的顺序,我们会发现树底部的部分会进行多次重复判断。当我们以后序遍历的顺序判断时,可以通过输出型参数的方式将底部子树高度带出,这样就减少了重复计算。

	bool _IsBalence(Node* root,int& height){if (root == nullptr){height = 0;return true;}int leftHeight = 0;int rightHeight = 0;// 将左右子树高度以输出型参数的方式带出if (!_IsBalence(root->_left, leftHeight) || !_IsBalence(root->_right, rightHeight)){return false;}if (abs(rightHeight - leftHeight) >= 2){cout << "不平衡" << endl;return false;}if (rightHeight - leftHeight != root->_bf){cout << "平衡因子异常" << endl;return false;}// 左右子树的最大高度值加上根节点这一层即为以该结点为根的子树高度height = max(leftHeight, rightHeight) + 1;return true;}bool IsBalence(){int height = 0;return _IsBalence(_root, height);}

4 删除

因为AVL树也是二叉搜索树,可按照二叉搜索树的方式将节点删除,然后再更新平衡因子,只不过与删除不同的时,删除节点后的平衡因子更新,最差情况下一直要调整到根节点的位置。

但是AVL树的删除相较于插入更为复杂,实际中很少会考察与代码相关的知识,如有兴趣可参考《算法导论》或《数据结构-用面向对象方法与C++描述》殷人昆版。

5 源码

源码中还包含了Height、Size等基础操作。

#include <iostream>
#include <cassert>
using namespace std;template <class K,class V>
struct AVLTreeNode
{AVLTreeNode<K, V>* _left;AVLTreeNode<K, V>* _right;AVLTreeNode<K, V>* _parent;int _bf;	// banance factor(平衡因子)pair<K, V> _kv;AVLTreeNode(const pair<K, V>& kv):_left(nullptr), _right(nullptr), _parent(nullptr), _bf(0), _kv(kv){}
};template <class K, class V>
class AVLTree
{
public:typedef AVLTreeNode<K,V> Node;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 = cur->_parent;parent = parent->_parent;}else if (parent->_bf == 2 || parent->_bf == -2){// 旋转处理if (parent->_bf == 2 && cur->_bf == 1){RotateL(parent);}else if (parent->_bf == -2 && cur->_bf == -1){RotateR(parent);}else if (parent->_bf == -2 && cur->_bf == 1){RotateLR(parent);}else{RotateRL(parent);}// 旋转完成以后树就合法了break;}else{// 插入之前AVL树已经异常assert(false);}}return true;}// 左单旋void RotateL(Node* parent){Node* subR = parent->_right;	// 父亲的右孩子Node* subRL = subR->_left;		// 父亲右孩子的左孩子parent->_right = subRL;// subR可能为空,要注意空指针的问题if(subRL)subRL->_parent = parent;subR->_left = parent;Node* ppnode = parent->_parent;//记录当前父亲的父亲parent->_parent = subR;if (parent == _root){_root = subR;subR->_parent = nullptr;}else{if (parent == ppnode->_left){ppnode->_left = subR;}else{ppnode->_right = subR;}subR->_parent = ppnode;}// 更改平衡因子subR->_bf = 0;parent->_bf = 0;}// 右单旋void RotateR(Node* parent){Node* subL = parent->_left;	// 父亲的左孩子Node* subLR = subL->_right;		// 父亲左孩子的右孩子parent->_left = subLR;// subL可能为空,要注意空指针的问题if (subLR)subLR->_parent = parent;subL->_right = parent;Node* ppnode = parent->_parent;//记录当前父亲的父亲parent->_parent = subL;if (parent == _root){_root = subL;subL->_parent = nullptr;}else{if (parent == ppnode->_left){ppnode->_left = subL;}else{ppnode->_right = subL;}subL->_parent = ppnode;}// 更改平衡因子subL->_bf = 0;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 == -1){parent->_bf = 1;subL->_bf = 0;subLR->_bf = 0;}else if (bf == 0){parent->_bf = 0;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 == -1){parent->_bf = 0;subR->_bf = 1;subRL->_bf = 0;}else if (bf == 0){parent->_bf = 0;subR->_bf = 0;subRL->_bf = 0;}else if (bf == 1){parent->_bf = -1;subR->_bf = 0;subRL->_bf = 0;}else{assert(false);}}void _InOrder(Node* root){if (root == nullptr)return;_InOrder(root->_left);cout << root->_kv.first << "[" << root->_bf << "]" << endl;_InOrder(root->_right);}void InOrder(){_InOrder(_root);}int _Height(Node* root){if (root == nullptr)return 0;int leftHeight = _Height(root->_left);int rightHeight = _Height(root->_right);return max(leftHeight, rightHeight) + 1;}int Height(){return _Height(_root);}bool _IsBalence(Node* root,int& height){if (root == nullptr){height = 0;return true;}int leftHeight = 0;int rightHeight = 0;// 将左右子树高度以输出型参数的方式带出if (!_IsBalence(root->_left, leftHeight) || !_IsBalence(root->_right, rightHeight)){return false;}if (abs(rightHeight - leftHeight) >= 2){cout << "不平衡" << endl;return false;}if (rightHeight - leftHeight != root->_bf){cout << "平衡因子异常" << endl;return false;}// 左右子树的最大高度值加上根节点这一层即为以该结点为根的子树高度height = max(leftHeight, rightHeight) + 1;return true;}bool IsBalence(){int height = 0;return _IsBalence(_root, height);}size_t _size(Node* root){if (root == nullptr)return 0;return _size(root->_left) + _size(root->_right) + 1;}size_t size(){return _size(_root);}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 NULL;}private:Node* _root = nullptr;
};

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

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

相关文章

找不到api-ms-win-crt-runtime-l1-1-0.dll文件5种解决方法

在日常使用计算机的过程中&#xff0c;我们时常会遭遇各类意想不到的问题&#xff0c;其中之一就是“api-ms-win-crt-runtime-l1-1-0.dll丢失”。这个错误通常发生在Windows操作系统中&#xff0c;它表示一个动态链接库文件丢失或损坏。这个问题可能会导致某些应用程序无法正常…

1Panel官方出品丨MaxKB:基于LLM大模型的知识库问答系统

1Panel&#xff08;github.com/1Panel-dev/1Panel&#xff09;是一款现代化、开源的Linux服务器运维管理面板&#xff0c;它致力于通过开源的方式&#xff0c;帮助用户简化建站与运维管理流程。为了方便广大用户快捷安装部署相关软件应用&#xff0c;1Panel特别开通应用商店&am…

TensorFlow 1.x的学习

.为什么还有很多人都选择使用TensorFlow 1.x 兼容性问题: TensorFlow 1.x在一些旧项目中已经得到了广泛应用&#xff0c;这些项目可能依赖于1.x版本的特定API或行为。升级到2.x可能需要大量的代码修改和测试工作&#xff0c;对于一些已经稳定运行的项目&#xff0c;维护者可能…

实现iOS App代码混淆

简介 在开发iOS应用程序时&#xff0c;保护代码安全是至关重要的。代码混淆是一种常用的技术&#xff0c;可以增加逆向工程的难度&#xff0c;防止他人对代码的篡改和盗用。本文将介绍如何实现iOS App代码混淆的步骤和操作方法。 整体流程 下面是实现iOS App代码混淆的整体流…

创维:在博鳌论坛 叩响世界之门

出走半生&#xff0c;归来仍是少年。 2024年4月8日&#xff0c;一个离开海南近半个世纪的“少年”回到琼海博鳌&#xff0c;“下一站&#xff0c;1000亿&#xff01;”&#xff0c;他的承诺掷地有声。“1000亿”&#xff0c;意指创维集团在2025年前冲击千亿营收&#xff0c;这…

【JavaWeb】Day45.Mybatis——入门程序

什么是MyBatis? MyBatis是一款优秀的持久层框架&#xff0c;用于简化JDBC的开发。 &#xff08;持久层&#xff1a;指的是就是数据访问层(dao)&#xff0c;是用来操作数据库的。&#xff09; &#xff08;框架&#xff1a;是一个半成品软件&#xff0c;是一套可重用的、通用…

【企业动态】瑞芯微高级业务总监来访东胜物联,共探新能源汽车市场合作

近日&#xff0c;瑞芯微高级业务总监阙金珍一行来访东胜物联参观交流&#xff0c;并就深化合作进行讨论。 东胜物联与瑞芯微建有长期稳固的合作关系&#xff0c;基于RK3588、RK3399、RK3568等处理器&#xff0c;推出了多款嵌入式核心硬件产品&#xff0c;包括核心板、网关等&a…

工业物联网网关

在数字化浪潮席卷全球的今天&#xff0c;工业物联网&#xff08;IIoT&#xff09;作为连接物理世界与数字世界的桥梁&#xff0c;正在逐渐改变传统工业的面貌。而作为IIoT的核心枢纽&#xff0c;工业物联网网关发挥着至关重要的作用。今天&#xff0c;我们就来深入了解一下工业…

使用webpack5+TypeScript+npm发布组件库

一、前言 作为一只前端攻城狮&#xff0c;没有一个属于自己的组件库&#xff0c;那岂不是狮子没有了牙齿&#xff0c;士兵没有了武器&#xff0c;姑娘没有了大宝SOD蜜&#xff0c;你没有了我.... 言归正传&#xff0c;下面将给大家介绍如何通过webpack5编译一个TS组件发布到NPM…

【c 语言】声明了一个指针,会给指针分配内存吗?

&#x1f388;个人主页&#xff1a;豌豆射手^ &#x1f389;欢迎 &#x1f44d;点赞✍评论⭐收藏 &#x1f917;收录专栏&#xff1a;C语言 &#x1f91d;希望本文对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出指正&#xff0c;让我们共同学习、交流进步&…

租用境外服务器,越南服务器的优势有哪些

自从中国加入世界贸易组织之后&#xff0c;国内经济增加速度非常快&#xff0c;同时越来越多的人选择去东南亚国家发展&#xff0c;因为当地的中国人很多&#xff0c;所以中国企业在当地面临着更小的文化差异。东南亚地区也是最新的经济体&#xff0c;互联网正处于蓬勃发展的阶…

docker服务无法启动

背景&#xff1a;断电重启经常会导致磁盘io错误&#xff0c;甚至出现磁盘坏块 这时可以使用xfs_repair来修复磁盘&#xff0c;但是修复过程可能会导致部分数据丢失 xfs_repair -f -L /dev/sdc问题一&#xff1a; Apr 15 19:27:15 Centos7.6 systemd[1]: Unit docker.service e…

【十一】MyBatis Plus 原理分析

MyBatis Plus 原理分析 摘要 Java EE开发中必不可少ORM框架&#xff0c;目前行业里最流行的orm框架非Mybatis莫属了&#xff0c;而Mybatis框架本身没有提供api实现&#xff0c;所以市面上推出了Mybatis plus系列框架&#xff0c;plus版是mybatis增强工具&#xff0c;用于简化My…

嵌入式系统及应用-1.1嵌入式

嵌入式系统 定义&#xff1a;看不见的计算机&#xff0c;一般不能被用户编程&#xff0c;它有一些专用的I/O设备&#xff0c;对用户的接口是应用专用的 通常将嵌入式计算机系统简称为嵌入式系统。 嵌入式系统是以应用为中心&#xff0c;以计算机技术为基础、软件硬件可裁剪、…

记录一下如何腾讯云服务器用客户端连MySQL

我一般喜欢用IDEA连数据库&#xff0c;别问我为啥&#xff08;就喜欢用一个软件解决所有问题&#xff09; 当然写SQL语句个人还是觉得sqlyog体验最佳&#xff01;

【C/C++】什么是内存泄漏?如何检测内存泄漏?

一、内存泄漏概述 1.1 什么是内存泄漏 内存泄漏是在没有自动 gc 的编程语言里面&#xff0c;经常发生的一个问题。 自动垃圾回收&#xff08;Automatic Garbage Collection&#xff0c;简称 GC&#xff09;是一种内存管理技术&#xff0c;在程序运行时自动检测和回收不再使用…

MySQL8.0.36-社区版:错误日志(3)

mysql有个错误日志&#xff0c;是专门记录错误信息的&#xff0c;这个功能默认是开启的 一般都是在/var/log/mysqld.log 日志中存放 1.错误日志的位置 首先我们使用命令去查看一下&#xff0c;这个错误日志文件究竟在哪 进入到mysql中&#xff0c;使用命令 show variables…

FME学习之旅---day24

我们付出一些成本&#xff0c;时间的或者其他&#xff0c;最终总能收获一些什么。 高级地理数据库 教程&#xff1a;地理数据库转换 上述教程包括 如何使用 Esri 模板地理数据库 该内容在FME学习之旅day19 已经学习过 使用地理数据库属性域&#xff1a;编写编码属性域 属…

机器学习实训 Day1(线性回归练习)

线性回归练习 Day1 手搓线性回归 随机初始数据 import numpy as np x np.array([56, 72, 69, 88, 102, 86, 76, 79, 94, 74]) y np.array([92, 102, 86, 110, 130, 99, 96, 102, 105, 92])from matplotlib import pyplot as plt # 内嵌显示 %matplotlib inlineplt.scatter…

古月·ROS2入门21讲——学习笔记(一)核心概念部分1-14讲

讲解视频地址&#xff1a;1.ROS和ROS2是什么_哔哩哔哩_bilibili 笔记分为上篇核心概念部分和下篇常用工具部分 下篇&#xff1a;古月ROS2入门21讲——学习笔记&#xff08;二&#xff09;常用工具部分15-21讲-CSDN博客 目录 第一讲&#xff1a;ROS/ROS2是什么 1. ROS的诞生…