当前位置: 首页 > news >正文

AVL树 ---(C++)

        本篇讲全面的讲解 AVL 树的插入,旋转以及验证 AVL 树的性能(本篇未实现删除代码)。至于为什么会有 AVL 树,这是因为简单的二叉搜索树并不能直接的保证搜索的效率,因为当我们在二叉搜索树中插入一段有序的序列的时候,二叉搜索树就会退化为单枝树,这个时候进行搜索的时候,时间复杂度就变为了 O(n^2),如下:

        但是通过 AVL 树的旋转就可以很好的解决这个问题,使树近似等于完全二叉树或者满二叉树。

AVL 树代码

        先给出代码,接着在下文中给出解释:

#pragma once
#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 _balanceFactor;AVLTreeNode(const pair<K, V>& kv): _left(nullptr), _right(nullptr), _parent(nullptr), _kv(kv), _balanceFactor(0){}
};template <class K, class V>
class AVLTree {
public:typedef AVLTreeNode<K, V> Node;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;}// 插入删除查找遍历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;elsereturn false;}// cur == nullptrcur = new Node(kv);//if (parent->_left == cur)//	parent->_left = cur;//else//	parent->_right = cur;if (parent->_kv.first > kv.first)parent->_left = cur;elseparent->_right = cur;cur->_parent = parent;// 需要更新平衡因子// 如果是在父亲的左边,父亲的平衡因子减一、右边加一if (parent->_left == cur)parent->_balanceFactor--;elseparent->_balanceFactor++;// 查看爷爷结点是否需要更新while (parent) {if (parent->_balanceFactor == 0) {break;}else if (parent->_balanceFactor == 1 || parent->_balanceFactor == -1) {if (parent == _root)break;// 现在的parent就不可能等于nullparent = parent->_parent;cur = cur->_parent;if (parent->_left == cur)parent->_balanceFactor--;elseparent->_balanceFactor++;}else if(parent->_balanceFactor == 2 || parent->_balanceFactor == -2) {if (parent->_balanceFactor == 2 && cur->_balanceFactor == 1)RotateLeft(parent);else if (parent->_balanceFactor == -2 && cur->_balanceFactor == -1)RotateRight(parent);else if (parent->_balanceFactor == -2 && cur->_balanceFactor == 1)RotateLeftRight(parent);else if (parent->_balanceFactor == 2 && cur->_balanceFactor == -1)RotateRightLeft(parent);elseassert(false);break;}else {assert(false);}}return true;}void RotateRight(Node* parent) {Node* subL = parent->_left;Node* subLR = subL->_right;// 将左孩子的右节点链接到原父亲结点if (subLR) subLR->_parent = parent;parent->_left = subLR;Node* ppNode = parent->_parent;// 将左孩子变为原父亲结点的父亲subL->_right = parent;parent->_parent = subL;// 将爷爷结点重新链接if (ppNode == nullptr) {_root = subL;_root->_parent = nullptr;}else {if (ppNode->_left == parent)ppNode->_left = subL;elseppNode->_right = subL;subL->_parent = ppNode;}subL->_balanceFactor = parent->_balanceFactor = 0;}void RotateLeft(Node* parent) {Node* subR = parent->_right;Node* subRL = subR->_left;Node* ppNode = parent->_parent;parent->_right = subRL;if (subRL) subRL->_parent = parent;subR->_left = parent;parent->_parent = subR;if (ppNode == nullptr) {_root = subR;_root->_parent = nullptr;}else {if (ppNode->_left == parent)ppNode->_left = subR;elseppNode->_right = subR;subR->_parent = ppNode;}subR->_balanceFactor = parent->_balanceFactor = 0;}void RotateRightLeft(Node* parent) {Node* subR = parent->_right;Node* subRL = subR->_left;int balanceFactor = subRL->_balanceFactor;RotateRight(subR);RotateLeft(parent);// 更新平衡因子subRL->_balanceFactor = 0;if (balanceFactor == -1) {parent->_balanceFactor = 0;subR->_balanceFactor = 1;}else if (balanceFactor == 1) {parent->_balanceFactor = -1;subR->_balanceFactor = 0;}else if (balanceFactor == 0) {parent->_balanceFactor = 0;subR->_balanceFactor = 0;}else {assert(false);}}void RotateLeftRight(Node* parent) {Node* subL = parent->_left;Node* subLR = subL->_right;int balanceFactor = subLR->_balanceFactor;// 先左旋后右旋RotateLeft(subL);RotateRight(parent);subLR->_balanceFactor = 0;if (balanceFactor == -1) {subL->_balanceFactor = 0;parent->_balanceFactor = 1;}else if (balanceFactor == 1) {parent->_balanceFactor = 0;subL->_balanceFactor = -1;}else if (balanceFactor == 0) {parent->_balanceFactor = 0;subL->_balanceFactor = 0;}else {assert(false);}}void InOrder() {_InOrder(_root);cout << endl;}int height() {int h = _height(_root);return h;}int size() {int s = _size(_root);return s;}bool IsBalance() {return _IsBalance(_root);}private:bool _IsBalance(Node* root) {if (root == nullptr)return true;int leftHeight = _height(root->_left);int rightHeight = _height(root->_right);if (abs(leftHeight - rightHeight) >= 2)return false;if (abs(root->_balanceFactor) >= 2)return false;return _IsBalance(root->_left) && _IsBalance(root->_right);}int _height(Node* root) {if (root == nullptr)return 0;int left = _height(root->_left);int right = _height(root->_right);int height = max(left, right);return height + 1;}int _size(Node* root) {if (root == nullptr)return 0;return _size(root->_left) + _size(root->_right) + 1;}void _InOrder(Node* root) {if (root == nullptr) return;_InOrder(root->_left);cout << root->_kv.first << " " << root->_kv.second << endl;_InOrder(root->_right);}
private:Node* _root = nullptr;
};

