深入探究:AVL树的平衡之道

文章目录

  • 一、AVL树的原理
    • AVL树的定义和特性
    • 平衡因子的概念
  • 二、AVL树的自平衡策略
    • a. 单旋(single rotation)
      • 1. 左单旋(Left Rotation):
      • 2. 右单旋(Right Rotation):
    • b. 双旋(Double Rotation):
      • 1. 左右双旋(Left-Right Double Rotation):
      • 2. 右左双旋(Right-Left Double Rotation):
  • 三、AVL树的插入与验证
    • 1. 插入操作
    • 2. AVL树的验证

本文将带领读者深入探索 AVL 树的奥秘,从基本概念到具体实现,逐步剖析 AVL 树的平衡之道。无论您是初学者还是资深程序员,相信通过本文的阅读,您都能对 AVL 树有更深入的理解,并能够运用它解决实际问题。


一、AVL树的原理

AVL 树(Adelson-Velsky and Landis tree),作为一种自平衡的二叉搜索树,由 G.M. Adelson-Velsky 和 E.M. Landis 在 1962 年提出。它承载着优雅而高效的数据结构设计理念。其独特之处在于通过旋转操作保持树的高度平衡,从而确保各种操作的时间复杂度始终保持在 $O(log n) $的水平。AVL 树的诞生源于对二叉搜索树的不足之处的思考和突破,是计算机科学领域中的重要里程碑之一。

AVL树的定义和特性

AVL树是一种自平衡的二叉搜索树,其在每个节点上维护一个平衡因子(Balance Factor),用于确保树的高度保持在较低水平( 即保持在$O(log n) $)从而提高性能。下面详细解释AVL树的定义和特性:

  1. 定义:AVL树是一种满足以下性质的二叉搜索树:

    • 每个节点的平衡因子(Balance Factor)是 -1、0 或 1。
    • 左右子树高度之差(简称平衡因子)的绝对值不超过1(-1/0/1)。
    • 左子树和右子树都是AVL树。

    我们给出如下代码来定义 AVL树:

    template<class K,class V>
    struct AVLTreeNode {AVLTreeNode<K, V>* _left;	// 该节点的左孩子AVLTreeNode<K, V>* _right;	// 该节点的右孩子AVLTreeNode<K, V>* _parent;	// 该节点的双亲int _bf;					// 该节点的平衡因子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 {typedef AVLTreeNode<K, V> Node;
    public://...
    private:Node* _root;
    };
    

    AVLTreeNode是AVL树的节点结构,包含节点的左孩子、右孩子、双亲,以及数据和平衡因子。AVLTree是AVL树的类,包含了根节点和其他操作方法。在AVLTree类中,_root成员变量表示根节点。

  2. 特性

    • 每个节点的平衡因子限制:在AVL树中,每个节点的平衡因子必须是 -1、0 或 1。这意味着任何节点的左子树高度和右子树高度之差的绝对值不超过1。
    • 左右子树的高度差不超过1:AVL树的关键特点是保持树的高度平衡,即任意节点的左子树和右子树高度差不超过1。这确保了树的高度近似于$ log(n) ,其中 n 是节点数量,从而保证了插入、查找和删除等操作的平均时间复杂度为 ,其中 n 是节点数量,从而保证了插入、查找和删除等操作的平均时间复杂度为 ,其中n是节点数量,从而保证了插入、查找和删除等操作的平均时间复杂度为 O(log n)$。

在这里插入图片描述

两个二叉搜索树,只有左边的树是 AVL 树。

AVL的优势(相对于普通二叉搜索树):

  • 平衡性保证:AVL树通过限制每个节点的平衡因子来保持树的平衡,相比普通二叉搜索树更加平衡,避免了出现极端不平衡的情况,提高了整体性能。
  • 高效的插入和删除操作:由于AVL树具有平衡性质,插入和删除操作会触发自平衡机制,保持树的平衡状态,使得这些操作的时间复杂度为 O ( l o g n ) O(log n) O(logn),相比普通二叉搜索树更加高效。
  • 稳定的查找性能:AVL树的平衡性质确保了树的高度较低,从而保证了查找操作的稳定性能,使得在动态数据集合中仍能保持较快的查找速度。

