[C++进阶]---AVL树模拟实现

目录

  • 1.AVL树的概念
  • 2.AVL树模拟实现
  • 2.1AVL树节点的定义
    • 2.2AVL的插入
    • 2.3AVL树的旋转
      • 2.3.1左单旋
      • 2.3.2右单旋
      • 2.3.3右左双旋
        • 2.3.3.1旋转情况分析
        • 2.3.3.2平衡因子更新分析
      • 2.3.4右左双旋
        • 2.3.4.1旋转情况分析
        • 2.3.4.2平衡因子更新分析
      • 2.3.5AVL树的验证
  • 3.AVL模拟实现源码
  • 4.总结

1.AVL树的概念

二叉搜索树虽可以缩短查找的效率,但如果数据有序或接近有序二叉搜索树将退化为单支树,查找元素相当于在顺序表中搜索元素,效率低下。因此,两位俄罗斯的数学家G.M.Adelson-Velskii和E.M.Landis在1962年发明了一种解决上述问题的方法:当向二叉搜索树中插入新结点后,如果能保证每个结点的左右子树高度之差的绝对值不超过1(需要对树中的结点进行调整),即可降低树的高度,从而减少平均搜索长度。
一棵AVL树或者是空树,或者是具有以下性质的二叉搜索树:

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

在这里插入图片描述
如果一棵二叉搜索树是高度平衡的,它就是AVL树。如果它有n个结点,其高度可保持在 O ( l o g 2 n ) O(log_2 n) O(log2n) ,搜索时间复杂度O( l o g 2 n log_2 n log2n)

2.AVL树模拟实现

2.1AVL树节点的定义

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):_left(nullptr),_right(nullptr),_parent(nullptr),_kv(kv),_bf(0){}
};

2.2AVL的插入

假设下面这棵树是我们最开始AVL树,接下来我们需要对其进行节点的插入,并针对不同的情况有不同的处理情况
在这里插入图片描述
1.新增节点在parent节点的左边,parent的平衡因子减一(减减)
在这里插入图片描述

2.新增节点在parent节点的右边,parent的平衡因子加一(加加)
3.更新parent平衡因子==0,说明parent所在的子树的高度不变,不会再影响祖先,不用继续沿着到root的路径继续往上更新,插入结束

在这里插入图片描述

4.更新后parent平衡因子==1 or -1,说明parent所在的子树的高度变化,会再影响祖先,需要继续沿着到root的路径继续往上更新
5.更新后parent平衡因子==2 or -2,说明parent所在的子树的高度变化且不平衡,对parent所在子树进行旋转,让子树平衡,插入结束(涉及旋转下面将分情况进行讲解)
在这里插入图片描述
6.更新到根节点,没有出现parent平衡因子==2 or -2的情况更新结束
在这里插入图片描述

2.3AVL树的旋转

旋转的时候涉及的问题:

旋转之后的树需要满足两个条件:1.保持这棵树是二叉搜索树;2.变成平衡树(AVL树),且降低这个子树的高度

2.3.1左单旋

当更新parent(30)节点的平衡因子为2cur(60)节点的平衡因子为1,造成parent(30)所在子树右边高,需要进行左单旋
在这里插入图片描述
以下是h等于0和h等于1的具象图:
在这里插入图片描述
根据上面两种右边子树比较高的情况,需要将cur的左子树链接成为parent的右子树,让parent链接成为cur的左子树。情况一:插入之前,AVL树高度为1;插入节点后,树高度为2,旋转之后,树高度为1且达到平衡,不用继续沿着到root的路径继续往上更新,插入结束;情况二:插入之前,AVL树高度为2;插入节点后,树高度为3;旋转之后,树高度为2且达到平衡,不用继续沿着到root的路径继续往上更新,插入结束。
代码如下:

