深入學習c++之---AVL樹

VL樹簡介?

AVL樹是一種自平衡二叉搜索樹,通過平衡因子(Balance Factor, BF)?旋轉操作,確保樹始終保持平衡,避免退化成鏈表,從而保證查找、插入、刪除的時間復雜度穩定在 ?O(log n)?

?核心特點?
  1. ?平衡條件?:每個節點的左右子樹高度差不超過1(BF ∈ {-1, 0, 1})。
  2. ?調整方法?:通過四種旋轉?(右單旋、左單旋、左右雙旋、右左雙旋)修復失衡。
  3. ?優勢?:比普通BST更穩定,適合頻繁動態更新的場景。

目錄

VL樹簡介?

?核心特點?

一, 搜索二叉樹的固有問題:

二,平衡因子:

1`基本定義:

2`補充說明:

三,結構的設計:

????????1`擴充的成員變量:?

?? ? ? ? 2`小坑:?

四,元素的插入:

? ? ? ? ?1`插入元素 :

?????????2`平衡因子的維護:

? ? ? ? 3`旋轉:

????????1`右單旋:

????????2`左單旋:

?????? ?3`左右雙旋:

????????4`右左雙旋:

五`元素的查找(按值):

?六`中序遍歷順序輸出:

七`類的結構和成員函數代碼匯總:


一, 搜索二叉樹的固有問題:

  • ? ? ? ? 理想情況下 , 搜索二叉樹的搜索效率已經足夠高 , 僅僅log(N)的時間復雜度 .
  • ? ? ? ? 但是 , 如果是按照近似有序序列來插入 , 會導致樹形結構退化為普通的鏈式結構 (沒錯,和普通單鏈表一樣了!!!).
  • ? ? ? ? 于是 , 本節索要探討的AVL樹的結構就應運而生了(AVL是按照兩個前蘇聯科學家的名字來命名的,沒有特殊含義).

二,平衡因子:

? ? ? ? 想要避免搜索二叉樹的退化?, 就需要在恰當的時候通過旋轉來調整(之后的內容) , 而為了能夠更加優雅地理清旋轉的邏輯和簡化相關代碼編寫?, 平衡因子是我們可以提前準備的小工具 .

1`基本定義:

  • 節點初始的平衡因子為0
  • 在節點左邊插入新節點 , 平衡因子 +1
  • 在節點右邊插入新節點,平衡因子 -1
  • 形象的來說 , 一個節點的平衡因子 = 右子樹高度 - 左子樹高度 .

2`補充說明:

  • ? ? ? ? 平衡因子可以讓我們以量化的方式來判斷一個節點左右子樹的高度差 , 以此來決定是否需要旋轉.
  • ? ? ? ? ?對于AVL樹來說 , 任何一個節點的平衡因子的絕對值必須小于等于1 .
  • ? ? ? ? ?之所以沒有要求節點的平衡因子嚴格等于0 , 是因為在部分情況下不現實 , 比如整棵樹只有兩個節點時 , 那要么左子樹高度低要么右子樹高度低 .
  • ? ? ? ? ?下面列舉幾個例子來具象化展示:?
平衡因子為0的根節點

平衡因子的絕對值為1的根節點, 此處為-1


平衡因子的絕對值大于等于2的節點 , 此處為-2
平衡因子做不到為0的情況

三,結構的設計:

? ? ?總的類框架沒變 , 還是分為一個主類來包含一個根節點的成員變量以及各種成員函數 , 和一個節點類 . 只是節點類的成員變量稍有擴充? ?

????????1`擴充的成員變量:?

AVL樹
和搜索二叉樹相同的成員變量值key / 左孩子left / 右孩子right
新增的成員變量一 :?平衡因子BF (單詞 balance factor的縮寫)
新增的成員變量二:父節點parent (方便檢索平衡因子)
//節點類
template<class K , class V>
struct AvlNode
{//構造AvlNode(K key , V val){_k.first = key;_k.second = val;_parent = nullptr;_left = nullptr;_right = nullptr;}//成員變量pair<K, V> _k;AvlNode* _parent;  //新增AvlNode* _left;AvlNode* _right;int _bf = 0;    //新增
};

?? ? ? ? 2`小坑:?

? ? ? ? 對于一顆初始為空的AVL樹 , 成員變量(_root)應該默認初始化為nullptr , 否則難以應對向空樹中插入元素時的情況.

