C++数据结构:手撕AVL树

news/2024/4/25 11:57:56/文章来源:https://blog.csdn.net/weixin_43908419/article/details/130342227

目录

一. 什么是AVL树

二. AVL树的节点定义

三. AVL树的插入操作

3.1 寻找插入位置

3.2 更新平衡因子

3.3 AVL树的旋转调整

3.4 AVL树插入操作的整体实现

四. AVL树的检验

附录:AVL树的实现完整代码 

AVL树定义代码 -- AVLTree.h

AVL树检验代码 -- test.cpp


一. 什么是AVL树

AVL树,是二叉搜索树的一种特殊形式。一般的二叉搜索树,如果插入节点的数据有序或十分接近有序,那么二叉搜索树就会退化为近似单叉树,这样查找数据的时间复杂度就会变为O(1),从而失去高效查找的能力。

为了解决普通二叉搜索树的这一缺陷,两位俄罗斯数学家G.M.Adelson-Velskii 和E.M.Landis在1962年发明了一种二叉搜索树的平衡模式 -- AVL树,AVL树的左右子树的高度差不超过1,因此可以保证时间复杂度为O(logN)的查找。

AVL树要么为空树,要么满足以下三个条件:

  • 左右子树均为AVL树。
  • 左右子树的高度差(平衡因子)不超过1。
  • 节点的数据满足二叉搜索树(左子树节点小于根节点,右子树节点大于根节点)

其中,平衡因子 = 右子树高度 - 左子树高度。

图1.1 AVL树典型结构

二. AVL树的节点定义

AVL树一般定义为三叉链结构,每个节点包含3个指针,分别为:指向左子树根节点的指针_left、指向右子树根节点的指针_right、指向父亲节点的指针_parent。同时,每个节点还应当记录该节点的平衡因子_bf以及节点数据_kv(键值对)。

图2.1 AVL树的节点结构示意图

代码2.1:(AVL树节点定义)

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

三. AVL树的插入操作

3.1 寻找插入位置

AVL树寻找插入位置的操作规则,与普通的二叉搜索树一致,为:

  • 如果当前位置为nullptr,那么该位置为插入节点的位置。
  • 如果当前节点值大于要插入的值,到该节点的左子树去查找。
  • 如果当前节点值小于要插入的值,到该节点的右子树去查找。
  • 如果当前节点值等于要插入的值,则插入失败。(二叉搜索树一般不允许存在相同的节点)。

代码3.1:(查找插入位置并插入节点)

	//找要插入节点的位置Node* cur = _root;   //root为AVL树的根节点Node* parent = nullptr;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;}}//插入节点Node* newNode = new Node(kv);if (parent->_kv.first > kv.first){parent->_left = newNode;newNode->_parent = parent;}else{parent->_right = newNode;newNode->_parent = parent;}

3.2 更新平衡因子

由于插入节点会影响新插入节点的父亲节点的左右子树高度,因此,父亲节点的平衡因子需要更新,平衡因子的更新遵循以下几条规则。

  1. 如果新节点插入在右子树,那么父亲节点的平衡因子+1。
  2. 如果新节点插入在左子树,那么父亲节点的平衡因子-1。
  3. 若插入节点后,parent->_bf == 1或-1成立,说明parent节点原来的平衡因子为0,左右子树高度相同,那么新插入的节点引发了parent的高度变化,但还没有打破平衡,需要继续向上更新。
  4. 如果插入节点后,parent->_bf == 0成立,那么原来父亲节点的平衡因子为-1或1,且插入在了父亲节点较矮的子树中,parent的高度没有发生改变,不继续向上更新。
  5. 若插入节点后,parent->_bf == 2或-2成立,那么parent原来的平衡因子为1或-1,处于平衡的临界状态,继续插入节点平衡被打破,不再满足AVL树的结构要求,需要进行旋转调整(详见3.3)。
  6. 若插入节点后,parent->_bf>=3或parent->_bf<=-3,那说明之前的插入操作存在问题,需要进行检查排错。