AVL树是一棵绝对平衡的二叉搜索树,其要求每个节点的左右子树高度差的绝对值都不超过1,这 样可以保证查询时高效的时间复杂度,即 l o g 2 ( N ) log_2 (N) log2(N)。但是如果要对AVL树做一些结构修改的操 作,性能非常低下,比如:插入时要维护其绝对平衡,旋转的次数比较多,更差的是在删除时, 有可能一直要让旋转持续到根的位置。因此:如果需要一种查询高效且有序的数据结构,而且数据的个数为静态的(即不会改变),可以考虑AVL树,但一个结构经常修改,就不太适合。

平衡因子的概念

在AVL树中,平衡因子(Balance Factor)是指一个节点的左子树高度减去右子树高度的结果。具体而言,对于任意节点N,其平衡因子 BF(N) 的计算公式如下:

[ BF(N) = Height(leftSubtree) - Height(rightSubtree) ]

其中,Height(leftSubtree)表示节点N的左子树的高度,而Height(rightSubtree)表示节点N的右子树的高度。

在AVL树中,每个节点的平衡因子必须是 -1、0 或 1,否则就不符合AVL树的定义。如果某个节点的平衡因子绝对值大于1,那么这个节点就会导致其祖先节点失衡,需要通过旋转等操作来重新平衡整棵树。

我们通过控制每个节点的平衡因子,AVL树能够保持良好的平衡性质,确保在插入或删除节点后能够高效地进行自平衡操作,使得树的整体高度保持较低水平,从而提高查找、插入和删除等操作的效率。

二、AVL树的自平衡策略

AVL 树之所以被称为自平衡二叉搜索树,是因为它会在每次插入或删除操作后自动调整树的结构以保持平衡。

在插入以后,只有那些从插人点到根节点的路径上的节点的平衡可能被改变,因为只有这些节点的子树可能发生变化。当我们沿着这条路径上行到根并更新平衡信息时,我们可以找到一个节点,它的新平衡破坏了AVL条件。我们将指出如何在第一个这样的节点(即最深的节点)重新平衡这棵树,并证明,这一重新平衡保证整棵树满足AVL特性。

让我们把必须重新平衡的节点叫作a。由于任意节点最多有两个儿子,因此高度不平时,a点的两棵子树的高度差2。容易看出,这种不平衡可能出现在下面四种情况中:

  1. 对a的左儿子的左子树进行一次插入。
  2. 对a的左儿子的右子树进行一次插入,
  3. 对a的右儿子的左子树进行一次插入。
  4. 对a的右儿子的右子树进行一次插入。

情形1和4是关于a点的镜像对称,而情形2和3是关于a点的镜像对称。因此,理论上只有两种情况单旋转和双旋,当然从编程的角度来看还是四种情形:

左旋(Left Rotation),右旋(Right Rotation),左右双旋(Left-Right Double Rotation)和右左旋转(Right-Left Double Rotation),双旋是由两次单旋转组合而成的操作,用于处理更复杂的情况。下面详细介绍在AVL树如何根据节点的平衡因子进行相应的调整,以及中如何通过这些旋转操作来确保AVL树的平衡性(下图中括号内 1 代表在此处插入节点,A B C来形容其相对位置):

a. 单旋(single rotation)

下面我们依次介绍左单旋和右单旋:

1. 左单旋(Left Rotation):

左旋是针对某个节点的右子树过深而导致失衡的情况。即:新节点插入较高右子树的右侧—右右:左单旋。具体步骤如下:

在这里插入图片描述

  • 假设失衡节点为A,A的右子节点为B,B的左子节点为C。
  • 将B提升为新的根节点,A作为B的左子节点,C作为A的右子节点。
  • 可以看到,经过左单旋后,树的高度变为插入之前了,即树的高度没有发生变化,所以左单旋后无需继续往上更新平衡因子。

根据上述内容,我们给出如下代码:

void RotateL(Node* parent) {Node* subR = parent->_right;Node* subRL = subR->_left;parent->_right = subRL;if (subRL != nullptr) {subRL->_parent = parent;}subR->_left = parent;Node* ppnode = parent->_parent;parent->_parent = subR;if (parent == _root) {_root = subR;subR->_parent = nullptr;}else {if (ppnode->_left == parent)ppnode->_left = subR;elseppnode->_right = subR;subR->_parent = ppnode;}parent->_bf = 0;subR->_bf = 0;
}

首先将parent节点的右子节点subR的左子节点subRL作为parent节点的新右子节点,同时更新相关的指针和父节点标志位。然后根据parent节点是否为根节点进行相应的操作,将subR作为新的根节点或者将subR连接到原父节点ppnode上,并更新父节点指针,最后更新节点的平衡因子。

下面是对代码中各部分的功能和逻辑的解释:

首先,保存当前节点的右子节点(subR)和右子节点的左子节点(subRL)。然后,将当前节点的右子节点指向subRL,如果subRL不为空,则更新subRL的父节点指针为parent。接着,将subR的左子节点指向当前节点(parent),完成左旋操作。其次,更新受影响的父节点指针,确保树的连接正确性:

  • 将parent的父节点指针指向subR。
  • 如果parent是根节点,需要更新根节点指针为subR,并将subR的父节点指针置为空。
  • 否则,更新父节点(ppnode)的左子树或右子树指针为subR,并更新subR的父节点指针为ppnode。

最后,将调整后的节点的平衡因子设置为0,表示平衡。

这段代码实现了AVL树中左旋操作的所有必要步骤,包括旋转、节点指针的更新以及平衡因子的设置,以确保树的平衡性得到维护。左旋和右旋是 AVL 树中用于保持树平衡的重要操作,通过这些操作可以调整树的结构以维持平衡因子的要求。

2. 右单旋(Right Rotation):

右旋是针对某个节点的左子树过深而导致失衡的情况。即:新节点插入较高左子树的左侧—左左:右单旋。具体步骤如下:

在这里插入图片描述

  • 设失衡节点为A,A的左子节点为B,B的右子节点为C。
  • 将B提升为新的根节点,A作为B的右子节点,C作为A的左子节点。

根据上述内容,我们给出如下代码:

void RotateR(Node* parent) {Node* subL = parent->_left;Node* subLR = subL->_right;parent->_left = subLR;if (subLR != nullptr) {subLR->_parent = parent;}subL->_right = parent;Node* ppnode = parent->_parent;parent->_parent = subL;if (parent == _root) {_root = subL;subL->_parent = nullptr;}else {if (ppnode->_left == parent)ppnode->_left = subL;elseppnode->_right = subL;subL->_parent = ppnode;}parent->_bf = 0;subL->_bf = 0;
}

首先,保存当前节点的左子节点(subL)和左子节点的右子节点(subLR)。然后,将当前节点的左子节点指向subLR,如果subLR不为空,则更新subLR的父节点指针为parent。接着,将subL的右子节点指向当前节点(parent),完成右旋操作。其次,更新受影响的父节点指针,确保树的连接正确性:

  • 将parent的父节点指针指向subL。
  • 如果parent是根节点,需要更新根节点指针为subL,并将subL的父节点指针置为空。
  • 否则,更新父节点(ppnode)的左子树或右子树指针为subL,并更新subL的父节点指针为ppnode。

最后,将调整后的节点的平衡因子设置为0,表示平衡。

b. 双旋(Double Rotation):