void RotateL(Node* parent){Node* cur = parent->_right;Node* curleft = cur->_left;//curleft成为parent的右边parent->_right = curleft;//curleft可能为空if (curleft){//改变curleft父指针的指向curleft->_parent = parent;}//parent成为cur的左边cur->_left = parent;//记录parent的父节点指针Node* ppnode = parent->_parent;//改变parent父指针的指向parent->_parent = cur;//判断parent节点是否为根节点if (parent == _root){//cur成为根节点_root = cur;cur->_parent = nullptr;}else{//cur链接parent的父节点if (ppnode->_left == parent){ppnode->_left = cur;}else if (ppnode->_right == parent){ppnode->_right = cur;}cur->_parent = ppnode;}parent->_bf = cur->_bf = 0;}

2.3.2右单旋

当更新后,parent(30)节点的平衡因子为-2cur(60)节点的平衡因子为-1,造成parent(30)所在子树左边高,需要进行右单旋
在这里插入图片描述
以下是h等于0和h等于1的具象图:
在这里插入图片描述
情况一(h等于0): 插入之前,AVL树高度为1;插入节点后,树高度为2,旋转之后,树高度为1且达到平衡,不用继续沿着到root的路径继续往上更新,插入结束;情况二(h等于1): 插入之前,AVL树高度为2;插入节点后,树高度为3;旋转之后,树高度为2且达到平衡,不用继续沿着到root的路径继续往上更新,插入结束。
分析: 根据上面两种左边子树比较高的情况,需要将cur的右子树链接成为parent的左子树,让parent链接成为cur的左子树。局部根节点parent已经出现向左边倾斜的情况,当插入节点时导致parent所在子树左边高且不平衡,经过旋转后,cur成为新的局部根节点,cur所在子树的高度与未插入节点前的高度一致,不会对上层节点造成影响,达到平衡状态(即cur的平衡因子为0)

代码如下:

void RotateR(Node* parent){Node* cur = parent->_left;Node* curright = cur->_right;//curright链接成为parent的左子树parent->_left = curright;if (curright){//curright不为空情况进行,改变其父指针指向curright->_parent = parent;}//parent链接成为cur的右子树cur->_right = parent;//记录parent父指针Node* ppnode = parent->_parent;//让parent父指针指向curparent->_parent = cur;//cur与原parent指向父节点链接if (parent == _root){//parent为根节点的情况_root = cur;cur->_parent = nullptr;}else{//parent为局部子树根节点//判断parent原先为局部左子树还是局部右子树if (ppnode->_left == parent){ppnode->_left = cur;}else{ppnode->_right = cur;}cur->_parent = ppnode;}//更新parent\cur的平衡因子为0parent->_bf = cur->_bf = 0;}

2.3.3右左双旋

2.3.3.1旋转情况分析

当插入节点更新平衡因子后,parent(30)节点的平衡因子为2cur(60)节点的平衡因子为-1,造成parent(30)所在子树右边高,但右子树cur节点所在子树却左边高,这时候已经parent所在的子树并不是纯粹的右边高了,所以只进行左单旋不能解决问题,需要进行右左双旋。
在这里插入图片描述
以下是h等于0和h等于1的具象图:
仅仅进行左单旋操作:
在这里插入图片描述

从上面的图可以看出,若parent所在子树不是完全右边高的情况,对其进行左单旋操作不能使AVL树达到平衡

进行右左双旋操作:
在这里插入图片描述

parent(30)所在子树右边高,但右子树cur节点所在子树却左边高,像一个折线;需要先对以cur节点为根节点的子树进行右旋操作,然后对parent节点为根节点的子树进行左旋操作,经过双旋操作后,curleft代替parent节点成为新的(局部)根节点,解决了不平衡问题,使左右子树高度一致,达到平衡状态。

2.3.3.2平衡因子更新分析

在这里插入图片描述
问题: 双旋调整后,parent\cur\curleft三个节点的位置发生了变化,这三个节点的平衡因子是否都无脑改为0呢?答案:不是的!那么该这三个节点的平衡因子应该怎么修改呢?友友们不要着急,且和我一起慢慢分析吧!
h==0(curleft->_bf ==0)的情况:
在这里插入图片描述

分析: ①当curleft的平衡因子为0时,curleft没有节点给cur\parent节点,且cur\parent节点原本也无左右子树,所以cur\parent节点的平衡因子都更新为0

h==1(curleft->_bf ==-1)的情况:
在这里插入图片描述

分析: ②当curleft的平衡因子为-1curleft没有右节点可以链接成为cur节点的左子树,以cur节点为根节点右旋之后,造成cur节点所在子树右边偏高,所以cur节点的平衡因子更新为1curleft有左节点可以链接成为parent的右子树,以parent节点为根节点左旋之后,使parent节点左右子树高度一致达到平衡状态,所以parent的平衡因子也更新为0

h==1(curleft->_bf ==-1)的情况:
在这里插入图片描述

分析: ③当curleft的平衡因子为1curleft有右节点可以链接成为cur节点的左子树,以cur节点为根节点右旋之后,使cur节点左右子树高度一致达到平衡状态,所以cur节点的平衡因子更新为0curleft有左节点链接成为parent的右子树,以parent节点为根节点左旋之后,造成parent节点所在子树左边偏高,所以parent节点的平衡因子更新为-1

代码如下:

void RotateRL(Node* parent){Node* cur = parent->_right;Node* curleft = cur->_left;int bf = curleft->_bf;//旋转过程中,curleft的bf发生变化,需要提前记录//先以cur(parent->_right)为根节点右旋//再以parent为根节点左旋RotateR(cur);RotateL(parent);//针对不同curleft->_bf的情况进行处理if (bf == 0){curleft->_bf = 0;cur->_bf = 0;parent->_bf = 0;}else if (bf == 1){curleft->_bf = 0;cur->_bf = 0;parent->_bf = -1;}else if (bf == -1){curleft->_bf = 0;cur->_bf = 1;parent->_bf = 0;}else{assert(false);}}

右左双旋平衡因子更新总结分析:
分析上面三种平衡因子更新的情况,可以看出右左双旋(先右旋,再左旋)结果的本质是:curleft的右边给了cur的左边,curleft的左边给了parent的左边,cur节点和parent分别成为curleft的右左子树。

2.3.4右左双旋

2.3.4.1旋转情况分析

当插入节点更新平衡因子后,parent(90)节点的平衡因子为-2cur(30)节点的平衡因子为1,造成parent(90)所在子树左边高,但其左子树cur节点所在子树却左边高,这时候已经parent所在的子树并不是纯粹的左边高了,所以只进行右单旋不能解决问题,需要进行右左双旋。
在这里插入图片描述
以下是h等于0和h等于1的具象图:
仅仅进行右单旋操作:
在这里插入图片描述

从上面的图可以看出,若parent所在子树不是完全左边高的情况,对其进行右单旋操作不能使AVL树达到平衡

进行左右双旋操作:
在这里插入图片描述

parent(90)所在子树左边高,但右子树cur节点所在子树却右边高,像一个折线;需要先对以cur节点为根节点的子树进行左旋操作,然后对parent节点为根节点的子树进行右旋操作,经过双旋操作后,curleft代替parent节点成为新的(局部)根节点,解决了不平衡问题,使左右子树高度一致,达到平衡状态。

2.3.4.2平衡因子更新分析

h==0(curright->_bf ==0)的情况:
在这里插入图片描述

分析: ①当curright的平衡因子为0时,curright没有节点给cur\parent节点,且cur\parent节点原本也无左右子树,所以cur\parent节点的平衡因子都更新为0

h==-1(curright->_bf ==-1)的情况:
在这里插入图片描述

分析: ②当curright的平衡因子为-1curright有左节点链接cur节点的右子树,以cur节点为根节点左旋之后,使cur节点左右子树高度一致达到平衡状态,所以cur的平衡因子也更新为0curright没有右节点链接成为parent的左子树,以parent节点为根节点右旋之后,造成parent节点所在子树右边偏高,所以parent节点的平衡因子更新为1

h==1(curright->_bf ==1)的情况:
在这里插入图片描述

分析: ③当curright的平衡因子为1curright没有左节点可以链接cur节点的右子树,以cur节点为根节点左旋之后,造成parent节点所在子树左边偏高,所以cur的平衡因子也更新为-1curright有右节点可以链接成为parent的左子树,以parent节点为根节点右旋之后,使parent节点左右子树高度一致达到平衡状态,所以parent节点的平衡因子更新为0

代码如下:

void RotateLR(Node* parent){Node* cur = parent->_left;Node* curright = cur->_right;int bf = curright->_bf;//先以cur(parent->_left)为根节点左旋//再以parent为根节点右旋RotateL(parent->_left);RotateR(parent);//针对不同curright->_bf的情况进行处理if (bf == 0){curright->_bf = 0;cur->_bf = 0;parent->_bf = 0;}else if (bf == -1){curright->_bf = 0;cur->_bf = 0;parent->_bf = 1;}else if (bf == 1){curright->_bf = 0;cur->_bf = -1;parent->_bf = 0;}}

左右双旋平衡因子更新总结分析:
分析上面三种平衡因子更新的情况,可以看出左右双旋(先左旋,再右旋)结果的本质是:curright的左边给了cur的右边,curleft的右边给了parent的左边,cur节点和parent分别成为curleft的左右子树。

注意: ① 双旋是先左(右)旋,然后再右(左)旋,单旋转的原理是一样的,所以可以直接对前面左(右)旋函数调用,然后对右(左)旋函数调用,可增加代码复用性;②双旋平衡因子单独分析进行更新,与单旋转函数可以起到解耦合的作用。

2.3.5AVL树的验证

我们已经实现了AVL树插入操作的功能,为了保证所写的代码正确,我们可以插入数据进行检验。

使用测试AVL树平衡的函数+进行数据的插入检验

	int Height(){return Height(_root);}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 IsBalance(){return IsBalance(_root);}bool IsBalance(Node* root){if (root == nullptr){return true;}int leftHeight=Height(root->_left);int rightHeight = Height(root->_right);if (rightHeight - leftHeight != root->_bf){cout << "Exception:" << root->_kv.first << "->" << root->_bf  << endl;assert(false);return false;}return abs(leftHeight - rightHeight) < 2 && IsBalance(root->_left) && IsBalance(root->_right);}

测试1:

void test()
{//普通场景int a[] = { 16, 3, 7, 11, 9, 26, 18, 14, 15 };AVLTree<int, int> t;for (auto e : a){t.insert(make_pair(e, e));cout << "Insert:" << e << "->" << t.IsBalance() << endl;}cout << "所有数据插入成功" << endl;
}

代码运行结果为:
在这里插入图片描述
测试2:

void test2()
{//插入随机数const int N = 1000000;vector<int> v;v.reserve(N);srand(time(0));for (size_t i = 0; i < N; i++){v.push_back(rand()+i);}AVLTree<int, int> t;for (auto e : v){t.insert(make_pair(e, e));}cout << "所有数据插入成功" << endl;
}

代码运行结果如下:
在这里插入图片描述
测试3:

void test2()
{//插入随机数const int N = 1000000;vector<int> v;v.reserve(N);srand(time(0));for (size_t i = 0; i < N; i++){v.push_back(rand()+i);}AVLTree<int, int> t;for (auto e : v){t.insert(make_pair(e, e));}cout << "Insert:" << t.IsBalance() << endl;cout << "所有数据插入成功" << endl;
}

代码运行结果如下:
在这里插入图片描述

3.AVL模拟实现源码

#include<iostream>
#include<assert.h>
using namespace std;
template<class K,class V>
struct AVLTreeNode
{//建议声明顺序与初始化顺序一样AVLTreeNode<K, V>* _left;	//该节点的左孩子AVLTreeNode<K, V>* _right;	//该节点的右孩子AVLTreeNode<K, V>* _parent;	//该节点的双亲pair<K, V> _kv;int _bf;//该节点的平衡因子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->_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;//更新平衡因子while (parent){if (parent->_left == cur){parent->_bf--;}else{parent->_bf++;}//parent所在子树高度不变,会影响祖先,不需要更新平衡因子,插入结束if (parent->_bf == 0){break;}//parent所在子树高度变化,会影响祖先,继续沿着到root的路径更新平衡因子else if (parent->_bf == 1 || parent->_bf == -1){cur = parent;parent = parent->_parent;}//parent所在子树高度变化且不平衡,需要旋转parent所在子树else if (parent->_bf == 2 || parent->_bf == -2){//插入在parent右子树的右侧,造成parent右边高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);}}}void RotateL(Node* parent){Node* cur = parent->_right;Node* curleft = cur->_left;//curleft成为parent的右边parent->_right = curleft;//curleft可能为空if (curleft){//改变curleft父指针的指向curleft->_parent = parent;}//parent成为cur的左边cur->_left = parent;//记录parent的父节点指针Node* ppnode = parent->_parent;//改变parent父指针的指向parent->_parent = cur;//判断parent节点是否为根节点if (parent == _root){//cur成为根节点_root = cur;cur->_parent = nullptr;}else{//cur链接parent的父节点if (ppnode->_left == parent){ppnode->_left = cur;}else if (ppnode->_right == parent){ppnode->_right = cur;}cur->_parent = ppnode;}parent->_bf = cur->_bf = 0;}void RotateR(Node* parent){Node* cur = parent->_left;Node* curright = cur->_right;//curright链接成为parent的左子树parent->_left = curright;if (curright){//curright不为空情况进行,改变其父指针指向curright->_parent = parent;}//parent链接成为cur的右子树cur->_right = parent;//记录parent父指针Node* ppnode = parent->_parent;//让parent父指针指向curparent->_parent = cur;//cur与原parent指向父节点链接if (parent == _root){//parent为根节点的情况_root = cur;cur->_parent = nullptr;}else{//parent为局部子树根节点//判断parent原先为局部左子树还是局部右子树if (ppnode->_left == parent){ppnode->_left = cur;}else{ppnode->_right = cur;}cur->_parent = ppnode;}//更新parent\cur的平衡因子为0parent->_bf = cur->_bf = 0;}void RotateRL(Node* parent){Node* cur = parent->_right;Node* curleft = cur->_left;int bf = curleft->_bf;//旋转过程中,curleft的bf发生变化,需要提前记录//先以cur(parent->_right)为根节点右旋//再以parent为根节点左旋RotateR(cur);RotateL(parent);if (bf == 0){curleft->_bf = 0;cur->_bf = 0;parent->_bf = 0;}else if (bf == 1){curleft->_bf = 0;cur->_bf = 0;parent->_bf = -1;}else if (bf == -1){curleft->_bf = 0;cur->_bf = 1;parent->_bf = 0;}else{assert(false);}}void RotateLR(Node* parent){Node* cur = parent->_left;Node* curright = cur->_right;int bf = curright->_bf;//先以cur(parent->_left)为根节点左旋//再以parent为根节点右旋RotateL(parent->_left);RotateR(parent);if (bf == 0){curright->_bf = 0;cur->_bf = 0;parent->_bf = 0;}else if (bf == -1){curright->_bf = 0;cur->_bf = 0;parent->_bf = 1;}else if (bf == 1){curright->_bf = 0;cur->_bf = -1;parent->_bf = 0;}}int Height(){return Height(_root);}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 IsBalance(){return IsBalance(_root);}bool IsBalance(Node* root){if (root == nullptr){return true;}int leftHeight=Height(root->_left);int rightHeight = Height(root->_right);if (rightHeight - leftHeight != root->_bf){cout << "Exception:" << root->_kv.first << "->" << root->_bf  << endl;assert(false);return false;}return abs(leftHeight - rightHeight) < 2 && IsBalance(root->_left) && IsBalance(root->_right);}
private:Node* _root = nullptr;
};

4.总结

①AVL树的删除(了解)

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

② AVL树的性能

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

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

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

相关文章

jetpack compose 学习(2)

jetpack compose 学习(1) 学会了如何创建一个compose项目,并成功run了起来 今天学习run起来的界面配置 找启动界面,当然是先找到界面的配置文件: androidManifest.xml 这个文件,然后通过启动项找到主界面, 第二步 按住ctrl 鼠标指向MainActivity 即可跳转主界面 进入后的界面…

_try_finally原理探究

无论try结构体中是什么代码&#xff0c;都会执行finally里面的代码 局部展开 当try里面没有异常&#xff0c;而是return、continue、break等语句时&#xff0c;就不会走_except_handle3这个函数&#xff0c;而是调用_local_unwind2进行展开 然后调用[ebx esi*4 8] 跟进去就到…

MySQL低版本中:字符串中的数字、英文字符、汉字提取

我们如何提醒一个字段中的汉字和数字呢 高版本指mysql8.0以上 使用sql语句 SELECT REGEXP_REPLACE(column_name, [^\\p{Han}], ) AS chinese_characters FROM table_name;其中 column_name指名称列&#xff0c;table_name是表名 2.低版本使用 需要新建函数 DELIMITER $$DR…

Java调用百度翻译API和调用有道翻译API进行翻译

目录 界面编写 调用百度API 调用有道API 源代码 界面编写 我们首先需要设计出这个翻译程序的GUI界面&#xff0c;我们写一个类继承自JFrame类&#xff0c;用来展示程序的主窗口&#xff0c;设置好窗口的名称和大小&#xff0c;设置在关闭窗口时终止程序&#xff0c;为了界…

高效数组处理的Numpy入门总结

NumPy是Python中一个重要的数学库&#xff0c;它提供了高效的数组操作和数学函数&#xff0c;是数据科学、机器学习、科学计算等领域的重要工具。下面是一个简单的NumPy学习教程&#xff0c;介绍了NumPy的基本用法和常用函数。 安装NumPy 在使用NumPy之前&#xff0c;需要先安…

删除Tencent files

QQ或者TIM卸载后,它还会残留在电脑上.要修改会说需要权限,修改权限又会重新变回去.这时候可以把整个文档放到其它盘,然后就可以删除Tencent files 方法是右键文档 选中属性–位置 然后就可以改掉了

【后端学前端】第三天 css动画 动态搜索框(定位、动态设置宽度)

1、学习信息 视频地址&#xff1a;css动画 动态搜索框&#xff08;定位、动态设置宽度&#xff09;_哔哩哔哩_bilibili 2、源码 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>test3</title>…

14:00面试,14:08就出来了,问的问题有点变态。。。。。。

从小厂出来&#xff0c;没想到在另一家公司又寄了。 到这家公司开始上班&#xff0c;加班是每天必不可少的&#xff0c;看在钱给的比较多的份上&#xff0c;就不太计较了。没想到5月一纸通知&#xff0c;所有人不准加班&#xff0c;加班费不仅没有了&#xff0c;薪资还要降40%…

Flutter的BuildContext简介

文章目录 BuildContext 简介BuildContext的主要作用 BuildContext 简介 BuildContext是Flutter中的一个重要概念&#xff0c;表示当前Widget在树中的位置上下文。它是一个对Widget树的一个位置的引用&#xff0c;用于查找、访问和操作该位置上的相关信息。每个Widget都有一个关…

VMware Fusion Pro 中文 for mac:好用的虚拟机

VMware Fusion 不仅能让你在 Mac 苹果电脑上运行 Windows 或 Linux 系统、使用非 Mac 平台的软件&#xff0c;而且还可以支持各种 USB硬件设备&#xff0c;它无疑是 macOS 用户的最佳助手。VMWare Fusion 是在 Mac 下虚拟运行 Windows / Linux 的最佳方式 无比轻松地在 Mac 上…

Map、Set集合与Gson注解

一、Map的序列化与反序列化 1.Map集合类型对象在反序列化时与List一样&#xff0c;需要使用TypeToken完成反序列化。 2.Set在反序列化时需要使用TypeToken完成反序列化。 创建User类和job类 public class User {Exposeprivate String userName;Exposeprivate String passwo…

Netflix Mac(奈飞客户端)激活版软件介绍

Netflix Mac(奈飞客户端)是一款流行的视频播放软件&#xff0c;专为Mac用户设计。它提供了大量的高质量电影、电视剧、纪录片和动画片资源&#xff0c;让用户可以随时随地观看自己喜欢的内容。 首先&#xff0c;Netflix Mac(奈飞客户端)以其简洁直观的用户界面而闻名。用户可以…

2024测试开发面试题完整版本(附答案)

目录 1. 什么是软件测试&#xff0c; 谈谈你对软件测试的了解 2. 我看你简历上有写了解常见的开发模型和测试模型, 那你跟我讲一下敏捷模型 3. 我看你简历上还写了挺多开发技能的, 那你给我讲讲哈希表的实现流程 4. 谈一谈什么是线程安全问题, 如何解决 5. 既然你选择走测…

Java - Spring中BeanFactory和FactoryBean的区别

BeanFactory Spring IoC容器的顶级对象&#xff0c;BeanFactory被翻译为“Bean工厂”&#xff0c;在Spring的IoC容器中&#xff0c;“Bean工厂”负责创建Bean对象。 BeanFactory是工厂。 FactoryBean FactoryBean&#xff1a;它是一个Bean&#xff0c;是一个能够辅助Spring实例…

【项目管理】CMMI对项目管理有哪些个人启发和思考

导读&#xff1a;本人作为项目经理参与公司CMMI5级评审相关材料准备工作&#xff0c;现梳理CMMI有关知识点&#xff0c;并结合项目给出部分示例参考&#xff0c;以及本人对于在整理材料过程中一些启发和体验思考。 目录 1、CMMI定义 2、CMMI-5级 3、CMMI文档清单 4、示例-度…

Node.js黑马时钟案例

先上没有使用node.js之前的html部分代码 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title></title><style>* {margin: 0;padding: 0;}html,body {height: 100%;overflow: hidden;backgrou…

Zabbix+Grafana

背景 对指标采集 将采集的信息存储 可视化 报警 因为节点上本身就是zabbix&#xff0c;但对应的server在数据中心&#xff0c;不知道一个agent可否服务于多个server端&#xff0c;而且不确定数据中心是否会提供用户。所以还是放弃zabbix 架构

如何使用CFImagehost结合内网穿透搭建简洁易用的私人图床并远程访问

文章目录 1.前言2. CFImagehost网站搭建2.1 CFImagehost下载和安装2.2 CFImagehost网页测试2.3 cpolar的安装和注册 3.本地网页发布3.1 Cpolar临时数据隧道3.2 Cpolar稳定隧道&#xff08;云端设置&#xff09;3.3.Cpolar稳定隧道&#xff08;本地设置&#xff09; 4.公网访问测…

OBS如何安装NDI 插件

能检索到这个文章的人&#xff0c;想必是知道什么是OBS和NDI&#xff0c;也知道他们的用途的。所以&#xff0c;不作赘述。4 安装NDI插件 1 直接访问https://github.com/obs-ndi/obs-ndi&#xff0c;打不开的同志你们自己想办法。 2 有两个东西要下载&#xff0c;如下图所示&a…

Python中栈的概念和使用

目录 一、引言 二、栈的概念 三、栈的使用 1、创建栈 2、入栈操作 3、出栈操作 4、查看栈顶元素 5、判断栈是否为空 四、应用场景 1、函数调用 2、表达式求值 3、深度优先搜索 4、括号匹配 五、总结 一、引言 栈是一种重要的数据结构&#xff0c;它遵循后进先出…