AVL 树的概念于抽象数据结构

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

        1. 它的左右子树都是 AVL 树

        2. 左右子树的高度之差(平衡因子)的绝对值不超过 1

        左右子树的高度差不超过 1,可以降低树的高度,减少平均搜索长度。如下:

        关于 AVL 树的抽象数据结构,我们首先需要抽象出 AVL 树节点的数据结构,在 AVL 树中,我们存储的关键数据为键值对 pair,AVL 树节点中的平衡因子。然后需要一个指向左子树的指针,指向右子树的指针同时还需要一个指向父节点的指针,可以让我们便于更新每个节点的平衡因子。如下:

template <class K, class V>
struct AVLTreeNode {AVLTreeNode<K, V>* _left;AVLTreeNode<K, V>* _right;AVLTreeNode<K, V>* _parent;pair<K, V> _kv;int _balanceFactor;AVLTreeNode(const pair<K, V>& kv):_left(nullptr), _right(nullptr), _parent(nullptr), _kv(kv), _balanceFactor(0){}
};

AVL 树的插入

        关于 AVL 树而言,只是在二叉搜索树的基础上引入了平衡因子,所以 AVL 树也可以看出二叉搜索树(左右高度差不大于1的二叉搜索树),所以对于 AVL 树的插入,可以分为以下两步:

        1. 按照二叉搜索树的方式插入新节点

        2. 调整节点的平衡因子。

        所以我们插入节点,只需要找到应该插入的位置,然后插入即可,寻找插入位置按照:键值小于当前节点,向左子树搜索,键值大于当前节点,向右子树搜索的原则,直到找到空节点为止,就是应该插入的位置。寻找的时候,还需要记录下每一次搜索的父节点,便于链接指针,如下:

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;elsereturn false;}// cur == nullptrcur = new Node(kv);// 链接孩子节点和父节点if (parent->_kv.first > kv.first)parent->_left = cur;elseparent->_right = cur;cur->_parent = parent;// 需要更新平衡因子...return true;
}

        插入成功,则返回 true,插入失败(树中已经存在键值)则返回 false。

        以上只是完成了插入,插入元素之后,我们还需要更新节点的平衡因子,更新平衡因子按照以下原则进行更新:

        1. 插入元素位置位于父节点的右边,父节点的平衡因子 +1;        

        2. 插入元素位置位于父节点的左边,父节点的平衡因子 -1

        3. 更新完父节点的平衡因子之后,父节点的平衡因子的取值可能为 0、正负1、正负2

        5. 父节点的平衡因子更新完之后为0,不会影响父节点的父节点的平衡,所以不用在往上更新。

        6. 父节点的平衡因子跟新完之后为正负1,说明原来父节点的平衡因子为0,这时还会影响父节点的父节点的平衡因子,所以需要继续向上更新。当某个节点的平衡原则为正负二的时候,我们就需要通过选择使树平衡

        如下:

// 需要更新平衡因子
// 如果是在父亲的左边,父亲的平衡因子减一、右边加一
if (parent->_left == cur)parent->_balanceFactor--;
elseparent->_balanceFactor++;
// 查看爷爷结点是否需要更新while (parent) {if (parent->_balanceFactor == 0) {break;}else if (parent->_balanceFactor == 1 || parent->_balanceFactor == -1) {if (parent == _root)break;// 现在的parent就不可能等于nullparent = parent->_parent;cur = cur->_parent;if (parent->_left == cur)parent->_balanceFactor--;elseparent->_balanceFactor++;}else if(parent->_balanceFactor == 2 || parent->_balanceFactor == -2) {if (parent->_balanceFactor == 2 && cur->_balanceFactor == 1)RotateLeft(parent);else if (parent->_balanceFactor == -2 && cur->_balanceFactor == -1)RotateRight(parent);else if (parent->_balanceFactor == -2 && cur->_balanceFactor == 1)RotateLeftRight(parent);else if (parent->_balanceFactor == 2 && cur->_balanceFactor == -1)RotateRightLeft(parent);elseassert(false);break;}else {assert(false);}
}

        对于如上的代码中,其中最难的一步就是旋转,关于旋转一共会出现四种情况:左单旋、右单旋、左右双旋、右左双旋

AVL 树的旋转

        我们首先介绍右单旋,当新节点插入导较高左子树的左侧就会出现右单旋,关于右单旋出现的情况如下:

        当出现如上所示的情况时(父亲节点的平衡因子等于-2,左孩子节点的平衡因子为-1时),我们就需要进行右旋,也就是将左孩子作为父节点,父节点作为右孩子,在将左孩子的右节点链接到原父节点上。其中还有需要注意的点:右旋时的父节点不一定是根节点,所以我们在旋转的时候,还需要记录下父节点的父节点,最后将其链接到一起。

void RotateRight(Node* parent) {Node* subL = parent->_left;Node* subLR = subL->_right;// 将左孩子的右节点链接到原父亲结点if (subLR) subLR->_parent = parent;parent->_left = subLR;Node* ppNode = parent->_parent;// 将左孩子变为原父亲结点的父亲subL->_right = parent;parent->_parent = subL;// 将爷爷结点重新链接if (ppNode == nullptr) {_root = subL;_root->_parent = nullptr;}else {if (ppNode->_left == parent)ppNode->_left = subL;elseppNode->_right = subL;subL->_parent = ppNode;}subL->_balanceFactor = parent->_balanceFactor = 0;
}

        记得最后将节点的平衡因子设置为0。

        接着我们介绍左单旋:当新节点插入到较高右子树的右侧,关于这种情况如下:

        关于左单旋,其思想和右单旋基本一致,不过是将右单旋的给镜像了过来。所以当父节点的平衡因子为2,右节点的平衡因子为1的时候,我们就需要对树进行左单旋。也就是让右孩子的左节点作为父节点的右孩子,左节点作为父节点,原父节点作为左孩子的左节点。注意原父节点的父节点是否为 nullptr,最后需要更新节点的平衡因子。如下:

