【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-VelskyE.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树的插入操作包括三步:

  1. 按二叉查找树规则插入节点
  2. 更新每个节点的平衡因子
  3. 如果需要,进行旋转操作来恢复树的平衡。
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,表示该节点不平衡。我们需要通过旋转操作来恢复平衡。旋转操作有四种:

  1. 右单旋(RotateR)
  2. 左单旋(RotateL)
  3. 左右双旋(RotateLR)
  4. 右左双旋(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树的平衡之道的内容啦,各位大佬有什么问题欢迎在评论区指正,或者私信我也是可以的啦,您的支持是我创作的最大动力!❤️
在这里插入图片描述

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

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

相关文章

么是静态住宅IP,跨境电商为什么需要静态住宅IP

静态住宅IP是指直接分配给一台属于私人住宅网络的设备的固定IP地址&#xff0c;这种地址不会频繁更改。它们作为代理IP&#xff0c;使使用者能够通过这些代理服务器进行网络访问&#xff0c;而对外显示的则是该住宅的IP地址。由于这些IP地址属于真实的住宅或个人&#xff0c;并…

清华大学deepseek教程第四版 DeepSeek+DeepResearch 让科研像聊天一样简单(附下载)

deepseek使用教程系列 DeepSeekDeepResearch 让科研像聊天一样简单(附下载) https://pan.baidu.com/s/1VMgRmCSEzNvhLZQc8mu6iQ?pwd1234 提取码: 1234 或 https://pan.quark.cn/s/f3d4511b790a

leetcode刷题记录(一百零七)——279. 完全平方数

&#xff08;一&#xff09;问题描述 279. 完全平方数 - 力扣&#xff08;LeetCode&#xff09;279. 完全平方数 - 给你一个整数 n &#xff0c;返回 和为 n 的完全平方数的最少数量 。完全平方数 是一个整数&#xff0c;其值等于另一个整数的平方&#xff1b;换句话说&#x…

软考高级信息系统项目管理师笔记-第2章信息技术发展

第2章 信息技术发展 2.1 信息技术及其发展 1、按表现形态的不同,信息技术可分为硬技术(物化技术)与软技术(非物化技术)。前者指各种信息设备及其功 能,如传感器、服务器、智能手机、通信卫星、笔记本电脑。后者指有关信息获取与处理的各种知识、方法 与技能,如语言文字…

搭建RAG知识库的完整源码实现

搭建RAG知识库的完整源码实现&#xff08;基于Python 3.8&#xff09;&#xff1a; # -*- coding: utf-8 -*- # 文件名&#xff1a;rag_knowledge_base.py # RAG知识库搭建完整源码&#xff08;含中文注释&#xff09;import os import re import shutil import chromadb from…

利用AFE+MCU构建电池管理系统(BMS)

前言 实际BMS项目中&#xff0c;可能会综合考虑成本、可拓展、通信交互等&#xff0c;用AFE&#xff08;模拟前端&#xff09;MCU&#xff08;微控制器&#xff09;实现BMS&#xff08;电池管理系统&#xff09;。 希望看到这篇博客的朋友能指出错误或提供改进建议。 有纰漏…

基于SpringBoot的智慧家政服务平台系统设计与实现的设计与实现(源码+SQL脚本+LW+部署讲解等)

专注于大学生项目实战开发,讲解,毕业答疑辅导&#xff0c;欢迎高校老师/同行前辈交流合作✌。 技术范围&#xff1a;SpringBoot、Vue、SSM、HLMT、小程序、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、安卓app、大数据、物联网、机器学习等设计与开发。 主要内容&#xff1a;…

什么是 Cloud Studio DeepSeek ; 怎么实现Open WebUI快速体验

什么是 Cloud Studio DeepSeek ;怎么实现Open WebUI快速体验 一、概述 欢迎使用 Cloud Studio DeepSeek 工作空间!我们已为您预装并启动了以下服务,等待加载十几秒即可查看效果: Ollama 服务:支持通过 API 调用 DeepSeek 模型。 AnythingLLM 前端服务:提供交互式聊天界…

【Python 语法】常用 Python 内置函数

reversed() 反转reversed() 的语法反转字符串、列表、元组 sorted() 自定义排序sorted() 语法使用示例1. 基本排序&#xff1a;默认升序排列2. 基本排序&#xff1a;降序排列3. 自定义排序&#xff1a;使用 key 参数4. 自定义排序&#xff1a;按某种规则进行排序5. 排序字典&am…

[网络] 如何开机自动配置静态IP,并自动启动程序

背景&#xff1a; 需要固定ip地址&#xff0c;并且能够自动启动可执行文件。 流程&#xff1a; 1.在/etc/network/interfaces 中添加 auto eth0 iface eth0 inet staticaddress 192.168.1.100netmask 255.255.255.0gateway 192.168.1.1 2.将下面这行代码添加自动启动脚本 …

打造智能聊天体验:前端集成 DeepSeek AI 助你快速上手

DeepSeek AI 聊天助手集成指南 先看完整效果&#xff1a; PixPin_2025-02-19_09-15-59 效果图&#xff1a; 目录 项目概述功能特点环境准备项目结构组件详解 ChatContainerChatInputMessageBubbleTypeWriter 核心代码示例使用指南常见问题 项目概述 基于 Vue 3 TypeScrip…

【C# 数据结构】队列 FIFO

目录 队列的概念FIFO (First-In, First-Out)Queue<T> 的工作原理&#xff1a;示例&#xff1a;解释&#xff1a; 小结&#xff1a; 环形队列1. **FIFO&#xff1f;**2. **环形缓冲队列如何实现FIFO&#xff1f;**关键概念&#xff1a; 3. **环形缓冲队列的工作过程**假设…

Mac 清理缓存,提高内存空间

步骤 1.打开【访达】 2.菜单栏第五个功能【前往】&#xff0c;点击【个人】 3.【command shift J】显示所有文件&#xff0c;打开【资源库】 4.删除【Containers】和【Caches】文件 Containers 文件夹&#xff1a;用于存储每个应用程序的沙盒数据&#xff0c;确保应用程序…

Hutool - DFA:基于 DFA 模型的多关键字查找

一、简介 在文本处理中&#xff0c;常常需要在一段文本里查找多个关键字是否存在&#xff0c;例如敏感词过滤、关键词匹配等场景。Hutool - DFA 模块基于确定性有限自动机&#xff08;Deterministic Finite Automaton&#xff0c;DFA&#xff09;模型&#xff0c;为我们提供了…

C++STL容器之map

1.介绍 map是 C 标准模板库&#xff08;STL&#xff09;中的一个关联容器&#xff0c;用于存储键值对&#xff08;key-value pairs&#xff09;。map中的元素是按照键&#xff08;key&#xff09;进行排序的&#xff0c;并且每个键在容器中是唯一的。map通常基于红黑树&#xf…

CentOS的ssh复制文件

1.前提 首先要已经连接上了对方的ssh 2.命令 scp [文件] 目标IP:目标路径 例如&#xff1a; $PWD是一个环境变量&#xff0c;可以获取当前绝对目录&#xff0c;ssh上传的时候一定要确保对方有这个目录才行&#xff0c;不然会报错 3.递归上传 scp -r 目录 目标IP:路径 可以…

《Python实战进阶》专栏 No.3:Django 项目结构解析与入门DEMO

《Python实战进阶》专栏 第3集&#xff1a;Django 项目结构解析与入门DEMO 在本集中&#xff0c;我们将深入探讨 Django 的项目结构&#xff0c;并实际配置并运行一个入门DEMO博客网站&#xff0c;帮助你在 Web 开发中更高效地使用 Django。Django 是一个功能强大的 Python Web…

每日一题——376. 摆动序列

题目链接&#xff1a;376. 摆动序列 - 力扣&#xff08;LeetCode&#xff09; 代码&#xff1a; class Solution { public:int wiggleMaxLength(vector<int>& nums) {int curdiff 0;int prediff 0;int result 1; for(int i 0;i < nums.size()-1;i){curdiff …

DeepSeek与ChatGPT:AI语言模型的全面技术解析与对比

DeepSeek与ChatGPT:AI语言模型的全面技术解析与对比 一、诞生背景与技术演进路径 1.1 OpenAI与ChatGPT的生态布局 ChatGPT的研发主体OpenAI成立于2015年,早期定位为非营利性研究机构,核心目标为实现通用人工智能(AGI)。其技术路径以Transformer架构为基础,通过堆叠参数规…

[原创](Modern C++)现代C++的关键性概念: 学习新算法: std::unique_copy

[作者] 常用网名: 猪头三 出生日期: 1981.XX.XX 企鹅交流: 643439947 个人网站: 80x86汇编小站 编程生涯: 2001年~至今[共24年] 职业生涯: 22年 开发语言: C/C、80x86ASM、PHP、Perl、Objective-C、Object Pascal、C#、Python 开发工具: Visual Studio、Delphi、XCode、Eclipse…