【重走C++學習之路】16、AVL樹

目錄

一、概念

二、AVL樹的模擬實現

2.1 AVL樹節點定義

2.2 AVL樹的基本結構?

2.3?AVL樹的插入

1. 插入步驟

2. 調節平衡因子

3. 旋轉處理?

?4. 開始插入

2.4 AVL樹的查找

2.5 AVL樹的刪除

1. 刪除步驟

2. 調節平衡因子

3. 旋轉處理

4. 開始刪除

結語


一、概念

二叉搜索樹查找效率為O(logN),但如果數據有序或接近有序二叉搜索樹將退化為單支樹,查找元素相當于在順序表中搜索元素,效率變為O(N),效率低下。

因此,兩位俄羅斯的數學家G.M.Adelson-Velskii和E.M.Landis在1962年發明了一種解決上述問題的方法:當向二叉搜索樹中插入新節點后,如果能保證每個節點的左右子樹高度之差的絕對值不超過1(超過則需要對樹中的節點進行調整),即可降低樹的高度,從而減少平均搜索長度。

AVL樹具有以下特點:

  • 它的左右子樹都是AVL樹
  • 左右子樹高度之差(簡稱平衡因子)的絕對值不超過1(-1/01)

平衡因子的取值由自己規定,在這里為了模擬實現,規定左子樹的高度在根結點中為負值,右子樹的高度在根結點中為正值,即平衡因子=?右子樹高度 - 左子樹高度

二、AVL樹的模擬實現

2.1 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 _bf;                     // 該節點的平衡因子AVLTreeNode(const pair<K, V>& kv):_left(nullptr),_right(nullptr),_parent(nullptr),_kv(kv),_bf(0){}
};

2.2 AVL樹的基本結構?

template <class K, class V>
class AVLTree
{typedef AVTreeNode<K, V> Node;
public:bool Insert(const std::pair<K, V>& kv);bool Find(const K& key);bool Erase(const K& key);private:Node* _root = nullptr;
};

2.3?AVL樹的插入

1. 插入步驟

  1. 按照二叉搜索樹的方式插入新節點
  2. 調整節點的平衡因子

2. 調節平衡因子

插入節點后,會影響該節點到根節點這一條路徑上節點的平衡因子,因此需要進行迭代,將這一條路徑上的平衡因子更新。

步驟:

第一步:?對插入的節點的父節點進行更新,會發生兩種情況:

  1. 如果插入的是根節點,則無父節點,不用更新。
  2. 如果插入的是父節點的左節點,則父節點的平衡因子-1;如果插入的是父節點的右節點,則父節點的平衡因子+1。

第二步:?父節點的平衡因子在插入節點后會有三種變化,也對應著三種措施:

  1. 父節點的平衡因子變為0,則說明在插入前兩顆子樹高度相差1,插入后整體的高度沒有發生變化,不需要向上繼續調整。
  2. 父節點的平衡因子變為-1或1,則說明在插入前兩顆子樹高度相等,插入后整體的高度+1,需要對父節點的父節點的平衡因子進行更新,這樣就回到了步驟一。
  3. 父節點的平衡因子變為-2或2,則說明在插入前兩個子樹高度相差1,且插入的節點為高度較高的那顆子樹的葉子節點,這導致了高度相差變為2,需要進行旋轉處理,降低樹的高度,從而達到平衡整顆樹的要求。

圖解:

第一種情況:

第二種情況:

可以看到這里在更新平衡因子的時候,只會影響節點到根節點路徑上的節點的平衡因子,其它的節點不需要考慮。

第三種情況:

更新平衡因子的時候,發現不滿足規則后,直接停止更新,轉而進行旋轉處理。

3. 旋轉處理?

旋轉有四種情況:

第一種情況:右單旋

新節點插入到較高左子樹的左側,左側的高度變高需要將右邊的調整下來,因此叫做右單旋。

具體操作:

讓subL的右孩子成為parent的左孩子,然后讓parent成為subL的右孩子,最后把兩個節點的平衡因子修改為0。

?圖解:

實現代碼:

void RotateR(Node* parent)
{Node* subL = parent->_left;Node* subLR = subL->_right;// 1.先讓把subL的右邊作為parent的左邊parent->_left = subLR;// 2.如果subLR不為空,就讓subLR的父指針指向parentif (subLR) subLR->_parent = parent;// 3.記錄parent的父節點的位置,然后把parent作為subL的右邊Node* pParent = parent->_parent;subL->_right = parent;// 4.parent的父指針指向subLparent->_parent = subL;// 5.如果ppNode為空,說明subR現在是根節點,就讓subL的父指針指向nullptr//   不是根節點就把subL的父指針指向parent的父節點,parent的父節點(左或右)指向subLif (pParent == nullptr){// 更新根節點_root = subL;subL->_parent = nullptr;}else{// 判斷parent是pparent的左還是右if (pParent->_left == parent)pParent->_left = subL;elsepParent->_right = subL;subL->_parent = pParent;}// 6.把parent和subL的平衡因子更新為0subL->_bf = parent->_bf = 0;
}

第二種情況:左單旋?

新節點插入到較高右子樹的右側,右側的高度變高需要將左邊的調整下來,因此叫做右單旋。

?具體操作:

讓subR的左孩子成為parent的右孩子,然后讓parent成為subR的左孩子,最后把兩個節點的平衡因子修改為0。

圖解:

實現代碼:

void RotateL(Node* parent)
{		Node* subR = parent->_right;Node* subRL = subR->_left;// 1.先讓把subR的左邊作為parent的右邊parent->_right = subRL;// 2.如果subRL不為空,就讓subRL的父指針指向parentif (subRL) subRL->_parent = parent;// 3.先記錄parent的父節點的位置,然后把parent作為subR的左邊 Node* pParent = parent->_parent;subR->_left = parent;// 4.parent的父指針指向subRparent->_parent = subR;// 5.如果ppNode為空,說明subR現在是根節點,就讓subR的父指針指向nullptr//   不是根節點就把subR的父指針指向parent的父節點,parent的父節點(左或右)指向subRif (pParent == nullptr){// 更新根節點_root = subR;subR->_parent = nullptr;}else{// 判斷parent是ppNode的左還是右if (pParent->_left == parent)pParent->_left = subR;elsepParent->_right = subR;subR->_parent = pParent;}// 6.把parent和subR的平衡因子更新為0subR->_bf = parent->_bf = 0;
}

第三種情況:左右雙旋?

新節點插入在較高左子樹右側,這里和第一種情況的區別就是前者是直線,后者是折線

具體操作:

先對subL進行一個左單旋,然后對parent進行右單旋,修改平衡因子,有三種改法。三個節點從左至右的三個節點一次是:subL、subLR和parent。
如果subLR的平衡因子為0,就將它們依次改為0,0, 0;
如果subLR的平衡因子為1,就將它們依次改為-1,0, 0;
如果subLR的平衡因子為-1,就將它們依次改為0,0, 1。

圖解:(這里只畫出了一種情況,剩下的類似)

實現代碼:

void RotateLR(Node* parent)
{Node* subL = parent->_left;Node* subLR = subL->_right;int bf = subLR->_bf;// 保留subLR的平衡因子的值,方便知道新插入的節點是在subLR的左子樹還是右子樹// 旋轉 先對subL進行左旋轉,再對parent進行右旋轉RotateL(subL);RotateR(parent);// 從左到右 subL subLR parentif (bf == -1)// subLR的左子樹  bf: 0 0 1{subL->_bf = 0;subLR->_bf = 0;parent->_bf = 1;}else if (bf == 1)// subLR的右子樹 bf: -1 0 0{subL->_bf = -1;subLR->_bf = 0;parent->_bf = 0;}else if (bf == 0){subL->_bf = 0;subLR->_bf = 0;parent->_bf = 0;}
}

第四種情況:右左單旋?

新節點插入在較高右子樹左側,這里和第二種情況的區別就是前者是直線,后者是折線

?具體操作:

先對subR進行一個右單旋,然后對parent進行左單旋,修改平衡因子,有三種改法。三個節點從左至右的三個節點一次是:parent、subRL和subR。
如果subRL的平衡因子為0,就將它們依次改為0,0, 0;
如果subRL的平衡因子為1,就將它們依次改為-1,0, 0;
如果subRL的平衡因子為-1,就將它們依次改為0,0, 1。

圖解:(這里只畫出了一種情況,剩下的類似)

實現代碼:

void RotateRL(Node* parent)
{Node* subR = parent->_right;Node* subRL = subR->_left;int bf = subRL->_bf;// 保留subRL的平衡因子的值,方便知道新插入的節點是在subRL的左子樹還是右子樹// 旋轉 先對subR進行右旋轉,再對parent進行左旋轉RotateR(subR);RotateL(parent);// 從左到右 parent subRL subRif (bf == -1)// subRL的左子樹  bf: 0 0 1{parent->_bf = 0;subRL->_bf = 0;subR->_bf = 1;}else if (bf == 1)// subRL的右子樹 bf: -1 0 0{parent->_bf = -1;subRL->_bf = 0;subR->_bf = 0;}else if (bf == 0){parent->_bf = 0;subRL->_bf = 0;subR->_bf = 0;}
}

?4. 開始插入

bool Insert(const pair<K, V>& kv)
{// 先按照二叉搜索數一樣插入元素// 無節點是插入if (_root == nullptr){_root = new Node(kv);return true;}// 有節點時插入Node* parent = nullptr;Node* cur = _root;while (cur){parent = cur;// 小于往左走if (kv.first < cur->_kv.first){cur = cur->_left;}// 大于往右走else if (kv.first > cur->_kv.first){cur = cur->_right;}else{// 找到了,就返回falsereturn false;}}cur = new Node(kv);// 判斷cur應該插在parent的左還是右 // 小于在左,大于在右		if (cur->_kv.first < parent->_kv.first){parent->_left = cur;cur->_parent = parent;}else{parent->_right = cur;cur->_parent = parent;}// 更新parent的平衡因子// 節點的插入只會影響cur的祖先的平衡因子(不是所有的,是一部分,分情況)while (parent){// 更新parent的平衡因子// cur在parent的左,parent->_bf--// cur在parent的右,parent->_bf++if (cur == parent->_left)parent->_bf--;elseparent->_bf++;// bf 可能為 -2、-1、0、1、2if (parent->_bf == 0){// 對上層無影響,退出break;}else if (parent->_bf == -1 || parent->_bf == 1){// 對上層有影響,迭代更新cur = parent;parent = parent->_parent;}else{// 平衡樹出現了問題,需要調整// 1.右邊高,左旋轉調整if (parent->_bf == 2){// 如果是一條直線==>左旋轉即可// 如果是一條折線==>右左旋轉if (cur->_bf == 1)RotateL(parent);else if (cur->_bf == -1)RotateRL(parent);}// 2.左邊高,右旋轉調整else if (parent->_bf == -2){// 如果是一條直線==>右旋轉即可// 如果是一條折線==>左右旋轉if (cur->_bf == -1)RotateR(parent);else if (cur->_bf == 1)RotateLR(parent);}// 調整后是平衡樹,bf為0,不需要調整了break;}}return bool;
}

2.4 AVL樹的查找

與二叉搜索樹的過程一致,這里就不多解釋了,直接上代碼:

bool Find(const K& key)
{if (_root == nullptr)return false;Node* cur = _root;while (cur){// 小于往左走if (key < cur->_kv.first){cur = cur->_left;}// 大于往右走else if (key > cur->_kv.first){cur = cur->_right;}else{// 找到了return true;}}return false;
}

2.5 AVL樹的刪除

1. 刪除步驟

  1. 我們先按照二叉搜索樹樹刪除節點的方式,刪除節點。
  2. 根據對應刪除情況更新平衡因子,更新平衡因子的方法與插入的更新方法是相反的。

2. 調節平衡因子

步驟:

第一步:

  1. 刪除節點后,如果刪除的節點為根節點,就結束。
  2. 如果刪除節點是父節點的左孩子,那么父節點的平衡因子加1;刪除節點是父節點的右孩子,那么父節點的平衡因子減1。

第二步:父節點的平衡因子在插入節點后會有三種變化,也對應著三種措施:

  1. 父節點的平衡因子變為0,則說明刪除前父親的平衡因子為1或-1,多出一個左節點或右節點,刪除節點后,左右高度相等,整體高度減1,對上層有影響,需要繼續調節
  2. 父節點的平衡因子變為-1或1,則說明刪除前父親的平衡因子為0,左右高度相等,刪除節點后,少了一個左節點或右節點,但是整體高度不變,對上層無影響,不需要繼續調節
  3. 父節點的平衡因子變為-2或2,則說明刪除前父親的平衡因子為-1或1,多了一個左節點或一個右節點,刪除了一個右節點或左節點,此時多了兩個左節點和右節點,這棵子樹一邊已經被拉高了,此時這棵子樹不平衡了,需要旋轉處理

3. 旋轉處理

這里的旋轉與插入時旋轉的沒有區別,就不詳細解答了,直接上代碼。

4. 開始刪除

bool Erase(const K& key)
{if (_root == nullptr)return false;// 有節點時插入Node* parent = nullptr;Node* cur = _root;while (cur){// 小于往左走if (key < cur->_kv.first){parent = cur;cur = cur->_left;}// 大于往右走else if (key > cur->_kv.first){parent = cur;cur = cur->_right;}else{// 找到了// 1.左邊為空,把parent指向cur的右// 2.右邊為空,把parent指向cur的左// 3.左右都不為空,用右子樹的最左邊的節點的值替換要刪除的節點,然后轉換為用1的情況刪除該節點if (cur->_left == nullptr){if (cur == _root){_root = cur->_right;delete cur;break;}else{if (parent->_left == cur){parent->_left = cur->_right;parent->_bf++;}else{parent->_right = cur->_right;parent->_bf--;}}if (parent->_bf != -1 && parent->_bf != 1) UpdateBf(parent);delete cur;}else if (cur->_right == nullptr){if (cur == _root){_root = cur->_left;delete cur;break;}else{if (parent->_left == cur){parent->_left = cur->_left;parent->_bf++;}else{parent->_right = cur->_left;parent->_bf--;}}if (parent->_bf != -1 && parent->_bf != 1) UpdateBf(parent);delete cur;}else{Node* rightMinParent = cur;Node* rightMin = cur->_right;// 先去右子樹while (rightMin->_left){rightMinParent = rightMin;rightMin = rightMin->_left;// 一種往左走}cur->_kv = rightMin->_kv;// 替代刪除// 刪除minNode  第一種情況:左節點為空if (rightMinParent->_left == rightMin){rightMinParent->_left = rightMin->_right;rightMinParent->_bf++;}else{rightMinParent->_right = rightMin->_right;rightMinParent->_bf--;}if (rightMinParent->_bf != -1 && rightMinParent->_bf != 1)                 UpdateBf(rightMinParent);delete rightMin;}return true;}}return false;
}void UpdateBf(Node* parent)
{if (parent == nullptr)return;Node* cur = parent;goto first;while (parent){// 更新parent的平衡因子// cur在parent的左,parent->_bf++// cur在parent的右,parent->_bf--if (cur == parent->_left)parent->_bf++;elseparent->_bf--;// bf 可能為 -2、-1、0、1、2first:if (parent->_bf == 0){// 對上層有影響,迭代更新// 如果parent是根節點就結束if (parent->_parent == nullptr)break;cur = parent;parent = parent->_parent;}else if (parent->_bf == -1 || parent->_bf == 1){// 對上層無影響,退出break;}else{// 平衡樹出現了問題,需要調整// 1.右邊高,左旋轉調整if (parent->_bf == 2){// 如果是一條直線==>左旋轉即可// 如果是一條折線==>右左旋轉if (parent->_right->_bf == 1){RotateL(parent);cur = parent->_parent;parent = cur->_parent;continue;}else if (parent->_right->_bf == -1)// 調整后 p sL s  如果sL是1或-1可以退出 {Node* s = parent->_right;Node* sL = s->_left;RotateRL(parent);// 不平衡向上調整if (sL->_bf != 1 && sL->_bf != -1){cur = sL;parent = cur->_parent;continue;}}else if (parent->_right->_bf == 0)    // 平衡因子修改{RotateL(parent);parent->_bf = 1;parent->_parent->_bf = -1;}}// 2.左邊高,右旋轉調整else if (parent->_bf == -2){// 如果是一條直線==>右旋轉即可// 如果是一條折線==>左右旋轉if (parent->_left->_bf == -1){RotateR(parent);cur = parent->_parent;parent = cur->_parent;continue;    //parent不是-1或1就繼續}else if (parent->_left->_bf == 1)// 調整后 s sR p  如果sL是1或-1可以退出{Node* s = parent->_left;Node* sR = s->_right;RotateLR(parent);// 不平衡向上調整 為0時如果parent為根//if (sR->_bf != 1 && sR->_bf != -1){cur = sR;parent = cur->_parent;continue;}}else if (parent->_left->_bf == 0)    // 平衡因子修改{RotateR(parent);parent->_parent->_bf = 1;parent->_bf = -1;}}// 調整后是平衡樹,bf為1或-1,不需要調整了break;}}
}

結語

上面這些是AVL樹的大致內容,其中旋轉是有些難的地方,但是面試會考察,需要著重掌握,而刪除是二叉搜索樹的刪除加上旋轉的疊加,難度更上一層了,這里如果沒能理解,可以自己畫一畫圖,并且配合著插入的圖來分析,應該會有所幫助。

下一篇將會介紹二叉搜索樹的另一種改良:紅黑樹,有興趣的朋友可以關注一下。

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

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

相關文章

char32_t、char16_t、wchar_t 用于 c++ 語言里存儲 unicode 編碼的字符,給出它們的具體定義

&#xff08;1&#xff09; #include <iostream> #include <string>int main() { std::u16string s u"C11 引入 char16_t"; // 定義 UTF-16 字符串for (char16_t c : s) // 遍歷輸出每個 char16_t 的值std::cout << std::hex << (…

redis數據類型-基數統計HyperLogLog

redis數據類型-基數統計HyperLogLog 文檔 redis單機安裝redis常用的五種數據類型redis數據類型-位圖bitmap 說明 官網操作命令指南頁面&#xff1a;https://redis.io/docs/latest/commands/?nameget&groupstringHyperLogLog介紹頁面&#xff1a;https://redis.io/docs…

邏輯思維:從混沌到秩序的理性推演在軟件開發中的應用

引言 在軟件開發的過程中&#xff0c;邏輯思維就像是開發者的“GPS導航”&#xff0c;幫助我們從混沌的需求中找到清晰的解決方案。想象一下&#xff0c;如果沒有邏輯思維&#xff0c;我們可能會在需求的海洋中迷失方向&#xff0c;最終寫出一堆“看似聰明但毫無意義”的代碼。…

Spring AI Alibaba Graph基于 ReAct Agent 的天氣預報查詢系統

1、在本示例中&#xff0c;我們僅為 Agent 綁定了一個天氣查詢服務&#xff0c;接收到用戶的天氣查詢服務后&#xff0c;流程會在 AgentNode 和 ToolNode 之間循環執行&#xff0c;直到完成用戶指令。示例中判斷指令完成的條件&#xff08;即 ReAct 結束條件&#xff09;也很簡…

HCIP(綜合實驗2)

1.實驗拓補圖 2.實驗要求 1.根據提供材料劃分VLAN以及IP地址&#xff0c;PC1/PC2屬于生產一部員工劃分VLAN10,PC3屬于生產二部劃分VLAN20 2.HJ-1HJ-2交換機需要配置鏈路聚合以保證業務數據訪問的高帶寬需求 3.VLAN的放通遵循最小VLAN透傳原則 4.配置MSTP生成樹解決二層環路問題…

使用 rebase 輕松管理主干分支

前言 最近遇到一個技術團隊的 dev 環境分支錯亂&#xff0c;因為是多人合作大家各自提交信息&#xff0c;導致出現很多交叉合并記錄&#xff0c;讓對應 log 看起來非常混亂&#xff0c;難以閱讀。 舉例說明 假設我們有一個項目&#xff0c;最初develop分支有 3 個提交記錄&a…

使用openssl為localhost創建自簽名

文章目錄 自簽名生成命令安裝安裝證書瀏覽器證書管理器 自簽名 生成命令 使用openssl生成私鑰和證書。 openssl req -x509 -newkey rsa:4096 -nodes -days 365 -subj "/CNlocalhost" -addext "subjectAltNameDNS:localhost" -keyout cert.key -out cer…

AI編程助手Cline之快速介紹

Cline 是一款深度集成在 Visual Studio Code&#xff08;VSCode&#xff09; 中的開源 AI 編程助手插件&#xff0c;旨在通過結合大語言模型&#xff08;如 Claude 3.5 Sonnet、DeepSeek V3、Google Gemini 等&#xff09;和工具鏈&#xff0c;為開發者提供自動化任務執行、智能…

1.微服務拆分與通信模式

目錄 一、微服務拆分原則與策略 業務驅動拆分方法論 ? DDD&#xff08;領域驅動設計&#xff09;中的限界上下文劃分 ? 業務功能正交性評估&#xff08;高內聚、低耦合&#xff09; 技術架構拆分策略 ? 數據層拆分&#xff08;垂直分庫 vs 水平分表&#xff09; ? 服務粒…

Element Plus表格組件深度解析:構建高性能企業級數據視圖

一、架構設計與核心能力 Element Plus的表格組件&#xff08;el-table&#xff09;基于Vue 3的響應式系統構建&#xff0c;通過聲明式配置實現復雜數據渲染。其核心設計理念體現在三個層級&#xff1a; 數據驅動&#xff1a;通過data屬性綁定數據源&#xff0c;支持動態更新與…

07前端項目----面包屑

面包屑 效果實現代碼全局事件總線-$bus 效果 實現代碼 上節searchParams中參數categoryName是表示一二三級分類所點擊的列表名 <!--bread面包屑--> <div class"bread"><ul class"fl sui-breadcrumb"><li><a href"#"…

kafka jdbc connector適配kadb數據實時同步

測試結論 源端增量獲取方式包括&#xff1a;bulk、incrementing、timestamp、incrementingtimestamp&#xff08;混合&#xff09;&#xff0c;各種方式說明如下&#xff1a; bulk: 一次同步整個表的數據 incrementing: 使用嚴格的自增列標識增量數據。不支持對舊數據的更新…

基于Hadoop的音樂推薦系統(源碼+lw+部署文檔+講解),源碼可白嫖!

摘要 本畢業生數據分析與可視化系統采用B/S架構&#xff0c;數據庫是MySQL&#xff0c;網站的搭建與開發采用了先進的Java語言、爬蟲技術進行編寫&#xff0c;使用了Spring Boot框架。該系統從兩個對象&#xff1a;由管理員和用戶來對系統進行設計構建。主要功能包括&#xff…

CentOS的安裝以及網絡配置

CentOS的下載 在學習docker之前&#xff0c;我們需要知道的就是docker是運行在Linux內核之上的&#xff0c;所以我們需要Linux環境的操作系統&#xff0c;當然了你也可以選擇安裝ubuntu等操作系統&#xff0c;如果你不想在本機安裝的話還可以考慮買阿里或者華為的云服務器&…

【條形碼識別改名工具】如何批量識別圖片條形碼,并以條碼內容批量重命名,基于WPF和Zxing的開發總結

批量圖片條形碼識別與重命名系統 (WPF + ZXing)開發總結 項目適用場景 ??電商商品管理??:批量處理商品圖片,根據條形碼自動分類歸檔??圖書館系統??:掃描圖書條形碼快速建立電子檔案??醫療檔案管理??:通過藥品條形碼整理醫療圖片資料??倉儲管理??:自動化識…

RAGFlow安裝+本地知識庫+踩坑記錄

RAGFlow是一種融合了數據檢索與生成式模型的新型系統架構&#xff0c;其核心思想在于將大規模檢索系統與先進的生成式模型&#xff08;如Transformer、GPT系列&#xff09;相結合&#xff0c;從而在回答查詢時既能利用海量數據的知識庫&#xff0c;又能生成符合上下文語義的自然…

android liveData observeForever 與 observe對比

LiveData 是一個非常有用的組件,用于在數據變化時通知觀察者。LiveData 提供了兩種主要的觀察方法:observe 和 observeForever。這兩種方法在使用場景、生命周期感知以及內存管理等方面有所不同。 一、observe 方法?? ??1. 基本介紹?? ??生命周期感知??:observe…

web-ssrfme

一、題目源碼 <?php highlight_file(__file__); function curl($url){ $ch curl_init();curl_setopt($ch, CURLOPT_URL, $url);curl_setopt($ch, CURLOPT_HEADER, 0);echo curl_exec($ch);curl_close($ch); }if(isset($_GET[url])){$url $_GET[url];if(preg_match(/file…

企業AI應用模式解析:從本地部署到混合架構

在人工智能快速發展的今天&#xff0c;企業如何選擇合適的大模型應用方式成為了一個關鍵問題。本文將詳細介紹六種主流的企業AI應用模式&#xff0c;幫助您根據自身需求做出最優選擇。 1. 本地部署&#xff08;On-Premise Deployment&#xff09; 特點&#xff1a;將模型下載…

OpenCV 圖形API(49)顏色空間轉換-----將 NV12 格式的圖像數據轉換為 BGR 顏色空間函數NV12toBGR()

操作系統&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 編程語言&#xff1a;C11 算法描述 將圖像從NV12&#xff08;YUV420p&#xff09;顏色空間轉換為BGR。 該函數將輸入圖像從NV12顏色空間轉換為RGB。Y、U和V通道值的常規范圍是0到25…