void RotateLeft(Node* parent) {Node* subR = parent->_right;Node* subRL = subR->_left;Node* ppNode = parent->_parent;parent->_right = subRL;if (subRL) subRL->_parent = parent;subR->_left = parent;parent->_parent = subR;if (ppNode == nullptr) {_root = subR;_root->_parent = nullptr;}else {if (ppNode->_left == parent)ppNode->_left = subR;elseppNode->_right = subR;subR->_parent = ppNode;}subR->_balanceFactor = parent->_balanceFactor = 0;
}

        第三种情况,左右双旋。左右双旋就是分别需要左旋一次,然后右旋一次,接着更新我们的平衡因子,如下:

        如上图所示,当左孩子节点的平衡因子为1,父节点的平衡因子为-2的时候,我们就需要进行左右双旋,当我们旋转之后,当前父节点的平衡因子一定为0,但原父节点和左孩子节点的平衡因子一共有三种情况,分别是0 0,1 0,0 -1。当 h = 0 的时候,插入的节点就是以上的60节点,旋转之后所有节点(一共就3个节点)都是为0,当节点插入到60的左边,那么30的平衡因子为0(如图),当节点插入到60的右边,90的平衡因子则为0。

        因为在单独调用左单选,右单旋之后,会将所有节点的平衡因子都置为0,所以我们需要进行特殊处理。如下:

void RotateLeftRight(Node* parent) {Node* subL = parent->_left;Node* subLR = subL->_right;int balanceFactor = subLR->_balanceFactor;// 先左旋后右旋RotateLeft(subL);RotateRight(parent);subLR->_balanceFactor = 0;if (balanceFactor == -1) {subL->_balanceFactor = 0;parent->_balanceFactor = 1;}else if (balanceFactor == 1) {parent->_balanceFactor = 0;subL->_balanceFactor = -1;}else if (balanceFactor == 0) {parent->_balanceFactor = 0;subL->_balanceFactor = 0;}else {assert(false);}
}

        最后一种情况:右左双旋。也就是先右旋然后在左旋,也就是和以上的情况是堆成的情况,如下:

        对于需要右左旋转的情况为父节点为2,右孩子为1.关于转换的细节和以上的左右双旋的情况向对称,在这就不细讲了,代码如下:

void RotateRightLeft(Node* parent) {Node* subR = parent->_right;Node* subRL = subR->_left;int balanceFactor = subRL->_balanceFactor;RotateRight(subR);RotateLeft(parent);// 更新平衡因子subRL->_balanceFactor = 0;if (balanceFactor == -1) {parent->_balanceFactor = 0;subR->_balanceFactor = 1;}else if (balanceFactor == 1) {parent->_balanceFactor = -1;subR->_balanceFactor = 0;}else if (balanceFactor == 0) {parent->_balanceFactor = 0;subR->_balanceFactor = 0;}else {assert(false);}
}

AVL 树的验证 + 测试

        接下来我们将对我们是新的 AVL 树进行验证,也就是看我们写出的代码是否符合 AVL 树的特性,其中主要包括特性测试和压力测试。在进行测试之前,我们需要先写出一些辅助函数,如下:

template <class K, class V>
class AVLTree {
public:typedef AVLTreeNode<K, V> Node;void InOrder() {_InOrder(_root);cout << endl;}int height() {int h = _height(_root);return h;}int size() {int s = _size(_root);return s;}bool IsBalance() {return _IsBalance(_root);}private:bool _IsBalance(Node* root) {if (root == nullptr)return true;int leftHeight = _height(root->_left);int rightHeight = _height(root->_right);if (abs(leftHeight - rightHeight) >= 2)return false;if (abs(root->_balanceFactor) >= 2)return false;return _IsBalance(root->_left) && _IsBalance(root->_right);}int _height(Node* root) {if (root == nullptr)return 0;int left = _height(root->_left);int right = _height(root->_right);int height = max(left, right);return height + 1;}int _size(Node* root) {if (root == nullptr)return 0;return _size(root->_left) + _size(root->_right) + 1;}void _InOrder(Node* root) {if (root == nullptr) return;_InOrder(root->_left);cout << root->_kv.first << " " << root->_kv.second << endl;_InOrder(root->_right);}
private:Node* _root = nullptr;
};

        我们先进行特性测试,如下:

        如上所示,我们一共验证了两组数据,其中包含了左旋、右旋、左右双旋、右左双旋四种情况。

        接着进行暴力测试,生成一百万个数据,主要测试性能和插入是否成功:

        如上所示,插入一百万个数据也可以生成平衡树。

        测试源码如下:

void TestAVL01() {int a[] = { 4, 2, 6, 1, 3, 5, 15, 7, 16, 14 };// {16, 3, 7, 11, 9, 26, 18, 14, 15}AVLTree<int, int> avtree;for (auto e : a) {if (e == 4) {int i = 0;}avtree.insert(make_pair(e, e));}avtree.InOrder();cout << avtree.height() << endl;cout << avtree.size() << endl;cout << avtree.IsBalance() << endl;
}void TestAVL02() {const int N = 1000000;vector<int> v;v.reserve(N);srand(time(0));for (int i = 0; i < N; i++) {v.push_back(rand() + 1);}size_t begin1 = clock();AVLTree<int,int> tree;for (auto e : v)tree.insert({e, e});size_t end1 = clock();cout << "insert" << end1 - begin1 << endl;cout << "Height:" << tree.height() << endl;cout << "Size:" << tree.size() << endl;size_t begin2 = clock();for (auto e : v)tree.find(e);size_t end2 = clock();cout << "find:" << end2 - begin2 << endl;cout << tree.IsBalance() << endl;
}

相关文章:

AVL树 ---(C++)

本篇讲全面的讲解 AVL 树的插入&#xff0c;旋转以及验证 AVL 树的性能&#xff08;本篇未实现删除代码&#xff09;。至于为什么会有 AVL 树&#xff0c;这是因为简单的二叉搜索树并不能直接的保证搜索的效率&#xff0c;因为当我们在二叉搜索树中插入一段有序的序列的时候&am…...

基于spring boot+MySQL 小区物业管理系统-计算机毕设 附源码37236

spring boot 小区物业管理系统 摘 要 在网络信息的时代&#xff0c;众多的软件被开发出来&#xff0c;给用户带来了很大的选择余地&#xff0c;而且人们越来越追求更个性的需求。在这种时代背景下&#xff0c;小区物业只能以客户为导向&#xff0c;以产品的持续创新作为小区物…...

Linux/Ubuntu/Debian常用服务管理命令

Linux/Ubuntu/Debian常用服务管理命令 在 Linux 系统中&#xff0c;服务管理是系统管理员日常维护工作的重要组成部分。通过一些常用的命令&#xff0c;我们可以查看服务状态、启动或停止服务、重启服务等。掌握这些命令&#xff0c;可以让系统管理工作更加高效和便捷。 1. s…...

Maven的三种项目打包方式——pom,jar,war的区别

1、pom&#xff1a;用在父级工程或聚合工程中&#xff0c;用来做jar包的版本控制&#xff0c;必须指明这个聚合工程的打包方式为pom。 聚合工程只是用来帮助其他模块构建的工具&#xff0c;本身并没有实质的内容。具体每个工程代码的编写还是在生成的工程中去写。 对于在父工程…...

[手游] 三色绘恋S Mobile Link

语音合成TTS: 文字转成语音的工具 WPS免登录一键修改器: 去除烦人的登录且能正常使用 故事简介&#xff1a; 深秋的雨季即将到来&#xff0c;正值那个为人所熟知的故事发生的前一年—— 地点&#xff1a;湖北省的重点高中&#xff0c;武汉师贰高校。 新学年开始&#xff0c;各…...

nss刷题(4)

