【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.pswp.cn/bicheng/71609.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/71609.shtml
英文地址,請注明出處:http://en.pswp.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…