图3.1 平衡因子调整逻辑

代码3.2:(调整平衡因子)

	//调整平衡因子cur = newNode;  //新节点while (parent){//如果新插入的节点位于右子树,那么父亲节点平衡因子+1if (parent->_right == cur){++parent->_bf;}else if (parent->_left == cur){//如果新插入的节点位于左子树,那么父亲节点的平衡因子-1--parent->_bf;}else{//int a = 0;assert(false);}if (parent->_bf == 0){//如果插入节点后父亲节点的平衡因子变为0,那么父亲节点原来的平衡因子为1或-1//那么插入节点在较矮的一侧,树的高度没有发生变化break;}else if (parent->_bf == 1 || parent->_bf == -1){//说明原来父亲节点的平衡因子为0,插入后树的高度发生变化,要继续向上更改平衡因子parent = parent->_parent;cur = cur->_parent;}else if (parent->_bf == 2 || parent->_bf == -2){//在较长的一边插入,此时结构已不满足AVL树的结构,要进行旋转// ......  旋转调整代码略break;}else if (parent->_bf >= 3 || parent->_bf <= -3){assert(false);}}

3.3 AVL树的旋转调整

如果插入新节点后,parent->_bf == 2/-2成立,那么就需要通过旋转调整来使树的结构平衡,对于AVL树的选择,可以分为以下四种情况来讨论。

  1. 如果插入节点在较高右子树的右子树 -- 右右:单左旋调整
  2. 如果插入节点在较高左子树的左子树 -- 左左:单右旋调整
  3. 如果插入节点在较高右子树的左子树 -- 右左:先进行单右旋再进行单左旋(右左双旋调整)
  4. 如果插入节点在较高左子树的右子树 -- 左右:先进行单左旋再进行单右旋(左右双旋调整)

左单旋

  • 将右子节点的左子节点托管给parent的右子节点,然后将parent节点托管给右子节点的左子节点。之后,将原父亲节点和右子节点的平衡因子全部更新为0。
图3.2 左单旋逻辑图

代码3.3:(左单旋)

	void RotateL(Node* parent)   //左单旋函数{Node* ppNode = parent->_parent;Node* pR = parent->_right;Node* pRL = pR->_left;//右子节点的左子节点托管给父亲节点的右子节点parent->_right = pRL;if (pRL != nullptr){pRL->_parent = parent;}//父亲节点托管给右子节点的左子节点pR->_left = parent;parent->_parent = pR;if (_root == parent){_root = pR;pR->_parent = nullptr;}else{pR->_parent = ppNode;if (ppNode->_left == parent){ppNode->_left = pR;}else{ppNode->_right = pR;}}parent->_bf = 0;pR->_bf = 0;}

右单旋

  • 将左子节点的右子节点托管给parent的左子节点,然后将parent节点托管给原左子节点的右子节点,更新原parent节点和左子节点的平衡因子为1。
图3.3 右单旋逻辑图

代码3.4:(右单旋) 

	void RotateR(Node* parent)    //右单旋函数{Node* ppNode = parent->_parent;Node* pL = parent->_left;Node* pLR = pL->_right;//将左子节点的右子节点托管给父亲节点的左子节点parent->_left = pLR;if (pLR != nullptr){pLR->_parent = parent;}//将父亲节点托管给左子节点的右子节点pL->_right = parent;parent->_parent = pL;if (_root == parent){_root = pL;pL->_parent = nullptr;}else{pL->_parent = ppNode;if (ppNode->_left == parent){ppNode->_left = pL;}else{ppNode->_right = pL;}}pL->_bf = 0;parent->_bf = 0;}

右左双旋

先对parent节点的右子节点pR进行右单旋操作,然后对parent节点进行左单旋操作。根据pR的左子节点pRL的平衡因子(三种情况讨论),确定旋转后每个节点的平衡因子进行更新。