1、[SWPUCTF 2021 新生赛]easyrce <?php error_reporting(0); highlight_file(__FILE__); if(isset($_GET[url])) { eval($_GET[url]); } ?> if(isset($_GET[url])) isset函数用来检测url变量是否存在&#xff1b;$_GET函数获取变量数据 eval($_GET[url]); eval函数用…...

iOS调整collectionViewCell顺序

效果图 原理 就是设置collectionView调整顺序的代理方法&#xff0c;这里要注意一点 调整过代理方法之后&#xff0c;一定要修改数据源&#xff0c;否则导致错乱。 还有就是在collectionView上面添加一个长按手势&#xff0c;在长按手势的不同阶段&#xff0c;调用collectionV…...

【回调函数】

1.回调函数是什么&#xff1f; 回调函数就是⼀个通过函数指针调用的函数。 如果你把函数的指针&#xff08;地址&#xff09;作为参数传递给另⼀个函数&#xff0c;当这个指针被用来调用其所指向的函数 时&#xff0c;被调用的函数就是回调函数。回调函数不是由该函数的实现方…...

找树左下角的值-力扣

本题个人认为不能叫做 找树左下角的值&#xff0c;左下角再怎么说也应当在树的左子树上&#xff0c;本题要求的节点是树最底层最左边的值。 首先想到的解法是对二叉树进行层序遍历&#xff0c;并记录本层第一个节点的值&#xff0c;当层序遍历结束时&#xff0c;此时记录的值即…...

【AI应用探讨】— Gemma2模型应用场景

目录 1. 金融风险管理 2. 营销策略优化 3. 医疗保健领域 4. 供应链管理 5. 人力资源管理 6. 自然语言处理&#xff08;NLP&#xff09; 7. 图像识别 8. 音频信号处理 9. 总结 1. 金融风险管理 场景描述&#xff1a;Gemma 2模型在金融领域可用于预测金融市场的波动性和…...

树二叉树

树 ​ 树是 n&#xff08;n≥0&#xff09;个结点的有限集。当 n 0时&#xff0c;称为空树。在任意一颗非空树中应满足&#xff1a; &#xff08;1&#xff09;有且仅有一个特定的称为根的结点。 &#xff08;2&#xff09;当 n > 1时&#xff0c;其余结点可分为 m&…...

无源晶振振荡电路失效问题分析与解决策略

无源晶振&#xff08;晶体谐振器&#xff09;在电子设备中扮演着至关重要的角色&#xff0c;为数字电路提供稳定的时钟信号。然而&#xff0c;振荡电路一旦失效&#xff0c;可能会导致整个系统运行不正常。晶发电子将从三个主要方面分析无源晶振振荡电路失效的问题&#xff0c;…...

LIMS系统在汽车第三方检测实验室的应用

随着汽车行业的快速发展&#xff0c;汽车第三方检测实验室的工作量不断增加&#xff0c;对实验室的管理效率和数据准确性提出了更高的要求。LIMS系统的引入可以实现实验室的全面数字化管理&#xff0c;提高工作效率&#xff0c;降低运营成本&#xff0c;并提升数据质量与决策支…...

positivessl泛域名https证书

PositiveSSL&#xff0c;作为Sectigo旗下的子品牌&#xff0c;一直以来颁发的https数字证书产品性价比较高&#xff0c;适合大多数个人网站和中小型企业。其中&#xff0c;DV基础型的泛域名https证书以申请简单、颁发速度快、价格低受到众多用户的欢迎。今天就随SSl盾小编了解P…...

MySQL bin-log日志恢复数据

目录 一、开启二进制日志 二、检查二进制日志是否开启 三、使用二进制日志备份和恢复 使用二进制日志备份恢复前先创建备份&#xff1a; 应用二进制日志&#xff1a; 扩展用法&#xff1a; 四、常见命令和操作 五. 使用 mysqlbinlog 工具查看二进制日志 1. 查看二进制…...

Linux网络命令——netstat

netstat是Linux系统中非常有用的网络工具&#xff0c;被称为是网络监控中的军工刀&#xff0c;足见其地位。 传统上&#xff0c;它用于问题确定而不是性能测量&#xff0c;但是也可用于查看网络上的流量&#xff0c;以确定性能问题是否由于网络阻塞引起。 netstat用于显示与I…...