双旋是针对某个节点的子树同时向左或向右倾斜而导致失衡的情况。一般需要进行两次单旋操作来恢复平衡。具体步骤可以是左旋后再进行右旋,或者右旋后再进行左旋。

1. 左右双旋(Left-Right Double Rotation):

当在某个节点的左子树中的右子树更深时,需要进行右左旋转操作。这种情况下,首先对当前节点的左子节点进行左旋转,然后再对当前节点进行右旋转,以实现树的平衡。即:新节点插入较高左子树的右侧—左右:先左单旋再右单旋。具体步骤如下:

  1. 首先,获取当前节点的左子节点 subL 和左子节点的右子节点 subLR

  2. 然后,获取 subLR 的平衡因子 bf

  3. 接下来,调用 RotateL 函数对当前节点的左子节点进行左旋转。

  4. 再次,调用 RotateR 函数对当前节点进行右旋转。

    Node* subL = parent->_left;
    Node* subLR = subL->_right;
    int bf = subLR->_bf;
    RotateL(parent->_left);
    RotateR(parent);
    
  5. 根据 subLR 的平衡因子 bf 的不同情况,进行平衡因子的更新:

    • 如果 bf 为 0,表示左右子树高度相等,将当前节点和左子节点的平衡因子都设置为 0。
      在这里插入图片描述

    • 如果 bf 为 1,表示左子树高度大于右子树,将当前节点的平衡因子设置为 0,左子节点的平衡因子设置为 -1。

    在这里插入图片描述

    • 如果 bf 为 -1,表示右子树高度大于左子树,将当前节点的平衡因子设置为 1,左子节点的平衡因子设置为 0。

    在这里插入图片描述

    • 如果以上情况都不满足,抛出异常。
    if (bf == 0) {parent->_bf = 0;subL->_bf = 0;
    }
    else if (bf == 1) {parent->_bf = 0;subL->_bf = -1;
    }
    else if (bf == -1) {parent->_bf = 1;subL->_bf = 0;
    }
    else {assert(false);
    }
    

总结上述代码如下:

void RotateLR(Node* parent) {if (bf == 0) {parent->_bf = 0;subL->_bf = 0;}else if (bf == 1) {parent->_bf = 0;subL->_bf = -1;}else if (bf == -1) {parent->_bf = 1;subL->_bf = 0;}else {assert(false);}
}

2. 右左双旋(Right-Left Double Rotation):

当在某个节点的右子树中的左子树更深时,需要进行左右旋转操作。这种情况下,首先对当前节点的右子节点进行右旋转,然后再对当前节点进行左旋转,以恢复树的平衡性。即: 新节点插入较高右子树的左侧—右左:先右单旋再左单旋。具体步骤如下:

  1. 首先,获取当前节点的右子节点 subR 和右子节点的左子节点 subRL

  2. 然后,获取 subRL 的平衡因子 bf

  3. 接下来,调用 RotateR 函数对当前节点的右子节点进行右旋转。

  4. 再次,调用 RotateL 函数对当前节点进行左旋转。

Node* subR = parent->_right;
Node* subRL = subR->_left;
int bf = subRL->_bf;
RotateR(parent->_right);
RotateL(parent);
  1. 根据 subLR 的平衡因子 bf 的不同情况,进行平衡因子的更新:

    • 如果 bf 为 0,表示右左子树高度相等,将当前节点和右子节点的平衡因子都设置为 0。

    在这里插入图片描述

    • 如果 bf 为 1,表示左子树高度大于右子树,将当前节点的平衡因子设置为 -1,右子节点的平衡因子设置为 0。

    在这里插入图片描述

    • 如果 bf 为 -1,表示右子树高度大于左子树,将当前节点的平衡因子设置为 0,右子节点的平衡因子设置为 1。

    在这里插入图片描述

    • 如果以上情况都不满足,抛出异常。
    if (bf == 0) {parent->_bf = 0;subR->_bf = 0;
    }
    else if (bf == 1) {parent->_bf = -1;subR->_bf = 0;
    }
    else if (bf == -1) {parent->_bf = 0;subR->_bf =  1;
    }
    else {assert(false);
    }
    