图3.4 右左双旋的三种情况

 代码3.5:(右左双旋)

	void RotateRL(Node* parent)   //右左双旋函数{Node* pR = parent->_right;Node* pRL = pR->_left;int bf = pRL->_bf;    //右子节点的左子节点的平衡因子RotateR(pR);  //对右子节点进行右单旋RotateL(parent);  //对父亲节点进行左单旋//更新平衡因子if (bf == 1){pR->_bf = 0;parent->_bf = -1;pRL->_bf = 0;}else if (bf == -1){pR->_bf = 1;parent->_bf = 0;pRL->_bf = 0;}else if(bf == 0){pRL->_bf = 0;pR->_bf = 0;parent->_bf = 0;}else{assert(false);}}

左右双旋

先针对parent节点的左子节点pL进行单左旋,然后再对parent节点进行单右旋,最后根据pL节点的右子节点pLR的平衡因子,更改每个节点的平衡因子。

图3.5 左右双旋的三种情况

代码3.6:(左右双旋)

	void RotateLR(Node* parent){Node* pL = parent->_left;Node* pLR = pL->_right;int bf = pLR->_bf;RotateL(pL);RotateR(parent);   //前后执行左右单旋if (bf == 1){pL->_bf = -1;parent->_bf = 0;pLR->_bf = 0;}else if (bf == -1){pL->_bf = 0;parent->_bf = 1;pLR->_bf = 0;}else if(bf == 0){pL->_bf = 0;parent->_bf = 0;pLR->_bf = 0;}else{assert(false);}}

3.4 AVL树插入操作的整体实现

通过3.1~3.3的分析,我们可以总结出,AVL树的插入操作步骤如下:

  1. 查找插入位置,找到了就新建节点插入,找不到函数终止运行。
  2. 调整平衡因子。
  3. 如果出现parent->_bf == 2或parent->_bf == -2,那么就对AVL树进行旋转调整。

代码3.7:(AVL树插入操作的主函数)

    bool insert(const std::pair<K, V>& kv)  //节点插入函数{if (_root == nullptr){Node* newNode = new Node(kv);   //新节点_root = newNode;return true;}//找要插入节点的位置Node* cur = _root;Node* parent = nullptr;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;}}//插入节点Node* newNode = new Node(kv);if (parent->_kv.first > kv.first){parent->_left = newNode;newNode->_parent = parent;}else{parent->_right = newNode;newNode->_parent = parent;}//调整平衡因子cur = newNode;  //新节点while (parent){//如果新插入的节点位于右子树,那么父亲节点平衡因子+1if (parent->_right == cur){++parent->_bf;}else if(parent->_left == cur){//如果新插入的节点位于左子树,那么父亲节点的平衡因子-1--parent->_bf;}else{//int a = 0;assert(false);}if (parent->_bf == 0){//如果插入节点后父亲节点的平衡因子变为0,那么父亲节点原来的平衡因子为1或-1//那么插入节点在较矮的一侧,树的高度没有发生变化break;}else if (parent->_bf == 1 || parent->_bf == -1){//说明原来父亲节点的平衡因子为0,插入后树的高度发生变化,要继续向上更改平衡因子parent = parent->_parent;cur = cur->_parent;}else if (parent->_bf == 2 || parent->_bf == -2){//在较长的一边插入,此时结构已不满足AVL树的结构,要进行旋转//旋转要分4种情况讨论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);}else{assert(false);}break;}else if(parent->_bf >= 3 || parent->_bf <= -3){assert(false);}}return true;}

四. AVL树的检验

要验证通过插入节点创建的AVL树是否正确,应当通过下面两重检验:

  • 验证其为搜索二叉树。
  • 验证其为平衡树。

验证搜索二叉树

搜索二叉树的检验,只需对二叉树进行中序遍历,如果得到的结果为升序排列的数据,那就说明该树为搜索二叉树。

代码4.1:(中序遍历)

	//中序遍历函数void InOrder(){_InOrder(_root);std::cout << std::endl;}//子函数void _InOrder(Node* root){if (root == nullptr){return;}_InOrder(root->_left);std::cout << root->_kv.first << " ";_InOrder(root->_right);}

验证平衡树

平衡树的验证也需要以下两重检验:

  • 检验左右子树的高度差是否不超过1,如果任何一颗子树的左右子树高度差超过1,则不满足平衡树的结构要求。
  • 检验平衡因子是否正确,算出 右子树高度 - 左子树高度 的值,与本节点的平衡因子比较,看是否相等,不等就不是平衡树。

代码4.2:(平衡树检验)

	bool isAVLTree()  //检查是否为AVL树(平衡树){return _isAVLTree(_root);}bool _isAVLTree(Node* root){if (root == nullptr){return true;}int leftHigh = TreeHigh(root->_left);int rightHigh = TreeHigh(root->_right);   //调用TreeHigh函数求左右子树高度if (root->_bf != rightHigh - leftHigh){std::cout << "平衡因子异常" << " ";return false;}return abs(rightHigh - leftHigh) < 2 && _isAVLTree(root->_left) && _isAVLTree(root->_right);}

代码4.3:(求二叉树的高度)

	int TreeHigh(Node* root){if (nullptr == root){return 0;}int left = TreeHigh(root->_left);int right = TreeHigh(root->_right);return left > right ? left + 1 : right + 1;}

附录:AVL树的实现完整代码 

AVL树定义代码 -- AVLTree.h

//AVLTree.h
#pragma once#include<iostream>
#include<utility>
#include<assert.h>
#include<math.h>template<class K, class V>
struct AVLTreeNode
{AVLTreeNode<K, V>* _left;AVLTreeNode<K, V>* _right;AVLTreeNode<K, V>* _parent;std::pair<K, V> _kv;int _bf;   //平衡因子AVLTreeNode(const std::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 std::pair<K, V>& kv)  //节点插入函数{if (_root == nullptr){Node* newNode = new Node(kv);   //新节点_root = newNode;return true;}//找要插入节点的位置Node* cur = _root;Node* parent = nullptr;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;}}//插入节点Node* newNode = new Node(kv);if (parent->_kv.first > kv.first){parent->_left = newNode;newNode->_parent = parent;}else{parent->_right = newNode;newNode->_parent = parent;}//调整平衡因子cur = newNode;  //新节点while (parent){//如果新插入的节点位于右子树,那么父亲节点平衡因子+1if (parent->_right == cur){++parent->_bf;}else if(parent->_left == cur){//如果新插入的节点位于左子树,那么父亲节点的平衡因子-1--parent->_bf;}else{//int a = 0;assert(false);}if (parent->_bf == 0){//如果插入节点后父亲节点的平衡因子变为0,那么父亲节点原来的平衡因子为1或-1//那么插入节点在较矮的一侧,树的高度没有发生变化break;}else if (parent->_bf == 1 || parent->_bf == -1){//说明原来父亲节点的平衡因子为0,插入后树的高度发生变化,要继续向上更改平衡因子parent = parent->_parent;cur = cur->_parent;}else if (parent->_bf == 2 || parent->_bf == -2){//在较长的一边插入,此时结构已不满足AVL树的结构,要进行旋转//旋转要分4种情况讨论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);}else{assert(false);}break;}else if(parent->_bf >= 3 || parent->_bf <= -3){assert(false);}}return true;}//中序遍历函数void InOrder(){_InOrder(_root);std::cout << std::endl;}bool isAVLTree()  //检查是否为AVL树{return _isAVLTree(_root);}private:bool _isAVLTree(Node* root){if (root == nullptr){return true;}int leftHigh = TreeHigh(root->_left);int rightHigh = TreeHigh(root->_right);if (root->_bf != rightHigh - leftHigh){std::cout << "平衡因子异常" << " ";return false;}return abs(rightHigh - leftHigh) < 2 && _isAVLTree(root->_left) && _isAVLTree(root->_right);}int TreeHigh(Node* root){if (nullptr == root){return 0;}int left = TreeHigh(root->_left);int right = TreeHigh(root->_right);return left > right ? left + 1 : right + 1;}void _InOrder(Node* root){if (root == nullptr){return;}_InOrder(root->_left);std::cout << root->_kv.first << " ";_InOrder(root->_right);}void RotateL(Node* parent)   //左单旋函数{Node* ppNode = parent->_parent;Node* pR = parent->_right;Node* pRL = pR->_left;//右子节点的左子节点托管给父亲节点的右子节点parent->_right = pRL;if (pRL != nullptr){pRL->_parent = parent;}//父亲节点托管给右子节点的左子节点pR->_left = parent;parent->_parent = pR;if (_root == parent){_root = pR;pR->_parent = nullptr;}else{pR->_parent = ppNode;if (ppNode->_left == parent){ppNode->_left = pR;}else{ppNode->_right = pR;}}parent->_bf = 0;pR->_bf = 0;}void RotateR(Node* parent)    //右单旋函数{Node* ppNode = parent->_parent;Node* pL = parent->_left;Node* pLR = pL->_right;//将左子节点的右子节点托管给父亲节点的左子节点parent->_left = pLR;if (pLR != nullptr){pLR->_parent = parent;}//将父亲节点托管给左子节点的右子节点pL->_right = parent;parent->_parent = pL;if (_root == parent){_root = pL;pL->_parent = nullptr;}else{pL->_parent = ppNode;if (ppNode->_left == parent){ppNode->_left = pL;}else{ppNode->_right = pL;}}pL->_bf = 0;parent->_bf = 0;}void RotateRL(Node* parent)   //右左双旋函数{Node* pR = parent->_right;Node* pRL = pR->_left;int bf = pRL->_bf;    //右子节点的左子节点的平衡因子RotateR(pR);  //对右子节点进行右单旋RotateL(parent);  //对父亲节点进行左单旋//更新平衡因子if (bf == 1){pR->_bf = 0;parent->_bf = -1;pRL->_bf = 0;}else if (bf == -1){pR->_bf = 1;parent->_bf = 0;pRL->_bf = 0;}else if(bf == 0){pRL->_bf = 0;pR->_bf = 0;parent->_bf = 0;}else{assert(false);}}void RotateLR(Node* parent){Node* pL = parent->_left;Node* pLR = pL->_right;int bf = pLR->_bf;RotateL(pL);RotateR(parent);   //前后执行左右单旋if (bf == 1){pL->_bf = -1;parent->_bf = 0;pLR->_bf = 0;}else if (bf == -1){pL->_bf = 0;parent->_bf = 1;pLR->_bf = 0;}else if(bf == 0){pL->_bf = 0;parent->_bf = 0;pLR->_bf = 0;}else{assert(false);}}private:Node* _root = nullptr;  //根节点
};

AVL树检验代码 -- test.cpp

//test.cpp
#include<iostream>
#include<stdlib.h>
#include<time.h>
#include "AVLTree.h"void TestAVLTree1()
{AVLTree<int, int> at;int arr[] = { 10, 9, 7 };for (auto& e : arr){at.insert(std::make_pair(e, 0));}int a = 0;
}void TestAVLTree2()
{AVLTree<int, int> at;//srand(time(nullptr));for (int i = 0; i < 1000; ++i){int e = rand();std::cout << e << " " << "num=" << i << std::endl;at.insert(std::make_pair(e, i));bool ret = at.isAVLTree();if (!ret){std::cout << "不是AVL树" << " ";}else{std::cout << "是AVL树" << " ";}std::cout << std::endl;}at.InOrder();
}int main()
{TestAVLTree2();return 0;
}

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

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

相关文章

MFC加载动态gif图片文件C++语言,基于MFC的动画播放控件

MFC加载动态gif图片&#xff0c;使用VS2015环境 一、将下载的PictureEx.h和PictureEx.cpp放在工程文件的目录下&#xff0c;动态gif图片放在工程文件的res文件夹下&#xff1b;&#xff08;GIF动图下载 https://icons8.com/preloaders/en/search/move&#xff09; &#xff08…

软考软件设计师 操作系统笔记

操作系统地位 程序顺序执行&#xff08;进程管理&#xff09; 程序顺序执行的特征&#xff0c;顺序性封闭性可再现性 前趋图 P1结束后 V操作 SS1 P2操作前先执行S S -1 此时S0 一个箭头对应一个信号量 程序并发执行和前驱图 找到输入i计算c输出p&#xff0c;如果找不到就…

“老司机”机器视觉工程师警告,硬件,软件,固件,程序使用新版本务必谨慎

做任何事情之前&#xff0c;程序先保存。没保存&#xff0c;真的会哭的。千万别保存在系统盘。​ 机器视觉最终的目的解决是什么问题&#xff1f;项目验收结束。 如果公司不知道或者希望去测试新的东西&#xff0c;要积极主动去使用&#xff0c;也会学到很多新的东西&#xff…

Java版本企业电子招投标采购系统源代码——功能模块功能描述+数字化采购管理 采购招投标

​ 功能模块&#xff1a; 待办消息&#xff0c;招标公告&#xff0c;中标公告&#xff0c;信息发布 描述&#xff1a; 全过程数字化采购管理&#xff0c;打造从供应商管理到采购招投标、采购合同、采购执行的全过程数字化管理。通供应商门户具备内外协同的能力&#xff0c;为外…

塔望食研院丨百年益生菌,千亿市场正蓝海!

2022年12月塔望咨询开设塔望食品大健康消费研究院&#xff08;简称塔望食研院&#xff09;栏目&#xff0c;塔望食研院以“为食品行业品牌高质量发展赋能”为理念&#xff0c;将不定期发布食品大健康行业研究、消费研究报告。塔望食研院致力于结合外部数据、消费调研数据、企业…

Mybatis学习基础篇(一)——使用Maven快速搭建一个mybatis项目,并实现简单的增删改查

题外话&#xff1a; 在了解mybatis框架之前&#xff0c;我先说明一句&#xff0c;目前主流的框架技术层出不穷&#xff0c;每个人都有自己喜欢的技术框架&#xff0c;自己喜欢用就行。技术并没有高低之分&#xff0c;喜欢用就用&#xff0c;虽然目前大部分人都喜欢向新技术看齐…

C++、STL标准模板库和泛型编程 ——迭代器、 算法、仿函数(侯捷)

C、STL标准模板库和泛型编程 ——迭代器、 算法、仿函数 &#xff08;侯捷&#xff09; 迭代器iterator_category 算法accumulatefor_eachreplacecountfindsortbinary_search 仿函数 functors(六大部件中最简单的一种&#xff01;) 使用一个东西&#xff0c;却不明白它的道理&a…

4月21日第壹简报,星期五,农历三月初二

4月21日第壹简报&#xff0c;星期五&#xff0c;农历三月初二坚持阅读&#xff0c;静待花开1. 推特拒向大模型免费开放数据&#xff01;马斯克威胁起诉微软&#xff1b;Reddit宣布不再向大模型免费开放数据&#xff0c;要求科技巨头付费使用API接口。2. 浙江&#xff1a;鼓励杭…

2023.04.24 c++第六讲

作业&#xff1a; 1. 手动实现顺序栈&#xff0c;要求实现数据结构中&#xff0c;所有栈的相关操作 #include <iostream> #define MAXSIZE 20 //宏定义&#xff0c;栈的最大容量 using namespace std;template <typename T> class stacklink { pri…

FBEC大会 | 瑞云科技 CTO 赵志杰:元宇宙时代的基础设施——实时云渲染

​ FBEC未来商业生态链接大会于2023年2月24日在深圳福田大中华喜来登酒店盛大召开&#xff0c;本次大会由广东省游戏产业协会、深圳市互联网文化市场协会指导&#xff0c;陀螺科技主办。 大会以“勇毅前行逐光而上”为主题&#xff0c;以具有行业前瞻洞察的“探索者”为视角&a…

Three.js+TypeScript+Webpack学习记录(二)

使用环境参考 Node.js v16.19.1 正文 跟着文档画个线 看看 Three 的官方文档&#xff0c;起步 -> 画线 -> 没了&#xff1f;&#xff01;&#xff01; 不管怎么说&#xff0c;先画个线吧。 import * as THREE from threeconst scene new THREE.Scene() const camer…

PyTorch深度学习实战 | 基于深度学习的电影票房预测研究

基于深度学习的映前票房预测模型(Cross&Dense网络结构模型)&#xff0c;该模型通过影片基本信息如&#xff1a;电影类型、影片制式、档期和电影的主创阵容和IP特征等信息对上映影片的票房进行预测。 本篇采用451部电影作为训练模型&#xff0c;最后再在194部影片上进行测试…

【计网 从头自己构建协议】一、libpcap 介绍 手撕以太网帧

上一篇&#xff1a;IndexError: list index out of range 下一篇&#xff1a;[【计网 从头自己构建协议】二、收发 ARP 请求帧与响应帧] 介绍 理论的学习总是枯燥的&#xff0c;想要加深对理论的理解&#xff0c;最好的方法就是自己实践一遍。 想要亲手实现各种协议&#xf…

【音视频第17天】RTSP、RTMP协议初识

被叫去搞直播了&#xff0c;悲喜交加。先学习一下基本的技术栈&#xff0c;RTSP RTMP HTTP 先简单随便看看吧。 目录 什么是流媒体协议RTMPRTMP 工作原理 RTSPRTSP 工作原理 RTMP 与 RTSP 区别详细看看RTSP简介RTSP交互流程OPTIONSDESCRIBESETUPPLAYPAUSESET_PARAMETERGET_PAR…

春招,进阿里了....

个人背景是东北某 985 科班本硕&#xff0c;做的 测试开发&#xff0c;有两个自己写的小项目。下面是一些印象比较深刻的面试题 阿里一面 什么是软件测试&#xff1f; 软件测试过程中会面向哪些群体&#xff1f; 开发一个软件都要经过哪些阶段&#xff1f; 什么是黑盒测试&…

八年软件测试生涯,是时候做出改变了

五年前&#xff0c;我在南方的大城市&#xff1a;广州&#xff0c;做着一个快乐的游戏测试&#xff0c;工作不太忙&#xff0c;对一切技术充满了好奇心。测试工作不专业&#xff0c;也不受重视。但我有自己的快乐。工作不忙的时候&#xff0c;我今天学学Python&#xff0c;明天…

什么是客户服务平台?

在社交媒体和智能手机出现之前&#xff0c;品牌主要通过单向广告渠道与客户互动。社交媒体打破了这种自上而下的动态&#xff0c;以前所未有的方式打开了对话&#xff0c;将客户包括在内。 品牌不再控制客户对人们分享公司内容的行为。人们可以点击离开&#xff0c;向左滑动&a…

Python-pyppeteer解决微软Microsoft的登录机器人验证(8)

前言 本文是该专栏的第8篇,结合优质项目案例,让你精通使用Pyppeteer,后面会持续分享Pyppeteer的干货知识,记得关注。 在注册微软Microsoft账号或者注册outlook邮箱账号的时候,会遇到如下机器人验证: 是的,你可能第一眼看到这个验证页面,首先会想到是定位它的页面元素N…

非常详细的阻抗测试基础知识

编者注&#xff1a;为什么要测量阻抗呢&#xff1f;阻抗能代表什么&#xff1f;阻抗测量的注意事项... ...很多人可能会带着一系列的问题来阅读本文。不管是数字电路工程师还是射频工程师&#xff0c;都在关注各类器件的阻抗&#xff0c;本文非常值得一读。全文13000多字&#…

Vue CLI 服务

使用命令 在一个 Vue CLI 项目中&#xff0c;vue/cli-service 安装了一个名为 vue-cli-service 的命令。你可以在 npm scripts 中以 vue-cli-service、或者从终端中以 ./node_modules/.bin/vue-cli-service 访问这个命令。 这是你使用默认 preset 的项目的 package.json&…