【C++篇】树影摇曳,旋转无声:探寻AVL树的平衡之道
文章目录
- 从结构到操作:手撕AVL树的实现
- 一、AVL树介绍
- 1.1 什么是AVL树
- 1.2 平衡因子的定义
- 1.3 平衡的意义
- 1.4 AVL树的操作
- 二、AVL树的节点结构
- 2.1 节点结构的定义:
- 三、插入操作
- 3.1 插入操作概述
- 3.2 步骤1:按二叉查找树规则插入节点
- 3.3 步骤2:更新平衡因子
- 3.4 步骤3:旋转操作详解
- 3.4.1 右单旋
- 3.4.2 左单旋
- 3.4.3 左右双旋
- 3.4.4 右左双旋
- 四、其他操作
- 4.1 查找操作 (`Find`)
- 4.2 计算树的高度 (`Height`)
- 4.3 计算节点数量 (`Size`)
- 4.4 树是否平衡 (`IsBalanceTree`)
- 五、性能分析与测试
- 5.1 性能分析
- 5.2 测试代码
- 六、全部代码
- 七、总结与展望
从结构到操作:手撕AVL树的实现
💬 欢迎讨论:如果你在阅读过程中有任何疑问或想要进一步探讨的内容,欢迎在评论区留言!我们一起学习、一起成长。
👍 点赞、收藏与分享:如果你觉得这篇文章对你有帮助,记得点赞、收藏并分享给更多的朋友!
🚀 逐步实现AVL树:本篇文章将带你一步一步实现一个自平衡的二叉查找树——AVL树,从最基本的节点结构开始,逐步实现插入、查找等操作,并确保每个步骤都详细讲解。
一、AVL树介绍
1.1 什么是AVL树
AVL树是一种自平衡的二叉查找树(BST),由G.M. Adelson-Velsky和E.M. Landis于1962年提出。AVL树的核心特点是它保证树的每个节点的左右子树的高度差(平衡因子)不超过1,从而保证了AVL树在插入、删除和查找操作时的时间复杂度始终为O(log N)。
1.2 平衡因子的定义
每个节点都有一个平衡因子(_bf
),它表示节点左子树的高度减去右子树的高度:
平衡因子 = 左子树的高度 − 右子树的高度 \text{平衡因子} = \text{左子树的高度} - \text{右子树的高度} 平衡因子=左子树的高度−右子树的高度
- 如果平衡因子为 0,表示左右子树的高度相等。
- 如果平衡因子为 1,表示左子树比右子树高1。
- 如果平衡因子为 -1,表示右子树比左子树高1。
1.3 平衡的意义
AVL树的自平衡特性确保了其查询、插入和删除操作的最坏时间复杂度为O(log N),避免了普通二叉查找树的退化(比如变成链表)。因此,AVL树非常适用于需要频繁插入和删除操作的场景。
1.4 AVL树的操作
AVL树的主要操作有:
- 插入操作:在树中插入节点,并在插入后调整平衡因子。如果平衡因子为2或-2,执行旋转操作恢复平衡。
- 删除操作:删除节点,并且在删除节点后,可能需要调整树的结构以保持平衡。由于AVL树的删除操作涉及复杂的旋转和调整,因此在本文中我们不会详细讲解该操作。如果你希望深入了解,可以参考《算法导论》中的相关章节来获取详细内容。
- 查找操作:通过比较节点值来查找特定的元素。
- 旋转操作:当树失衡时,使用旋转来恢复平衡,常见的旋转有左旋、右旋、左右旋和右左旋。
二、AVL树的节点结构
在实现AVL树之前,首先要定义节点结构。每个节点存储以下信息:
- 键值对(
_kv
):存储数据(pair<K, V>
)。 - 左右子树指针(
_left
,_right
):指向左右子树。 - 父节点指针(
_parent
):指向父节点。 - 平衡因子(
_bf
):用于标识该节点的左右子树的高度差。
2.1 节点结构的定义:
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){}
};
三、插入操作
3.1 插入操作概述
AVL树的插入操作包括三步:
- 按二叉查找树规则插入节点;
- 更新每个节点的平衡因子;
- 如果需要,进行旋转操作来恢复树的平衡。
bool Insert(const pair<K, V>& kv);
3.2 步骤1:按二叉查找树规则插入节点
在插入节点时,我们根据值的大小,递归地找到插入位置,并在该位置插入新节点:
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;
}
//更新插入节点的_parent
cur->_parent = parent;
3.3 步骤2:更新平衡因子
插入节点后,我们从新插入的节点开始,逐步更新路径上每个节点的平衡因子。插入时会对父节点的平衡因子进行调整:
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 = parent;parent = cur->_parent;}else if (parent->_bf == 2 || parent->_bf == -2){//旋转if (parent->_bf == 2 && cur->_bf == 1){RotateR(parent);}else if (parent->_bf == -2 && cur->_bf == -1){RotateL(parent);}else if (parent->_bf == 2 && cur->_bf == -1){RotateLR(parent);}else if (parent->_bf == -2 && cur->_bf == 1){RotateRL(parent);}elseassert(false);break;}else{assert(false);}
}
return true;
3.4 步骤3:旋转操作详解
如果某个节点的平衡因子为2或-2,表示该节点不平衡。我们需要通过旋转操作来恢复平衡。旋转操作有四种:
- 右单旋(RotateR)
- 左单旋(RotateL)
- 左右双旋(RotateLR)
- 右左双旋(RotateRL)
3.4.1 右单旋
右单旋适用于当左子树较高时,执行旋转操作来恢复平衡。
void RotateR(Node* parent) {Node* subL = parent->_left;Node* subLR = subL->_right;Node* ppNode = parent->_parent;parent->_left = subLR;if (subLR) {subLR->_parent = parent;}subL->_right = parent;parent->_parent = subL;if (parent == _root) {subL->_parent = nullptr;_root = subL;} else {subL->_parent = ppNode;if (parent == ppNode->_right)ppNode->_right = subL;elseppNode->_left = subL;}parent->_bf = 0;subL->_bf = 0;
}
3.4.2 左单旋
左单旋适用于当右子树较高时,执行旋转操作来恢复平衡。
void RotateL(Node* parent) {Node* subR = parent->_right;Node* ppNode = parent->_parent;Node* subRL = subR->_left;parent->_right = subRL;parent->_parent = subR;if (subRL) {subRL->_parent = parent;}subR->_left = parent;if (parent == _root) {_root = subR;subR->_parent = nullptr;} else {subR->_parent = ppNode;if (parent == ppNode->_right)ppNode->_right = subR;elseppNode->_left = subR;}parent->_bf = 0;subR->_bf = 0;
}
3.4.3 左右双旋
当左子树的右子树较高时,首先进行左旋,再进行右旋,恢复平衡。
void RotateLR(Node* parent) {Node* subL = parent->_left;Node* subLR = subL->_right;int bf = subLR->_bf;RotateL(subL); // 左旋RotateR(parent); // 右旋if (bf == 1) {subLR->_bf = 0;subL->_bf = 0;parent->_bf = -1;} else if (bf == -1) {subLR->_bf = 0;subL->_bf = 1;parent->_bf = 0;} else if (bf == 0) {subL->_bf = subLR->_bf = parent->_bf = 0;}
}
3.4.4 右左双旋
当右子树的左子树较高时,首先进行右旋,再进行左旋,恢复平衡。
void RotateRL(Node* parent) {Node* subR = parent->_right;Node* subRL = subR->_left;int bf = subRL->_bf;RotateR(subR); // 右旋RotateL(parent); // 左旋if (bf == 0) {parent->_bf = subR->_bf = subRL->_bf = 0;} else if (bf == 1) {subRL->_bf = 0;parent->_bf = 0;subR->_bf = -1;} else if (bf == -1) {subRL->_bf = 0;parent->_bf = 1;subR->_bf = 0;}
}
总结:找失衡原因,决定如何旋转
- 左子树的左子树高:进行R
- 右子树的右子树高:进行L
- 左子树的右子树高:进行LR
- 右子树的左子树高:进行RL
四、其他操作
4.1 查找操作 (Find
)
查找操作与普通的二叉查找树类似。通过不断比较当前节点的键值和目标值,沿树的路径查找目标节点。
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; // 目标键值比当前节点小,往左子树查找elsereturn cur; // 找到目标节点,返回该节点}return nullptr; // 如果没有找到,返回空
}
该方法的时间复杂度为 O(log N),因为AVL树是平衡的,树的高度是对数级别的。
4.2 计算树的高度 (Height
)
树的高度指的是从根节点到最远叶子节点的最长路径上的边数。AVL树的高度是平衡的,计算时我们需要递归地计算左右子树的高度并返回较大的一个。
int Height() {return _Height(_root); // 从根节点开始计算树的高度
}int _Height(Node* root) {if (root == nullptr)return 0; // 空树的高度为0int leftHeight = _Height(root->_left); // 递归计算左子树的高度int rightHeight = _Height(root->_right); // 递归计算右子树的高度return leftHeight > rightHeight ? leftHeight + 1 : rightHeight + 1; // 返回较大值,并加1
}
该方法的时间复杂度为 O(N),其中N为节点总数。虽然AVL树是平衡的,但在计算高度时需要遍历整个树。
4.3 计算节点数量 (Size
)
节点数量是树中所有节点的总数。通过递归遍历树,计算左右子树的节点数量,并加上当前节点。
int Size() {return _Size(_root); // 从根节点开始计算树的大小
}int _Size(Node* root) {if (root == nullptr)return 0; // 空树节点数量为0return _Size(root->_left) + _Size(root->_right) + 1; // 左右子树的节点数量加1
}
该方法的时间复杂度为 O(N),需要遍历整个树来计算节点总数。
4.4 树是否平衡 (IsBalanceTree
)
为了验证AVL树的平衡性,我们需要检查每个节点的平衡因子,并确保它们的绝对值不超过1。如果树的任意节点的平衡因子大于1或小于-1,那么树就不平衡。
bool _IsBalanceTree(Node* root) {if (root == nullptr)return true; // 空树是平衡的int leftHeight = _Height(root->_left); // 左子树高度int rightHeight = _Height(root->_right); // 右子树高度int diff = leftHeight - rightHeight; // 计算平衡因子if (abs(diff) >= 2) {cout << root->_kv.first << "高度差异常" << endl;return false; // 如果高度差大于等于2,树不平衡}if (root->_bf != diff) {cout << root->_kv.first << "平衡因子异常" << endl;return false; // 如果平衡因子与实际高度差不符,树不平衡}// 递归检查左右子树是否平衡return _IsBalanceTree(root->_left) && _IsBalanceTree(root->_right);
}
该方法的时间复杂度为 O(N),需要递归遍历整个树并计算每个节点的平衡因子。
五、性能分析与测试
5.1 性能分析
AVL树通过保持平衡,保证了插入、查找和删除操作的时间复杂度都为O(logN),相比于普通的二叉查找树,AVL树避免了退化成链表的情况,极大提高了性能。
5.2 测试代码
下面是两个测试代码,用于验证AVL树的插入和查找操作。
// 测试代码
void TestAVLTree1()
{AVLTree<int, int> t;// 常规的测试用例//int a[] = { 16, 3, 7, 11, 9, 26, 18, 14, 15 };// 特殊的带有双旋场景的测试用例int a[] = { 4, 2, 6, 1, 3, 5, 15, 7, 16, 14 };for (auto e : a){t.Insert({ e, e });cout << "Insert:" << e << "->";cout << t.IsBalanceTree() << endl;}t.InOrder();cout << t.IsBalanceTree() << endl;
}// 插入一堆随机值,测试平衡,顺便测试一下高度和性能等
void TestAVLTree2()
{const int N = 1000000;vector<int> v;v.reserve(N);srand((unsigned int)time(0));for (int i = 0; i < N; i++){v.push_back(rand() + i);}size_t begin2 = clock();AVLTree<int, int> t;for (auto e : v){t.Insert(make_pair(e, e));}size_t end2 = clock();cout << t.IsBalanceTree() << endl;cout << "Insert:" << end2 - begin2 << endl;cout << "Height:" << t.Height() << endl;cout << "Size:" << t.Size() << endl;size_t begin1 = clock();// 确定在的值/*for (auto e : v){t.Find(e);}*/// 随机值for (int i = 0; i < N; i++){t.Find((rand() + i));}size_t end1 = clock();cout << "Find:" << end1 - begin1 << endl;
}
六、全部代码
AVLTree.h
#pragma oncetemplate<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: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 = parent;parent = cur->_parent;}else if (parent->_bf == 2 || parent->_bf == -2){//旋转if (parent->_bf == 2 && cur->_bf == 1){RotateR(parent);}else if (parent->_bf == -2 && cur->_bf == -1){RotateL(parent);}else if (parent->_bf == 2 && cur->_bf == -1){RotateLR(parent);}else if (parent->_bf == -2 && cur->_bf == 1){RotateRL(parent);}elseassert(false);break;}else{assert(false);}}return true;}void InOrder(){_InOrder(_root);}bool IsBalanceTree(){return _IsBalanceTree(_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;elsereturn cur;}return nullptr;}int Height(){return _Height(_root);}int Size(){return _Size(_root);}private:int _Size(Node* root){if (root == nullptr)return 0;return _Size(root->_left) + _Size(root->_right) + 1;}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 diff = leftHeight - rightHeight;if (abs(diff) >= 2){cout << root->_kv.first << "高度差异常" << endl;return false;}if (root->_bf != diff) {cout << root->_kv.first << "平衡因子异常" << endl;return false;}return _IsBalanceTree(root->_left) && _IsBalanceTree(root->_right);}void _InOrder(Node* root){if (root == nullptr){return;}_InOrder(root->_left);cout << root->_kv.first << ":" << root->_kv.second << " ";_InOrder(root->_right);}void RotateR(Node* parent){Node* subL = parent->_left;Node* subLR = subL->_right;Node* ppNode = parent->_parent;parent->_left = subLR;if (subLR){subLR->_parent = parent;}subL->_right = parent;parent->_parent = subL;if (parent == _root){subL->_parent = nullptr;_root = subL;}else{subL->_parent = ppNode;if (parent == ppNode->_right)ppNode->_right = subL;elseppNode->_left = subL;}parent->_bf = 0;subL->_bf = 0;}void RotateL(Node* parent){Node* subR = parent->_right;Node* ppNode = parent->_parent;Node* subRL = subR->_left;parent->_right = subRL;parent->_parent = subR;if (subRL){subRL->_parent = parent;}subR->_left = parent;if (parent == _root){_root = subR;subR->_parent = nullptr;}else{subR->_parent = ppNode;if (parent == ppNode->_right)ppNode->_right = subR;elseppNode->_left = subR;}parent->_bf = 0;subR->_bf = 0;}void RotateLR(Node* parent){Node* subL = parent->_left;Node* subLR = subL->_right;int bf = subLR->_bf;RotateL(subL);RotateR(parent);if (bf == 1){subLR->_bf = 0;subL->_bf = 0;parent->_bf = -1;}else if (bf == -1){subLR->_bf = 0;subL->_bf = 1;parent->_bf = 0;}else if (bf == 0){subL->_bf = subLR->_bf = parent->_bf = 0;}else{assert(false);}}void RotateRL(Node* parent){Node* subR = parent->_right;Node* subRL = subR->_left;int bf = subRL->_bf;RotateR(subR);RotateL(parent);if (bf == 0){parent->_bf = subR->_bf = subRL->_bf = 0;}else if (bf == 1){subRL->_bf = 0;parent->_bf = 0;subR->_bf = -1;}else if (bf == -1){subRL->_bf = 0;parent->_bf = 1;subR->_bf = 0;}else{assert(false);}}Node* _root = nullptr;
};// 测试代码
void TestAVLTree1()
{AVLTree<int, int> t;// 常规的测试用例//int a[] = { 16, 3, 7, 11, 9, 26, 18, 14, 15 };// 特殊的带有双旋场景的测试用例int a[] = { 4, 2, 6, 1, 3, 5, 15, 7, 16, 14 };for (auto e : a){/*if (e == 14){int x = 0;}*/t.Insert({ e, e });cout << "Insert:" << e << "->";cout << t.IsBalanceTree() << endl;}t.InOrder();cout << t.IsBalanceTree() << endl;
}// 插入一堆随机值,测试平衡,顺便测试一下高度和性能等
void TestAVLTree2()
{const int N = 1000000;vector<int> v;v.reserve(N);srand((unsigned int)time(0));for (int i = 0; i < N; i++){v.push_back(rand() + i);}size_t begin2 = clock();AVLTree<int, int> t;for (auto e : v){t.Insert(make_pair(e, e));}size_t end2 = clock();cout << t.IsBalanceTree() << endl;cout << "Insert:" << end2 - begin2 << endl;cout << "Height:" << t.Height() << endl;cout << "Size:" << t.Size() << endl;size_t begin1 = clock();// 确定在的值/*for (auto e : v){t.Find(e);}*/// 随机值for (int i = 0; i < N; i++){t.Find((rand() + i));}size_t end1 = clock();cout << "Find:" << end1 - begin1 << endl;
}
Test.cpp
#define _CRT_SECURE_NO_WARNINGS 1#include<iostream>
#include<assert.h>
#include<vector>
using namespace std;
#include"AVLTree.h"int main()
{TestAVLTree1();TestAVLTree2();return 0;
}
七、总结与展望
随着数据量的不断增长,如何保持高效的查找与插入操作成为了现代计算机科学的核心问题之一。AVL树作为一种自平衡二叉查找树,凭借其稳定的时间复杂度和高效的性能,已广泛应用于各类需要动态数据结构支持的场景。未来,随着算法和硬件的进一步发展,AVL树的实现和优化将迎来更多挑战与机遇。我们期待看到更高效的树形结构和操作算法,不断推动计算机科学的前沿发展,同时也期待你在实现这些算法时能够继续探索与创新,为我们带来更多启发和思考。
以上就是关于【C++篇】树影摇曳,旋转无声:探寻AVL树的平衡之道的内容啦,各位大佬有什么问题欢迎在评论区指正,或者私信我也是可以的啦,您的支持是我创作的最大动力!❤️
相关文章:
【C++篇】树影摇曳,旋转无声:探寻AVL树的平衡之道
文章目录 从结构到操作:手撕AVL树的实现一、AVL树介绍1.1 什么是AVL树1.2 平衡因子的定义1.3 平衡的意义1.4 AVL树的操作 二、AVL树的节点结构2.1 节点结构的定义: 三、插入操作3.1 插入操作概述3.2 步骤1:按二叉查找树规则插入节点3.3 步骤2…...
CPU、SOC、MPU、MCU--详细分析四者的区别
一、CPU 与SOC的区别 1.CPU 对于电脑,我们经常提到,处理器,内存,显卡,硬盘四大部分可以组成一个基本的电脑。其中的处理器——Central Processing Unit(中央处理器)。CPU是一台计算机的运算核…...
nacos编写瀚高数据库插件
1、下载nacos源码 git clone gitgithub.com:alibaba/nacos.git 2、引入瀚高驱动 <dependency><groupId>com.highgo</groupId><artifactId>jdbc</artifactId><version>${highgo.version}</version></dependency> 3、DataSource…...
使用excel中的VBA合并多个excel文件
需求是这样的: 在Windows下,用excel文件让多个小组填写了统计信息,现在我需要把收集的多个文件汇总到一个文件中,前三行为标题可以忽略,第四行为收集信息的列名,处理每一行数据的时候,发现某一行…...
linux 安装启动zookeeper全过程及遇到的坑
1、下载安装zookeeper 参考文章:https://blog.csdn.net/weixin_48887095/article/details/132397448 2、启动失败 1、启动失败JAVA_HOME is not set and java could not be found in PATH 已安装 JAVA 配置了JAVA_HOME,还是报错解决方法:参考…...
JAVA JUC 并发编程学习笔记(一)
文章目录 JUC进程概述对比 线程创建线程ThreadRunnableCallable 线程方法APIrun startsleep yieldjoininterrupt打断线程打断 park终止模式 daemon不推荐 线程原理运行机制线程调度未来优化 线程状态查看线程 同步临界区syn-ed使用锁同步块同步方法线程八锁 锁原理Monitor字节码…...
内容中台架构下智能推荐系统的算法优化与分发策略
内容概要 在数字化内容生态中,智能推荐系统作为内容中台的核心引擎,承担着用户需求与内容资源精准匹配的关键任务。其算法架构的优化路径围绕动态特征建模与多模态数据融合展开,通过深度强化学习技术实现用户行为特征的实时捕捉与动态更新&a…...
Java 内存区域详解
1 常见面试题 1.1 基本问题 介绍下Java内存区域(运行时数据区)Java对象的创建过程(五步,建议能够默写出来并且要知道每一步虚拟机做了什么)对象的访问定位的两种方式(句柄和直接指针两种方式)…...
jEasyUI 创建学校课程表
jEasyUI 创建学校课程表 引言 随着信息技术的飞速发展,教育行业也迎来了数字化转型的浪潮。学校课程表的创建和管理作为教育信息化的重要组成部分,其效率和准确性直接影响到学校的教学秩序。jEasyUI,作为一款优秀的开源UI框架,凭借其易用性、灵活性和丰富的组件,成为了许…...
利用 OpenCV 进行棋盘检测与透视变换
利用 OpenCV 进行棋盘检测与透视变换 1. 引言 在计算机视觉领域,棋盘检测与透视变换是一个常见的任务,广泛应用于 摄像机标定、文档扫描、增强现实(AR) 等场景。本篇文章将详细介绍如何使用 OpenCV 进行 棋盘检测,并…...
git-提交时间和作者时间的区别
1.介绍 定义介绍 提交时间(Committer Date):决定了提交在 Git 历史中的位置,通常影响 GitHub 上提交显示的顺序。 作者时间(Author Date):虽然不影响提交的排序,但在每个提交详情页…...
解决双系统开机显示gnu grub version 2.06 Minimal BASH Like Line Editing is Supported
找了好多教程都没有用,终于解决了!!我是因为ubuntu分区的时候出问题了 问题描述: 双系统装好,隔天开机找不到引导项,黑屏显示下列 因为我用的D盘划分出来的部分空闲空间,而不是全部,…...
基于Flask的京东商品信息可视化分析系统的设计与实现
【Flask】基于Flask的京东商品信息可视化分析系统的设计与实现(完整系统源码开发笔记详细部署教程)✅ 目录 一、项目简介二、项目界面展示三、项目视频展示 一、项目简介 系统能够灵活地执行SQL查询,提取出用于分析的关键数据指标。为了将这…...
期权帮|股指期货中的套期保值如何操作?
锦鲤三三每日分享期权知识,帮助期权新手及时有效地掌握即市趋势与新资讯! 股指期货中的套期保值如何操作? 一、股指期货中的套期保值准备阶段 确定套保需求,投资者依据市场预判与投资组合分析,决定是否套保。 &…...
用Chrome Recorder轻松完成自动化测试脚本录制
前言 入门自动化测试,录制回放通常是小白测试首先用到的功能。而录制回放工具也一直是各大Web自动化测试必然会着重提供的一块功能。 早期WinRunner、QTP这样的工具,自动化测试可以说是围绕录制回放开展的。近年像Selenium也提供有录制工具 Selenium IDE,Playwright也包含…...
C/C++面试知识点总结
目录 1. 指针1.1 智能指针1.2 指针和引用的区别1.3 数组和指针的区别1.4 数组指针和指针数组的区别1.5 迭代器和指针的区别1.6 strcpy 和 memcpy 的区别 2. 内存管理与分配2.1 内存分配与存储区2.2 malloc / free2.3 volatile和extern的区别2.4 拷贝构造函数2.5 预处理、编译、…...
springboot三层架构详细讲解
目录 springBoot三层架构 0.简介1.各层架构 1.1 Controller层1.2 Service层1.3 ServiceImpl1.4 Mapper1.5 Entity1.6 Mapper.xml 2.各层之间的联系 2.1 Controller 与 Service2.2 Service 与 ServiceImpl2.3 Service 与 Mapper2.4 Mapper 与 Mapper.xml2.5 Service 与 Entity2…...
助力DeepSeek私有化部署服务:让企业AI落地更简单、更安全
在数字化转型的浪潮中,越来越多的企业选择私有化部署AI技术,以保障数据安全、提升业务效率并实现自主可控。DeepSeek作为行业领先的AI开源技术,其技术可以支持企业私有化部署,企业需要一站式服务私有化部署,涵盖硬件采…...
Mac book Air M2 用VMware安装 Ubuntu22.04
安装 VMware Fusion 下载 Ubuntu 安装VMware 完成之后运行新建 将对应Ubuntu 版本拖拽 如图 选择第一个回车 选绿色 回车 为空 相关命令行 sudo apt install net-tools sudo apt install ubuntu-desktop sudo reboot 常用命令行 uname uname -a clear ll ifconfig (查…...
Spring Boot接收参数的19种方式
Spring Boot是一个强大的框架,允许开发人员通过多种方式接收和处理参数。无论是HTTP请求参数、路径变量,还是请求体中的数据,Spring Boot都能提供灵活的处理方式。本文将介绍19种不同的方式来接收参数。 1. 查询参数(Query Param…...
Linux firewalld 常用命令
本文参考RedHat官网文章How to configure a firewall on Linux with firewalld。 Firewalld 是守护进程名,对应命令为firewall-cmd。帮助详见以下命令: $ firewall-cmd --helpUsage: firewall-cmd [OPTIONS...]General Options-h, --help Pr…...
火语言RPA--Excel插入空行
【组件功能】:在Excel内指定的位置插入空行 配置预览 配置说明 在第n行之前 支持T或# 填写添加插入第n行之前行号。 插入n行 支持T或# 插入多少行。 Sheet页名称 支持T或# Excel表格工作簿名称。 示例 Excel插入空行 描述 在第3行之后插入3行。 配置 输…...
纷析云开源版- Vue2-增加字典存储到localStorage
main.js //保存字典数据到LocalStorage Vue.prototype.$api.setting.SystemDictType.all().then(({data}) > {loadDictsToLocalStorage(data) })新增 dictionary.js 放在 Utils文件夹里面 // 获取字典数据 export function getDictByType(dictType) {const dicts JSON.par…...
LangChain-基础(prompts、序列化、流式输出、自定义输出)
LangChain-基础 我们现在使用的大模型训练数据都是基于历史数据训练出来的,它们都无法处理一些实时性的问题或者一些在训练时为训练到的一些问题,解决这个问题有2种解决方案 基于现有的大模型上进行微调,使得它能适应这些问题(本…...
机器学习在脑卒中预测中的应用:不平衡数据集处理方法详解
机器学习在脑卒中预测中的应用:不平衡数据集处理方法详解 目录 引言 脑卒中的全球影响机器学习在医疗预测中的挑战类别不平衡问题的核心痛点数据预处理与特征选择 数据来源与清洗缺失值处理方法类别特征编码特征选择技术处理类别不平衡的四大方法 SMOTE(合成少数类过采样技术…...
Spring Boot项目@Cacheable注解的使用
Cacheable 是 Spring 框架中用于缓存的注解之一,它可以帮助你轻松地将方法的结果缓存起来,从而提高应用的性能。下面详细介绍如何使用 Cacheable 注解以及相关的配置和注意事项。 1. 基本用法 1.1 添加依赖 首先,确保你的项目中包含了 Spr…...
飞书API
extend目录下,API <?php // ---------------------------------------------------------------------- // | 飞书API // ---------------------------------------------------------------------- // | COPYRIGHT (C) 2021 http://www.jeoshi.com All rights reserved. …...
杨校老师课堂之信息学奥赛结构体操作使用经典题集锦汇总
C基础:结构体数组综合训练 员工信息处理系统题目描述输入描述输出描述解题思路参考代码 员工信息处理系统 题目描述 在一家企业中,员工信息的准确性和时效性是日常人事管理工作的关键。由于企业员工数量众多,手动统计与更新员工信息不仅耗费大量时间&a…...
交互编程工具之——Jupyter
Jupyter 是什么? Jupyter 是一个开源的交互式编程和数据分析工具,广泛应用于数据科学、机器学习、教育和研究领域。其核心是 Jupyter Notebook(现升级为 JupyterLab),允许用户在一个基于浏览器的界面中编写代码、运行…...
Redis常见问题排查
redis连接不上去,ERR max number of clients reached redis默认最大连接是10000,如果出现连接泄露或者被服务器被攻击可能会出现连接数超过限制。 Redis 的 INFO 命令可以提供服务器的统计信息,其中包括当前客户端连接数。这是获取连接数最…...
Hadoop初体验
一、HDFS初体验 1. shell命令操作 hadoop fs -mkdir /itcast hadoop fs -put zookeeper.out /itcast hadoop fs -ls / 2. Web UI页面操作 结论: HDFS本质就是一个文件系统有目录树结构 和Linux类似,分文件、文件夹为什么上传一个小文件也这…...
深入解析C++26 Execution Domain:设计原理与实战应用
一、Domain设计目标与核心价值 Domain是C26执行模型的策略载体,其核心解决两个问题: 执行策略泛化:将线程池、CUDA流等异构调度逻辑抽象为统一接口策略组合安全:通过类型隔离避免不同执行域的策略污染 // Domain类型定义示例&a…...
基于Flask的租房信息可视化系统的设计与实现
【Flask】基于Flask的租房信息可视化系统的设计与实现(完整系统源码开发笔记详细部署教程)✅ 目录 一、项目简介二、项目界面展示三、项目视频展示 一、项目简介 随着互联网的快速发展,租房市场日益繁荣,信息量急剧增加ÿ…...
TensorFlow v2.16 Overview
TensorFlow v2.16 Overview 一、模块 Modules二、类 Classes三、函数 Functions TensorFlow v2.16.1 Overview 一、模块 Modules 模块是TensorFlow中组织代码的一种方式,将相关的功能和类封装在一起,方便用户使用和管理。每个模块都提供了特定领域的公共…...
网页版的俄罗斯方块
1、新建一个txt文件 2、打开后将代码复制进去保存 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>俄…...
Vue3 状态管理 - Pinia
目录 1. 什么是Pinia 2. 手动添加Pinia到Vue项目 3. Pinia的基础使用 4. getters实现 5. action异步实现 6. storeToRefs工具函数 7. Pinia的调试 8. Pinia的持久化插件 1. 什么是Pinia Pinia 是 Vue 专属的最新状态管理库 ,是 Vuex 状态管理工具的替代品 …...
Arduino 第十六章:pir红外人体传感器练习
Arduino 第十六章:PIR 传感器练习 一、引言 在 Arduino 的众多有趣项目中,传感器的应用是非常重要的一部分。今天我们要学习的主角是 PIR(被动红外)传感器。PIR 传感器能够检测人体发出的红外线,常用于安防系统、自动…...
伯克利 CS61A 课堂笔记 10 —— Trees
本系列为加州伯克利大学著名 Python 基础课程 CS61A 的课堂笔记整理,全英文内容,文末附词汇解释。 目录 01 Trees 树 Ⅰ Tree Abstraction Ⅱ Implementing the Tree Abstraction 02 Tree Processing 建树过程 Ⅰ Fibonacci tree Ⅱ Tree Process…...
Springboot 高频面试题
以下是Spring Boot的高频面试题及答案和底层原理解释: 基础概念 什么是Spring Boot,其主要特点是什么? 答案: Spring Boot本质上是一个建立在Spring框架之上的快速应用开发框架。其主要特点包括: 启动器:一…...
从零开始玩转TensorFlow:小明的机器学习故事 2
你好,TensorFlow!——从零开始的第一个机器学习程序 1. 为什么要写这个“Hello, TensorFlow!”? 无论学习什么新语言或新框架,“Hello World!”示例都能帮助我们快速确认开发环境是否就绪,并掌握最基本的使用方式。对…...
第四届图像、信号处理与模式识别国际学术会议(ISPP 2025)
重要信息 会议官网:www.icispp.com 会议时间:2025年3月28-30日 会议地点:南京 简介 由河海大学和江苏大学联合主办的第四届图像、信号处理与模式识别国际学术会议(ISPP 2025) 将于2025年3月28日-30日在中国南京举行。会议主…...
阿里云通过docker安装skywalking及elasticsearch操作流程
系统 本文使用系统为 Alibaba Cloud Linux 3.2104 LTS 64位 配置为 4核8G PS:最低配置应为2核4G,配置过低无法启动 安装docker 1.卸载旧版本docker yum remove docker \docker-client \docker-client-latest \docker-common \docker-latest \docker-…...
Linux·spin_lock的使用
自旋锁 内核当发生访问资源冲突的时候,可以有两种锁的解决方案选择: 一个是原地等待一个是挂起当前进程,调度其他进程执行(睡眠) Spinlock 是内核中提供的一种比较常见的锁机制,自旋锁是“原地等待”的方…...
企业内部真题
文章目录 前端面试题:一个是铺平的数组改成树的结构问题一解析一问题一解析二前端面试题:for循环100个接口,每次只调3个方法一:使用 `async/await` 和 `Promise`代码解释(1):代码解释(2):1. `fetchApi` 函数2. `concurrentFetch` 函数3. 生成 100 个接口地址4. 每次并…...
MySQL基本操作——包含增删查改(环境为Ubuntu20.04,MySQL5.7.42)
1.库的操作 1.1 创建数据库 语法: 说明: 大写的表示关键字 [] 是可选项 CHARACTER SET: 指定数据库采用的字符集 COLLATE: 指定数据库字符集的校验规则 1.2 创建案例 创建一个使用utf8字符集的db1数据库 create database db1 charsetutf8; …...
程序代码篇---Python指明函数参数类型
文章目录 前言简介一、函数参数的类型指定1. 基本类型提示2. 默认参数3. 可变参数4. 联合类型(Union)5. 可选类型(Optional)6. 复杂类型 二、返回值的类型指定1. 基本返回类型2. 无返回值(None)3. 返回多个…...
AIGC视频扩散模型新星:SVD——稳定扩散的Video模型
大家好,这里是好评笔记,公主号:Goodnote,专栏文章私信限时Free。本文详细介绍慕尼黑大学携手 NVIDIA 等共同推出视频生成模型 Video LDMs。NVIDIA 在 AI 领域的卓越成就家喻户晓,而慕尼黑大学同样不容小觑,…...
MySql面试宝典【刷题系列】
文章目录 一、Mysql 的存储引擎 myisam 和 innodb 的区别。二、MySQL数据库作发布系统的存储,一天五万条以上的增量,预计运维三年,怎么优化?三、对于大流量的网站,您采用什么样的方法来解决各页面访问量统计问题?四、锁的优化策略…...
银河麒麟系统安装mysql5.7【亲测可行】
一、安装环境 cpu:I5-10代; 主板:华硕; OS:银河麒麟V10(SP1)未激活 架构:Linux 5.10.0-9-generic x86_64 GNU/Linux mysql版本:mysql-5.7.34-linux-glibc2.12-x86_64.ta…...
CTF-内核pwn入门1: linux内核模块基础原理
本文由A5rZ在2025-2-18-21:00编写 1.可加载内核模块是什么? 内核可加载模块(*.ko 文件)是内核的一种扩展机制,可以在不重启系统的情况下加载和卸载代码。它们允许动态地向内核添加新的功能或支持。 以下是一些内核模块常见的功能&…...