手机怎么压缩图片?通过三种压缩操作

手机怎么压缩图片&#xff1f;在智能手机日益普及的今天&#xff0c;拍照分享已成为日常生活的一部分。然而&#xff0c;高质量的照片往往占用较大的存储空间&#xff0c;且在网络上传输时速度较慢。那么&#xff0c;如何在手机上压缩图片呢&#xff1f;本文将介绍三种实用的手…...

分布式CAP、BASE理论务必了解一下

分布式系统理论是计算机科学中的一个重要分支&#xff0c;它关注如何设计和实现能够跨多个物理或逻辑位置运行的系统。在分布式系统中&#xff0c;CAP定理和BASE理论是两个非常著名的理论&#xff0c;它们分别描述了分布式系统设计中的一些基本约束和原则。 CAP定理 CAP定理&…...

spring最常用的注解

核心注解 Component 描述&#xff1a;将类标记为 Spring 组件&#xff0c;以便自动检测。用途&#xff1a;通常用于标注服务类或其他支持类。 Controller 描述&#xff1a;将类标记为 Spring MVC 控制器。用途&#xff1a;用于处理 Web 请求。 Service 描述&#xff1a;将类标记…...

Docker:认识镜像仓库及其命令

文章目录 Docker Registry什么是Docker Registry 镜像仓库工作机制使用流程实际使用方法仓库的拉取机制 常用的镜像仓库---DockerHub什么是DockerHub私有仓库 镜像仓库命令docker logindocker pulldocker pushdocker searchdocker logout Docker Registry 什么是Docker Regist…...

使用 Django 创建 App

文章目录 步骤 1&#xff1a;创建 Django 项目步骤 2&#xff1a;创建 App步骤 3&#xff1a;配置 App步骤 4&#xff1a;编写代码步骤 5&#xff1a;运行服务器 在 Django 中&#xff0c;App 是组织代码的基本单元&#xff0c;它可以包含模型、视图、模板等组件&#xff0c;帮…...

java定时任务 设置开始时间、结束时间;每周一、四、六执行;并且隔n周执行。最后计算所有执行时间

java定时任务 设置开始时间、结束时间&#xff1b;每周一、四、六执行&#xff1b;并且隔n周执行。最后计算所有执行时间&#xff09; 定时任务需求程序设计依赖引入程序一、计算开始时间那周的周一时间二、根据executeTime和weekList.get(n),计算每个cron表达式。三、根据一和…...

linux的持续性学习

安装php 第一步&#xff1a;配置yum源 第二步&#xff1a;下载php。 yum install php php-gd php-fpm php-mysql -y 第三步&#xff1a;启动php。 systemctl start php-fpm 第四步&#xff1a;检查php是否启动 lsof -i :9000 计划任务 作用&am…...

MyBatis:概念简章

1. hello world 配置文件&#xff1a;mybatis-config.xml&#xff08;核心配置文件&#xff0c;用于配置连接的数据库信息&#xff09;&#xff08;一般一个&#xff09;XxxMapper.xml 该文件用于操作表&#xff08;执行sql语句&#xff09;&#xff08;一张表一个&#xff09;…...

有什么接码平台比较好用的

接码平台&#xff0c;也被称作短信接收平台或虚拟号码服务&#xff0c;主要是提供可以接收短信验证码的虚拟手机号码服务。这种服务通常被用于需要在网络平台上注册大量账号的情况&#xff0c;如营销推广、应用测试或是海淘购物时所需的手机号验证。下面将推荐几个较为好用的接…...

微服务之负载均衡器