//AVL樹的類聲明
template<class K>
class AVLTree
{
public:using Node = AVLNode<K>;
private:Node* _root == nullptr;   //c++11的語法,用于初始化列表的缺省值
};

四,元素的插入:

? ? ? ? AVL樹就是在搜索二叉樹的基礎上通過旋轉來維持平衡 , 而插入元素的函數里也包含了平衡因子的維護和旋轉的邏輯.

? ? ? ? ?1`插入元素 :

? ? ? ? AVL樹插入元素的邏輯和二叉搜索樹類似 , 只不過每個節點多出來的BF(平衡因子)和parent(父節點)要求略微的調整.

  1. 函數接受一個插入的值 key.
  2. 定義一個指向根節點的局部指針變量cur , 和一個備用的局部指針變量parent來保存cur每次更新之前的值(放標最后鏈接)
  3. 每次通過判斷cur節點的key和待插入的key的大小來決定往左子樹還是右子樹更新.
  4. 如果cur走到空,則找到插入位置,借助以保存的parent變量來連接新節點 ; 如果cur走到值和key相同的節點,說明已存在該值,不做處理直接結束.

特殊情況 : 如果起初的根節點為空(nullptr) , 則直接讓新節點作根,結束函數體.

//insert函數插入數據的部分
//-----------------------------------------------------------
//根為空的空樹情況
if (nullptr == _root)
{_root = new Node(key);return true;
}Node* parent = nullptr;
Node* cur = _root;
while (cur)
{if (key < cur->_key) //待插入的節點值小于當前節點,則往左子樹{parent = cur;cur = cur->_left}else if (key > cur->_key) //待插入的節點值大于當前節點,則往右子樹{parent = cur;cur = cur->_right;}else{return false;}
}
Node* newNode = new Node(key);
//判斷新節點應該連接到父節點的哪邊(通過值判斷!!!)
if (key < parent->_key)
{parent->_left = newNode;
}
else
{parent->_right = newNode;
}
newNode->_parent = parent;

?????????2`平衡因子的維護:

? ? ? ? 前面已經基本介紹過了節點平衡因子的定義 , 用代碼維護起來也比較簡單.

  1. 使用兩個局部指針變量 , 一個新插入節點的指針cur , 另一個是他的父節點指針parent
  2. 新節點cur的平衡因子是天然的0 , 所以從parent的平衡因子開始判斷.
  3. 若新節點cur是parent的左節點 , parent的平衡因子BS減一 ;? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?若新節點cur是parent的右節點 , parent的平衡因子BS加一;
  4. 接下來直接判斷更新后的當前parent的平衡因子 , 有三種情況:? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 第一種 : parent更新后平衡因子為0 , 說明平衡了 , 不用旋轉 , 結束插入的過程.? ? ? ? ? ? ? 第二種 : parent更新后的平衡因子為 1或-1 , 說明還是平衡 , 但是需要將cur和parent全部往上更新一層 , 進行第?3 點的操作.? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 第三種 : parent更新后的平衡因子為2或-2 , 說明當前節點的左右子樹不平衡 , 需要旋轉(下文的內容,此處省略).