总结上述代码如下:

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

在旋转过程中,左右双旋的 subLR->_bf 在进行左单旋和右单旋函数调用时旋已置为0;右左双旋的 subRL->_bf 在进行右单旋和左单旋函数调用时旋已置为0。观察旋转后的结果我们可以得知 subLR->_bfsubRL->bf 在上述各种情况里都是 0,因此双旋函数不需要再对其平衡因子进行处理。


三、AVL树的插入与验证

1. 插入操作

AVL 树的插入操作主要包含两个步骤:插入新节点和平衡性调整。

A. 插入新节点:

  1. 从根节点开始,循环比较要插入节点的键值和当前节点的键值大小关系,确定插入位置。
  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->_left;}else if (cur->_kv.first < kv.first) {parent = cur;cur = cur->_right;}else {return false;}}cur = new Node(kv);if (parent->_kv.first > kv.first) {parent->_left = cur;}else {parent->_right = cur;}cur->_parent = parent;//....
}

B. 平衡性调整:

  1. 在插入新节点后,从插入点向上回溯到根节点,检查沿途节点的平衡因子是否失衡(绝对值大于1)。
  2. 如果某个节点失衡,根据其子树的情况,进行相应的旋转操作(单旋转或双旋转)来保持树的平衡。
  3. 旋转操作可能会影响祖先节点的平衡因子,因此需要继续向上回溯,直到整棵树恢复平衡为止。
bool Insert(const pair<K, V>& kv) {// ... 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) {RotateRL(parent);}else if (parent->_bf == -2 && cur->_bf == 1) {RotateLR(parent);}break;}else {assert(false);//插入前就出错!}}return true;
}

通过以上两个步骤,可以实现在 AVL 树中插入新节点并保持树的平衡性(把两个代码块中内容合在一起看)。 AVL 树的平衡性调整是通过旋转操作来实现的,旋转操作包括左旋、右旋、左右双旋和右左双旋。使 AVL 树的每个节点的左右子树高度差不超过 1,以保持树的平衡性。

2. AVL树的验证

为了证明二叉树是AVL树还需验证二叉树的平衡性,在该过程中我们需要检查每个结点的平衡因子是否正确:

bool _IsBalance(Node* root, int& height) {// 后序if (root == nullptr) {height = 0;return true;}int leftHeight = 0, rightHeight = 0;if (!_IsBalance(root->_left, leftHeight) || !_IsBalance(root->_right, rightHeight))return false;if (abs(leftHeight - rightHeight) >= 2) {cout <<root->_kv.first<< " height warning!" << endl;return false;}if (rightHeight - leftHeight != root->_bf) {cout << root->_kv.first << " balance factor warning!" << endl;return false;}height = max(leftHeight, rightHeight) + 1;return true;
}bool IsBalance() {int heght = 0;return _IsBalance(_root, heght);
}

在函数 _IsBalance 中,它首先判断当前节点是否为空,如果是空节点,则将高度设置为 0,并返回 true。然后分别递归检查左右子树,并获取它们的高度。接着,它会判断左右子树的高度差是否大于等于 2,如果是则输出警告并返回 false。然后它会再次判断当前节点的平衡因子是否与左右子树的高度差相符,如果不符则输出警告并返回 false。最后,更新当前节点的高度为左右子树中较大的高度加 1,并返回 true。

IsBalance 函数中,它调用了 _IsBalance 函数,并传入根节点和一个用于记录树高度的变量。最终返回 _IsBalance 函数的结果。

总的来说,这段代码通过递归后序遍历的方式来检查整棵树的平衡性,并在发现不平衡的情况下输出相应的警告信息。这样的实现方式是典型的检查 AVL 树平衡性的方法,但需要保证树的平衡因子 _bf 在每次插入、删除后得到正确更新。