1、负载均衡介绍 负载均衡就是将负载(工作任务&#xff0c;访问请求)进行分摊到多个操作单元(服务器&#xff0c;组件)上 进行执行。 根据负载均衡发生位置的不同&#xff0c; 一般分为服务端负载均衡和客户端负载均衡。 服务端负载均衡指的是发生在服务提供者一方&#xff…...

《时间管理九段》前四阶段学习笔记

文章目录 0.何谓时间管理九段0.1 第一段--把一件事做好0.2 第二段--把一天过好0.3 第三段--掌控两周内的固定日程0.4 第四段--掌控两周内的弹性时间0.5 第五段--科学管理3个月的项目事件0.6 第六段--实现一年的梦想0.7 第七段--明确一生的愿景0.8 第八段--正确补充和释放自身能…...

LLVM Cpu0 新后端5 静态重定位 动态重定位

想好好熟悉一下llvm开发一个新后端都要干什么&#xff0c;于是参考了老师的系列文章&#xff1a; LLVM 后端实践笔记 代码在这里&#xff08;还没来得及准备&#xff0c;先用网盘暂存一下&#xff09;&#xff1a; 链接: https://pan.baidu.com/s/1yLAtXs9XwtyEzYSlDCSlqw?…...

旅游卡是项目还是骗局?还是实实在在的旅游项目?

旅游卡是一个实实在在的旅游项目&#xff0c;而非骗局。以下是我对旅游卡项目的几点分析&#xff1a; 项目实质&#xff1a; 旅游卡项目是由国内外多条旅游线路整合而成的卡片&#xff0c;为旅游者提供方便、实惠的旅游方式。持有旅游卡&#xff0c;可以完全抵销跟团游线路中的…...

大模型+RAG,全面介绍!

1 、介绍 大型语言模型&#xff08;LLMs&#xff09;在处理特定领域或高度专业化的查询时存在局限性&#xff0c;如生成不正确信息或“幻觉”。缓解这些限制的一种有前途的方法是检索增强生成&#xff08;RAG&#xff09;&#xff0c;RAG就像是一个外挂&#xff0c;将外部数据…...

免费个人网站怎么制作/免费刷网站百度关键词

def max_sub_str(line):lens = len(line)s = e = 0while s != lens-1:e = swhile e != lens-1:if line[s:e]...

网站在线优化/网站排名优化服务公司

1云里雾里的RAW到底是什么?最近小米手机3和诺基亚1520不约而同地在拍照方面增加了一项新功能——对RAW格式的支持。这让不少手机摄影达人惊喜不已&#xff0c;惊呼手机拍照的专业时代来临了。当然对于单反玩家来说RAW格式不算什么&#xff0c;但是在手机上应用确实具有里程碑式…...

贵阳专业做网站的公司/市场营销策划方案3000字

科目编号&#xff1a;8638 座位号 2017-2018学年度第二学期期末考试 薪酬制度与薪酬管理 试题 2018年 7 月 一、单选题&#xff08;本大题共10小题&#xff0c;每小题3分&#xff0c;共计30分&#xff09; &#xff08;★请考生务必将答案填入到下面对应序号的答题框中★&…...

做p2p网站的公司/seo建设招商

//这张表存放的是所有单据模板的信息表 如果不知道单据模板的信息后可在数据库中查询PUB_BILLTEMPLET//这张表是打印模板的表改模板可以再此表修改pub_print_template//获取表头某个字段的值getBillCardPanel().getHeadItem("kitarea").getValueObject()//获取当前登…...

网站权重分散/百度号注册官网

1、接收参数是Date类型 接收参数前边加一个DateTimeFormat(pattern "yyyy-MM-dd") RequestMapping("/updateDate") public String updateDate(DateTimeFormat(pattern "yyyy-MM-dd") Date newDate) {return "redirect:list.do"; }…...

一级A视网站 一级做爰片/在百度上打广告找谁推广产品

当你遇到连接WordPress数据库链接错误时&#xff0c;可以有多个原因造成了这种错误。这时候 &#xff0c;我们就要排查出是哪里出现的问题 &#xff0c;我将在这篇文章中分享如何修复WordPress数据库连接错误时的故障排除和所有可能的原因。为什么数据库连接会发生错误通常 &am…...