//維護平衡因子
cur = newNode;
while (parent)
{if (parent->_left == cur){parent->_bf--;}else{parent->_bf++;}if (parent->_bf == 0){return true;}else if (abs(parent->_bf) == 1){cur = parent;parent = parent->_parent;}else if (abs(parent->_bf) == 2){//旋轉的類型和邏輯 , 此處略}else{assert(false); //防御性編程}
}

? ? ? ? 3`旋轉:

? ? ? ? 旋轉是最有意思的部分 , 通過節點不平衡的情況來判斷旋轉的類型 .

????????旋轉的類型有左單旋/右單旋/右左雙旋/左右雙旋.

  1. 單旋轉 : 存粹的左子樹高或者右子樹高 , 可以大致理解為新插入的節點是左子樹的最左邊或右子樹的最右邊 .
  2. 雙旋轉:不存粹的左子樹高或者右子樹高 , 可以大致理解為新插入的節點是左子樹的葉子結點的右側 或 右子樹的葉子結點的左側.

不純粹的高 :

???????? 如果是看圖 , 會發現一個節點的第一個節點插入到了左邊(右邊) , 下一個節點插入在了剛剛插入的節點的右邊(右邊) ;

????????如果是看平衡因子 , 令最后插入的節點為cur , 則cur->Parent的平衡因子和cur->Parent->Parent的平衡因子異號.

????????1`右單旋:

// 右單旋
void RotateR(Node* root) 
{Node* subL = root->_left;Node* subLR = subL->_right;//將左子樹的左子樹節點鏈接給旋轉節點的左邊root->_left = subLR;if (subLR != nullptr) //防止左子樹的左子樹的跟節點為空{subLR->_parent = root;}//提前保存節點旋轉之前的父節點Node* oldParent = root->_parent;//將旋轉節點和其左子節點顛倒父子關系subL->_right = root;root->_parent = subL;if (root == _root) //待旋轉節點為整棵樹的跟的情況{_root = subL;subL->_parent = nullptr;}else{//讓subL和root的父節點建立鏈接if (oldParent->_right == root){oldParent->_right = subL;}else{oldParent->_left = subL;}subL->_parent = oldParent;}//更新平衡因子root->_bf = 0;subL->_bf = 0;
}

????????2`左單旋:

//左單旋
void RotateL(Node* root)
{Node* subR = root->_right;Node* subRL = subR->_left;root->_right = subRL;if (subRL != nullptr){subRL->_parent = root;}Node* oldParent = root->_parent;subR->_left = root;root->_parent = subR;if (root == _root){_root = subR ;subR->_parent = nullptr;}else{if (oldParent->_left == root){oldParent->_left = subR;}else{oldParent->_right = subR;}subR->_parent = oldParent;}root->_bf = 0;subR->_bf = 0;
}

?????? ?3`左右雙旋:

?

//左右雙旋
void RotateLR(Node* root)
{Node* subL = root->_left;Node* subLR = subL->_right;int bf_subLR = subLR->_bf;RotateL(subL);RotateR(root);if (bf_subLR == 0){root->_bf = 0;subL->_bf = 0;subLR->_bf = 0;}else if (bf_subLR == 1){root->_bf = 0;subL->_bf = -1;subLR->_bf = 0;}else if (bf_subLR == -1){root->_bf = 1;subL->_bf = 0;subLR->_bf = 0;}else{assert(false);}
}

????????4`右左雙旋:

//右左雙旋
void RotateRL(Node* root)
{Node* subR = root->_right;Node* subRL = subR->_left;int bf_subRL = subRL->_bf;RotateR(subR);RotateL(root);if (bf_subRL == 0){root->_bf = 0;subR->_bf = 0;subRL->_bf = 0;}else if(bf_subRL == 1){root->_bf = -1;subR->_bf = 0;subRL->_bf = 0;}else if (bf_subRL == -1){root->_bf = 1;subR->_bf = 0;subRL->_bf = 0;}else{assert(false);}
}

五`元素的查找(按值):

? ? ? ? 元素的查找平平無奇 , 和搜索二叉樹一個樣 ,? 都是拿著待查找的值key從根節點開始比對 , key更小則找左子樹,更大則找右子樹 .

//按值查找節點
Node* Find(const K& key)
{Node* cur = _root;while (cur){if (key < cur->_key){cur = cur->_left;}else if (key > cur->_key){cur = cur->_right;}else{return cur;}}return false;
}

?六`中序遍歷順序輸出:

? ? ? ? 一般來說 , AVL樹的涉及使得? 左子樹的值<根節點的值<右子樹的值 . 所以通過中序遍歷(左子樹->根節點->左子樹)正好就可以得到一個順序序列.

? ? ? ? 需要注意的是:樹形結構的遞歸操作需要將根節點作為參數傳遞,但由于根節點通常作為類的私有成員變量,無法在外部直接訪問。因此,在C++中常見的做法是提供一個公有成員函數來傳遞根節點,從而確保內部的遞歸函數能夠正常運行。

//設置為共有成員函數,方便外部調用
public:
void MidTrave()
{_MidTrave(_root);cout << endl;
}//設置成私有成員函數,僅僅讓類類內部訪問
private:
void _MidTrave(Node* root)
{if (nullptr == root){return;}_MidTrave(root->_left);cout << root->_key << " ";_MidTrave(root->_right);
}

七`類的結構和成員函數代碼匯總:

....//頭文件的包含此處省略
----------------------------
//節點類的聲明
template<class K>
struct AVLNode
{AVLNode(K key):_key(key), _parent(nullptr), _left(nullptr), _right(nullptr), _bf(0){}K _key;AVLNode<K>* _parent;AVLNode<K>* _left;AVLNode<K>* _right;int _bf;
};//AVL樹的類聲明
template<class K>
class AVLTree
{
public:using Node = AVLNode<K>;//插入bool Insert(const K& key){//根為空的空樹情況if (nullptr == _root){_root = new Node(key);return true;}Node* parent = nullptr;Node* cur = _root;while (cur){if (key < cur->_key) //待插入的節點值小于當前節點,則往左子樹{parent = cur;cur = cur->_left;}else if (key > cur->_key) //待插入的節點值大于當前節點,則往右子樹{parent = cur;cur = cur->_right;}else{return false;}}Node* newNode = new Node(key);//判斷新節點應該連接到父節點的哪邊(通過值判斷!!!)if (key < parent->_key){parent->_left = newNode;}else{parent->_right = newNode;}newNode->_parent = parent;//----------------------------------------------------------------//維護平衡因子cur = newNode;while (parent){if (parent->_left == cur){parent->_bf--;}else{parent->_bf++;}if (parent->_bf == 0){return true;}else if (abs(parent->_bf) == 1){cur = parent;parent = parent->_parent;}else if (abs(parent->_bf) == 2){if (parent->_bf == -2 && cur->_bf <= 0) //右單旋{RotateR(parent);}else if (parent->_bf == 2 && cur->_bf >= 0) //左單旋{RotateL(parent);}else if (parent->_bf == -2 && cur->_bf >= 0) //左右雙旋{RotateLR(parent);}else if (parent->_bf == 2 && cur->_bf <= 0) //右左雙旋{RotateRL(parent);}else{assert(false);}break;}else{assert(false);}}return true;}// 右單旋void RotateR(Node* root) {Node* subL = root->_left;Node* subLR = subL->_right;//將左子樹的左子樹節點鏈接給旋轉節點的左邊root->_left = subLR;if (subLR != nullptr) //防止左子樹的左子樹的跟節點為空{subLR->_parent = root;}//提前保存節點旋轉之前的父節點Node* oldParent = root->_parent;//將旋轉節點和其左子節點顛倒父子關系subL->_right = root;root->_parent = subL;if (root == _root) //待旋轉節點為整棵樹的跟的情況{_root = subL;subL->_parent = nullptr;}else{//讓subL和root的父節點建立鏈接if (oldParent->_right == root){oldParent->_right = subL;}else{oldParent->_left = subL;}subL->_parent = oldParent;}//更新平衡因子root->_bf = 0;subL->_bf = 0;}//左單旋void RotateL(Node* root){Node* subR = root->_right;Node* subRL = subR->_left;root->_right = subRL;if (subRL != nullptr){subRL->_parent = root;}Node* oldParent = root->_parent;subR->_left = root;root->_parent = subR;if (root == _root){_root = subR ;subR->_parent = nullptr;}else{if (oldParent->_left == root){oldParent->_left = subR;}else{oldParent->_right = subR;}subR->_parent = oldParent;}root->_bf = 0;subR->_bf = 0;}//左右雙旋void RotateLR(Node* root){Node* subL = root->_left;Node* subLR = subL->_right;int bf_subLR = subLR->_bf;RotateL(subL);RotateR(root);if (bf_subLR == 0){root->_bf = 0;subL->_bf = 0;subLR->_bf = 0;}else if (bf_subLR == 1){root->_bf = 0;subL->_bf = -1;subLR->_bf = 0;}else if (bf_subLR == -1){root->_bf = 1;subL->_bf = 0;subLR->_bf = 0;}else{assert(false);}}//右左雙旋void RotateRL(Node* root){Node* subR = root->_right;Node* subRL = subR->_left;int bf_subRL = subRL->_bf;RotateR(subR);RotateL(root);if (bf_subRL == 0){root->_bf = 0;subR->_bf = 0;subRL->_bf = 0;}else if(bf_subRL == 1){root->_bf = -1;subR->_bf = 0;subRL->_bf = 0;}else if (bf_subRL == -1){root->_bf = 1;subR->_bf = 0;subRL->_bf = 0;}else{assert(false);}}//按值查找節點Node* Find(const K& key){Node* cur = _root;while (cur){if (key < cur->_key){cur = cur->_left;}else if (key > cur->_key){cur = cur->_right;}else{return cur;}}return nullptr;}void MidTrave(){_MidTrave(_root);cout << endl;}
private:void _MidTrave(Node* root){if (nullptr == root){return;}_MidTrave(root->_left);cout << root->_key << " ";_MidTrave(root->_right);}Node* _root = nullptr;
};

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/88060.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/88060.shtml
英文地址,請注明出處:http://en.pswp.cn/web/88060.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

【PTA數據結構 | C語言版】輸出 1 ~ n

本專欄持續輸出數據結構題目集&#xff0c;歡迎訂閱。 文章目錄題目代碼題目 給定正整數 n&#xff0c;輸出 1 ~ n&#xff0c;每個數字占一行。 本題旨在測試不同的算法在各種數據情況下的表現。各組測試數據特點如下&#xff1a; 數據 0&#xff1a;測試基本正確性&#x…

如何禁止用戶復制頁面內容?

某些特定的業務場景下&#xff0c;我們可能會有禁止用戶復制頁面內容的需求。比如&#xff1a; 付費內容保護&#xff1a;在線小說、付費課程等&#xff0c;希望防止內容被輕易拷貝和傳播。試卷或答題系統&#xff1a;防止考生將題目復制出去尋求場外幫助。敏感信息展示&#x…

React + PDF.js 預覽 PDF 文件:從基礎實現到高級優化的完整指南

關鍵點 PDF.js&#xff1a;Mozilla 開發的開源 JavaScript 庫&#xff0c;用于在瀏覽器中渲染 PDF 文件。React 集成&#xff1a;結合 React 組件化特性&#xff0c;實現高效、交互式的 PDF 預覽功能。功能實現&#xff1a;支持 PDF 文件加載、頁面導航、縮放、搜索、書簽和注…

新能源汽車BMS電感產品應用及選型推薦

在新能源電動汽車中&#xff0c;BMS&#xff08;電池管理系統&#xff09;如同一個守護者&#xff0c;默默守護電池的安全與性能。它精準監控電壓、電流、溫度&#xff0c;防止過充過放&#xff0c;并通過智能均衡技術提升續航能力。電感在BMS系統的電源轉換、濾波和隔離通信等…

【機器學習筆記 Ⅱ】12隨機森林

隨機森林&#xff08;Random Forest&#xff09;詳解 隨機森林是一種基于集成學習&#xff08;Ensemble Learning&#xff09;的高性能分類/回歸算法&#xff0c;通過構建多棵決策樹并綜合其預測結果&#xff0c;顯著提升模型的準確性和魯棒性。其核心思想是“集體智慧優于個體…

問題 1:MyBatis-plus-3.5.9 的分頁功能修復

問題 1&#xff1a;MyBatis-plus-3.5.9 的分頁功能修復 使用 Sw?agger 接口文檔?依次對上述接口進行測 試&#xff0c;發現 listU?serVOByPage 接口有一些問題&#xff01; 分頁好像沒有生效&#xff0c;還是查出了全部數據&#xff1a; 由于我們用的是 MyBatis Plus 來操…

Qt 如何提供在線幫助

Qt 如何提供在線幫助一、概述二、工具提示、狀態提示和"Whats This?"幫助1、工具提示(Tool Tips)添加工具提示到控件富文本工具提示全局工具提示設置延遲顯示控制自定義工具提示窗口禁用工具提示工具提示與狀態欄聯動特點&#xff1a;2、狀態提示(Status Tips)3、&q…

Typecho站點關閉插件開發全指南:從原理到實現

文章目錄 開發Typecho站點關閉插件:從原理到實現一、背景與需求分析二、插件設計思路2.1 技術選型2.2 功能模塊設計三、插件開發實現3.1 插件基礎結構3.2 插件主文件實現3.3 核心功能實現3.4 后臺管理界面3.5 關閉頁面模板四、插件配置完善4.1 配置表單實現4.2 定時任務處理五…

詳細解析 .NET 依賴注入的三種生命周期模式

文章目錄一、Transient&#xff08;瞬時生命周期&#xff09;原理使用方式核心特性適用場景優勢劣勢二、Scoped&#xff08;作用域生命周期&#xff09;原理使用方式核心特性適用場景優勢劣勢三、Singleton&#xff08;單例生命周期&#xff09;原理使用方式核心特性適用場景優…

軟件工程經濟與倫理

前言 各位帥哥美女&#xff0c;能看到這篇博客的都有口福了&#xff0c;學習這門課程就像遨游在大份的海洋&#xff0c;一不小心就吃上一口。能看到這篇博客說明我們是有緣人可以點贊收藏一下&#xff0c;這篇博客可以在你無比饑餓的時候給你送上一坨&#xff01;&#xff08;香…

AI 體驗走查 - 火山引擎存儲的 AI UX 探索之路

01 概述 火山引擎存儲技術團隊驅動 AI 自主完成用戶體驗走查 / 可用性測試的執行與評價&#xff0c;幫助業務改善交互體驗。 立項“故事走查”的背景訴求和 AI 機遇 如何搭建“AI 評價”能力&#xff0c;精準識別交互問題 讓交互體驗故事走查變為技術產品&#xff0c;講解系…

【世紀龍科技】汽車零部件檢驗虛擬實訓室-助力汽車職教實訓

在汽車產業加速向電動化、智能化轉型的背景下&#xff0c;職業院校汽車專業教學面臨新的挑戰&#xff1a;傳統實訓受限于設備數量不足、操作風險高、標準化程度低等問題&#xff0c;導致學生實踐機會有限&#xff0c;技能掌握不扎實。如何讓學生在有限資源下高效掌握零部件檢驗…

MySQL常用操作 查看表描述以及表結構、連接數及緩存和性能指標

查看表描述以及表結構查看數據庫名SHOW DATABASES; SELECT DATABASE(); SELECT DATABASE() AS current_database;查看數據庫中表的列表SHOW TABLES; SELECT TABLE_NAME, TABLE_COMMENT FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA your_database_name; SELECT TABLE_NA…

音視頻學習(三十六):websocket協議總結

概述項目描述標準RFC 6455使用端口默認 80&#xff08;ws&#xff09;&#xff0c;443&#xff08;wss&#xff09;基于協議TCP特性全雙工、低開銷、持久連接、可穿透代理特點 全雙工通信&#xff1a; WebSocket 允許客戶端和服務器之間建立一個持久的連接&#xff0c;并且數據…

docker版本nacos的搭建

1.下載鏡像2.拷貝出容器中對應的配置文件&#xff0c;logs&#xff0c;data&#xff0c;conf3.編寫yaml配置文件version: 3.8 services:nacos-server:image: nacos/nacos-server:v2.4.0container_name: nacos-serverrestart: unless-stoppedports:- "8848:8848" # …

【機器學習深度學習】 如何解決“宏平均偏低 / 小類識別差”的問題?

目錄 &#x1f9e9; 場景 一、先問清楚&#xff1a;小類差&#xff0c;到底差在哪&#xff1f; 二、對癥下藥&#xff1a;六大優化策略&#xff08;分類任務專用&#xff09; ? 1. 處理類別不平衡&#xff08;最常見&#xff09; ? 2. 優化數據質量 ? 3. 更強的模型結…

數據結構 --- 棧

棧 --- stack前言一、棧結構二、相關方法1.初始化2.入棧3.出棧4.判空5.獲取棧頂元素6.獲取棧大小7.銷毀前言 棧是一個特殊的線性表&#xff0c;遵循一個先進后出的特性&#xff0c;即操作數據&#xff08;入棧&#xff0c;出棧&#xff09;只能從棧頂操作&#xff0c;棧底是一…

【uniapp】---- 在 HBuilderX 中使用 tailwindcss

1. 前言 接手了一個uniapp的微信小程序項目,因為在上一個 taro 的項目中使用的 tailwindcss,感覺比較方便,又不想動項目中原來的代碼,因此就配置 tailwindcss,在新創建的子包中使用。 2. 分析 vue2 版本的 uni-app 內置的 webpack 版本為 4 , postcss 版本為 7, 所以還是…

Spring Boot + Easy Excel 自定義復雜樣式導入導出

tips&#xff1a;能用模板就用模板&#xff0c;當模板不適用的情況下&#xff0c;再選擇自定義生成 Excel。官網&#xff1a;https://easyexcel.opensource.alibaba.com安裝<dependency><groupId>com.alibaba</groupId><artifactId>easyexcel</arti…

Spark從入門到實戰:安裝與使用全攻略

目錄一、Spark 簡介1.1 Spark 的概念1.2 Spark 的優勢1.3 Spark 的應用場景二、安裝前準備2.1 硬件要求2.2 軟件要求2.3 下載 Spark三、Spark 安裝步驟3.1 解壓安裝包3.2 配置環境變量3.3 配置 spark-env.sh3.4 配置 slaves 文件&#xff08;分布式模式&#xff09;3.5 啟動 Sp…