通过以上内容,我们将全面探索 AVL 树的内涵与外延,逐步揭示其平衡之道。希望本文能为读者提供清晰而全面的 AVL 树知识体系,帮助大家更好地理解和运用 AVL 树这一重要的数据结构。
本文代码的完整实现在此处,AVL树 · 比奇堡的Zyb/每日学习 - 码云 - 开源中国 (gitee.com)。

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

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

相关文章

低功耗DC-DC电压调整器IU5528D

IU5528D是一款超微小型,超低功耗,高效率,升降压一体DC-DC调整器。适用于双节,三节干电池或者单节锂电池的应用场景。可以有效的延长电池的使用时间。IU5528D由电流模PWM控制环路&#xff0c;误差放大器&#xff0c;比较器和功率开关等模块组成。该芯片可在较宽负载范围内高效稳…

自学软件测试真的能找到工作吗?“我“的测试之路...

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 自学软件测试当然…

【C++】C++学习前言

C前言与发展 一.什么是C二.C的发展史三.C的重要性 一.什么是C C语言是结构化和模块化的语言&#xff0c;适合处理较小规模的程序。对于复杂的问题&#xff0c;规模较大的程序&#xff0c;需要高度的抽象和建模时&#xff0c;C语言则不合适。为了解决软件危机&#xff0c; 20世纪…

猫生骨肉冻干价格合理区间是多少?真正性价比高的生骨肉冻干推荐

随着养猫知识的普及&#xff0c;生骨肉冻干喂养受到越来越多铲屎官的欢迎。然而&#xff0c;价格因素仍是部分铲屎官的阻碍。实际上&#xff0c;像我这样的资深铲屎官&#xff0c;早已认识到生骨肉冻干的价值。虽然生骨肉冻干的价格相对于烘焙粮和膨化粮要高一些&#xff0c;但…

前端之用HTML做一个汇款单

例子 代码 里面注释是我我对运用到的知识的理解 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>工商银行电子汇款单</title> </head> <body><h3>工商银行电子汇款单</…

面试题02.07.链表相交

方法一&#xff1a;暴力 public ListNode getIntersectionNode(ListNode headA, ListNode headB) {//先获得链表长度ListNode l1 headA;ListNode l2 headB;int m 0, n 0;while(l1 ! null){m;l1 l1.next;}while(l2 ! null){n;l2 l2.next;}ListNode l3 headA;for(int i …

什么是Redis的数据分片?

Redis的数据分片(sharding)是一种将一个Redis数据集分割成多个部分&#xff0c;分别存诸在不同的Redis节点上的技术。它可以用于将一个单独的Redis数据库扩展到多个物理机器上&#xff0c;从而提高Redis集群的性能和可扩展性 Redis数据分片的实现方式通常是将数据按照某种规则(…

独家直播!手机无人APP,让你跟上最新潮流

手机无人APP&#xff0c;是一款极具创新力和前瞻性的应用&#xff0c;旨在让用户随时随地跟上最新潮流。通过独家直播功能&#xff0c;用户可以第一时间了解世界各地的潮流资讯&#xff0c;与时俱进&#xff0c;展现出自己的个性和风采。 无人APP的直播功能实现了信息的即时传…

工业涂装行业的物联网解决方案

工业涂装行业的物联网解决方案 工业涂装行业在制造业中占据重要地位&#xff0c;其产品质量直接影响到最终产品的外观和性能。然而&#xff0c;传统涂装生产线容易出现质量问题&#xff0c;如色差、光泽度不均、橘皮现象等。为了解决这些问题&#xff0c;工业涂装行业需要寻求…

String 底层为什么使用 final 修饰?

1、典型回答 对于这个问题&#xff0c;Java之父詹姆斯 高斯林&#xff08;James Gosling&#xff09; 是这样回答的&#xff1a; I would use an immutable whenever I can 翻译为中文&#xff1a;只要允许&#xff0c;我就会使用不可变对象 而作为普通人的我们来说&#xff0…

算法---双指针练习-8(四数之和)

四数之和 1. 题目解析2. 讲解算法原理3. 编写代码 1. 题目解析 题目地址&#xff1a;四数之和 2. 讲解算法原理 首先对输入的数组进行排序&#xff0c;以便后续使用双指针法。初始化一个空的二维向量 ret&#xff0c;用于存储结果。对数组中的每个元素 nums[i] 进行遍历&#…

mysql主键和外键的区别

mysql主键和外键的区别 一、主键与外键存在的意义 主键和外键在关系数据库中扮演着至关重要的角色&#xff0c;它们共同维护着数据的完整性和一致性。 主键&#xff1a;它是每条记录的唯一标识符&#xff0c;犹如人的身份证号码&#xff0c;具有唯一性和非空性。例如&#x…

Linux基础命令[15]-less

文章目录 1. less 命令说明2. less 命令语法3. less 命令示例3.1 不加参数3.2 -N&#xff08;显示行号&#xff09;3.3 打开多个文件3.4 标记导航3.5 搜索内容 4. 总结 1. less 命令说明 less&#xff1a;用来分页查看文件&#xff0c;与 more 相比更加的灵活&#xff0c;并且…

算法之二分查找算法

二分查找算法简介 1. 首先说明二分查找算法是比较恶心, 细节很多, 很容易写出死循环的算法, 但熟悉了之后是最简单的算法. 2. 其次我们可能听说过二分查找的前提是数组有序的前提下进行, 但其实不一定. 3. 二分查找算法有一套模板: 朴素的二分模板: 比较简单, 但是有局限性查找…

docker——启动各种服务

1.Mysql 2.Redis 3.nginx 4.ES 注意&#xff1a;ES7之后环境为 -e ELASTICSEARCH_HOSTS http://ip地址:9200

【libwebrtc】基于m114的构建

libwebrtc A C++ wrapper for binary release, mainly used for flutter-webrtc desktop (windows, linux, embedded).是 基于m114版本的webrtc 最新(20240309 ) 的是m122了。官方给出的构建过程 .gclient 文件 solutions = [{"name" : src,"url

用信号的方式回收僵尸进程

当子进程退出后&#xff0c;会给父进程发送一个17号SIGCHLD信号&#xff0c;父进程接收到17号信号后&#xff0c;进入信号处理函数调用waitpid函数回收僵尸进程若多个子进程同时退出后&#xff0c;这是切回到父进程&#xff0c;此时父进程只会处理一个17号信号&#xff0c;其他…

植物病害识别:YOLO水稻病害识别/分类数据集(2000多张,2个类别,yolo标注)

YOLO水稻病害识别/分类数据集&#xff0c;包含疾病和正常2类&#xff0c;共2000多张图像&#xff0c;yolo标注完整&#xff0c;可直接训练。 适用于CV项目&#xff0c;毕设&#xff0c;科研&#xff0c;实验等 需要此数据集或其他任何数据集请私信

解决input事件监听拼音输入法导致高频事件

1、业务场景 在文本框中输入内容&#xff0c;执行查询接口&#xff0c;但遇到一个问题&#xff0c;当用拼音打字写汉字去搜索的时候&#xff0c;会输入一些字母拼成汉字&#xff0c;怎么能监听等拼音文字输入完成后再触发文本框监听事件 2、解决方案 通过查阅资料得知在输入中…

算法学习11:树与图的 DFS、BFS

算法学习11&#xff1a;树与图的 DFS、BFS 文章目录 算法学习11&#xff1a;树与图的 DFS、BFS前言一、树与图的深度优先遍历1.例题&#xff1a;树的重心&#xff1a; 二、树与图的宽度优先遍历1.例题&#xff1a;图中点的层次&#xff1a; 三、拓扑排序&#xff1a;&#xff0…