數據結構(六)——紅黑樹及模擬實現

目錄

前言

紅黑樹的概念及性質

紅黑樹的效率

紅黑樹的結構

紅黑樹的插入

變色不旋轉

單旋+變色

雙旋+變色

插入代碼如下所示:

紅黑樹的查找

紅黑樹的驗證

紅黑樹代碼如下所示:

小結


前言

在前面的文章我們介紹了AVL這一棵完全二叉搜索樹,我們分析后發現AVL樹查找的時間復雜度是O(logN),說明這是一個很優秀的數據結構,但是在底層實現我們發現,AVL樹為了維持它的性質,是需要進行頻繁的旋轉的,這樣會造成不必要的消耗,那么有沒有一種數據結構既可保證它的時間復雜度為O(logN),又能避免頻繁的旋轉呢?這就是我們今天要介紹的紅黑樹,學習了紅黑樹之后我們將用紅黑樹作為底層,去封裝實現一個模擬的map和set,下面讓我們一起來學習吧。

參考文章:

據結構(五)——AVL樹(平衡二叉搜索樹)

紅黑樹的概念及性質

首先我們要知道什么是紅黑樹,與AVL樹相比,紅黑樹的節點多了一個標志符號,這個標志符號表示節點的顏色,我們這里用枚舉來表示,通過控制節點的顏色來判平衡以及判斷是否需要旋轉。由于紅黑樹的旋轉不像AVL樹那么頻繁,所以它是一顆不完全平衡二叉搜索樹。

下面我們引入路徑的概念,我們把從根節點開始,一直到空節點的一條路線稱為路徑

它的規則如下所示:

規則一:根節點必須是黑色,其余節點的顏色不是黑色就是紅色

規則二:每個紅色節點的左右子樹必須是紅色的,即:不存在連續的紅色節點

規則三:每條路徑的黑色節點數量是相同的,其中我們把空節點視為黑色節點

規則四:最長路徑的節點數量不超過最長路徑節點數量的二倍

分析上面的規則我們發現,其中最關鍵也是最難維護的就是規則三,我們發現,在極端場景下,最短路徑就是全為黑色節點的路徑,最長路徑就是紅黑間隔的路徑,所以維護好了規則三,規則四自然就實現了

紅黑樹的效率

由于有規則四的存在,我們設紅黑樹最短路徑的節點數量是h,那么最長路徑的節點數量就是2*h,我們設每條路徑上黑色節點的數量是N,那么他們應該滿足:2^{h}-1<=N<2^{2*h}-1,也就是說,如果我們要在紅黑樹當中進行增刪查改,最少要走的長度是2^{h}-1,查找的次數就是log(N),最壞也就是查找2*log(N),分析下來,它的時間復雜度還是log(N)。

紅黑樹的結構

從上面的分析中我們知道,紅黑樹的大致框架與AVL樹相似,只不過將平衡因子改成了用枚舉表示的顏色,后續我們也是通過控制顏色來判平衡,因此紅黑樹的結構如下所示:

enum Colour
{BLACK,RED
};
template<class K, class V>
struct RBTreeNode
{pair<K, V> _kv;RBTreeNode<K, V>* _left;RBTreeNode<K, V>* _right;RBTreeNode<K, V>* _parent;Colour _col;RBTreeNode(const pair<K, V>& kv):_kv(kv), _left(nullptr), _right(nullptr), _parent(nullptr){}
};template<class K, class V>
class RBTree
{typedef RBTreeNode<K, V> Node;
public:
private:Node* _root = nullptr;
};

紅黑樹的插入

我們來分析紅黑樹的插入過程,首先由于它是一顆搜索樹,所以按照搜索樹的規則進行插入,更改插入結點的顏色,然后我們向上進行調整。

對于插入節點的顏色,我們發現,如果我們插入黑色,那么就在當前路徑上增加了一個黑色節點,不符合每條路徑上黑色節點數量相同這一規則,所以我們插入節點的顏色應該為紅色。代碼如下所示:

bool Insert(const pair<K, V>& kv)
{if (_root == nullptr){_root = new Node(kv);_root->_col = BLACK;return true;}Node* cur = _root;Node* parent = nullptr;while (cur){if (cur->_kv.first > kv.first){parent = cur;cur = cur->_left;}else if (cur->_kv.first < kv.first){parent = cur;cur = cur->_right;}else{cout << "插入值已經存在,請重新插入" << endl;return false;}}cur = new Node(kv);cur->_col = RED;if (parent->_kv.first < kv.first){parent->_right = cur;}else{parent->_left = cur;}cur->_parent = parent;
}

當我們插入紅色節點后,會出下面幾種情況:

情況一:父節點為黑色,不違反紅黑樹的規則,那么就停止向上調整

情況二:父節點為紅色,違反規則,那么就需要向上調整

接下來我們重點分析情況二。由于不能有兩個連續的紅色節點,所以我們應當把父節點變成黑色,但是將父節點變成黑色后我們就在當前路徑上憑空增加了一個黑色節點,這樣破壞了黑色節點數量相同這一規則,那么我們就需要調整其他路徑上黑色節點的數量,如果是這樣的話,是不符合我們的預期的,我們再仔細觀察紅黑樹的結構,我們把父節點的父節點定義為grandfather(祖父節點),那么祖父節點的另一個子節點稱為uncle(叔節點),我們發現,無論什么情況,只要涉及到需要向上調整,祖父節點總是黑色的,此時,如果叔節點存在且為紅色的話,我們就只需要將父節點和叔節點顏色變為黑色,再將祖父節點變為紅色,這樣就解決了這個問題;如果叔節點不存在或者存在且為空節點,那么此時我們就需要做特殊處理。

通過上述分析,我們總結一下紅黑樹的插入過程:

第一步:插入節點。按照二叉搜索樹的規則插入節點,鏈接父節點,將插入節點的顏色改成紅色,如果是根節點,就變為黑色。

第二步:向上調整黑色節點的數量,讓其滿足紅黑樹的規則。

接下來我們逐步來分析向上調整。

根據上面的分析我們知道,我們可以根據uncle節點的有無以及顏色,可以分為下面兩種情況:

情況一:uncle節點存在且顏色為紅色

情況二:uncle節點存在且為黑色或者uncle不存在

如下圖所示:

對于情況一,我們只需要將父節點和uncle節點顏色變為黑色,然后再將祖父節點顏色變為紅色,然后向上更新parent節點和grandfather節點,因此我們需要一個循環,循環結束的條件是:parent為空或者parent的顏色為黑色。

對于情況二,我們發現這種情況又需要分為兩種場景,一種是插入節點是parent的左節點,另一種是插入節點是parent的右節點,這兩種情景無論我們怎么變色都沒有辦法滿足紅黑樹的規則,為此我們需要尋找一種新的解決辦法。

回到紅黑樹的定義,我們知道,紅黑樹是一顆不完全平衡二叉搜索樹,從前面的AVL樹我們知道,要成為一顆平衡樹,我們要做的就是旋轉,而旋轉分為左旋和右旋,那么我們嘗試將AVL樹的旋轉帶入紅黑樹當中,那么對于parent與cur都位于同一側節點的這種情況,我們使用單旋,對于parent與cur位于不同一側節點的這種情況,我們使用雙旋。

綜上所述,紅黑樹的插入可以分為以下三種不同的情況:

變色不旋轉

條件:grandfather為黑,parent與cur為紅色,uncle存在且為紅。

解決辦法:parent與cur變為黑色,grandfather變為紅色,然后再將grandfather當成新的cur,繼續向上更新。

分析:由于cur與parent都是紅色,所以我們應該把parent變為黑色,這樣就在當前節點憑空增加了一個黑色節點,從而導致黑色節點失衡;由于uncle是紅色,為了讓黑色節點數量保持平衡,所以我們需要把uncle也變成黑色,然后讓grandfather變為紅色。

單旋+變色

條件:grandfather為黑色,parent與cur為紅色,uncle不存在或者uncle存在且為黑,并且parent與cur位于同一側

解決辦法:以grandfather節點為旋轉點,如果位于左側則右旋;如果位于右側則左旋,旋轉結束后讓parent變為黑色,grandfather變成紅色

分析:由于uncle是黑色節點,如果我們直接按照第一種情況進行變色,那么就會導致uncle這條路徑上始終少一個節點,這樣導致節點失衡,所以我們要先進行旋轉,旋轉結束后parent成為了cur與grandfather的父節點,為了讓黑色節點數量保持平衡,我們需要讓parent變成黑色,讓grandfather變成紅色

雙旋+變色

條件:grandfather為黑色,parent與cur為紅色,uncle不存在或者uncle存在且為黑,并且parent與cur位于不同側

解決辦法:首先以parent為旋轉點進行一次旋轉,然后再以grandfather為旋轉點進行一次旋轉,旋轉結束后讓cur變成黑色,讓grandfather變成紅色。

分析:由于uncle是黑節點,cur與parent位于相反的兩側,如果我們旋轉一次,結果還是這種情況,所以我們需要進行兩次旋轉,旋轉結束后,為了保持黑色節點數量的平衡,我們需要將cur變成黑色,讓grandfather變成紅色

插入代碼如下所示:

bool Insert(const pair<K, V>& kv)
{if (_root == nullptr){_root = new Node(kv);_root->_col = BLACK;return true;}Node* cur = _root;Node* parent = nullptr;while (cur){if (cur->_kv.first > kv.first){parent = cur;cur = cur->_left;}else if (cur->_kv.first < kv.first){parent = cur;cur = cur->_right;}else{cout << "插入值已經存在,請重新插入" << endl;return false;}}cur = new Node(kv);cur->_col = RED;if (parent->_kv.first < kv.first){parent->_right = cur;}else{parent->_left = cur;}cur->_parent = parent;while (parent && parent->_col == RED){Node* grandfather = parent->_parent;if (grandfather->_left == parent){Node* uncle = grandfather->_right;if (uncle && uncle->_col == RED){parent->_col = uncle->_col = BLACK;grandfather->_col = RED;cur = grandfather;parent = cur->_parent;}else{if (parent->_left == cur){RotateR(grandfather);grandfather->_col = RED;parent->_col = BLACK;}else{RotateL(parent);RotateR(grandfather);cur->_col = BLACK;grandfather->_col = RED;}break;}}else{Node* uncle = grandfather->_left;if (uncle && uncle->_col == RED){parent->_col = uncle->_col = BLACK;grandfather->_col = RED;cur = grandfather;parent = cur->_parent;}else{if (parent->_right == cur){RotateL(grandfather);grandfather->_col = RED;parent->_col = BLACK;}else{RotateR(parent);RotateL(grandfather);cur->_col = BLACK;grandfather->_col = RED;}break;}}}_root->_col = BLACK;return true;
}
void RotateL(Node* parent)
{Node* subR = parent->_right;Node* subRL = subR->_left;parent->_right = subRL;subR->_left = parent;if (subRL)subRL->_parent = parent;Node* grandfather = parent->_parent;parent->_parent = subR;if (_root == parent){_root = subR;subR->_parent = nullptr;}else{if (grandfather->_left == parent){grandfather->_left = subR;}else{grandfather->_right = subR;}subR->_parent = grandfather;}}
void RotateR(Node* parent)
{Node* subL = parent->_left;Node* subLR = subL->_right;parent->_left = subLR;subL->_right = parent;if (subLR)subLR->_parent = parent;Node* grandfather = parent->_parent;parent->_parent = subL;if (_root == parent){_root = subL;subL->_parent = nullptr;}else{if (grandfather->_left == parent){grandfather->_left = subL;}else{grandfather->_right = subL;}subL->_parent = grandfather;}
}

旋轉邏輯在這篇文章介紹:

數據結構(五)——AVL樹(平衡二叉搜索樹)

紅黑樹的查找

紅黑樹的查找與二叉搜索樹相同,都是通過遍歷比較節點值與目標值的大小知道找到目標值所在的節點然后返回或者直到遍歷完這顆樹為止,其代碼如下所示:

Node* Find(const pair<K, V>& kv)
{Node* cur = _root;Node* parent = nullptr;while (cur){if (cur->_kv.first > kv.first){parent = cur;cur = cur->_left;}else if (cur->_kv.first < kv.first){parent = cur;cur = cur->_right;}else{return cur;}}cout << "目標值不存在" << endl;return nullptr;
}

紅黑樹的驗證

前面我們驗證紅黑樹時使用的方法是驗證平衡因子以及左右子樹高度差,實際上我們是按照AVL樹的規則進行驗證的,那么對于紅黑樹而言我們要通過它的規則進行驗證,檢查最長路徑不超過最短路徑的二倍嗎?這樣顯然是不現實的,那么我們是通過什么方法去驗證呢?我們還是通過遞歸去驗證,不同的是我們引入了一個新的參數——參考值,這個值是用來記錄從根節點到空節點這一整條路徑中黑色節點的數量的,我們用這個值作為參考去比較其他路徑上黑色節點的數量是否與這個值相同。其代碼如下所示:

bool Check(Node* root, int blackNum, const int refNum)
{if (root == nullptr){// 前序遍歷?到空時,意味著?條路徑?完了//cout << blackNum << endl;if (refNum != blackNum){cout << "存在??結點的數量不相等的路徑" << endl;return false;}return true;}// 檢查孩?不太?便,因為孩?有兩個,且不?定存在,反過來檢查?親就?便多了if (root->_col == RED && root->_parent->_col == RED){cout << root->_kv.first << "存在連續的紅?結點" << endl;return false;}if (root->_col == BLACK){blackNum++;}return Check(root->_left, blackNum, refNum)&& Check(root->_right, blackNum, refNum);
}
bool IsBalance()
{if (_root == nullptr)return true;if (_root->_col == RED)return false;// 參考值int refNum = 0;Node* cur = _root;while (cur){if (cur->_col == BLACK){++refNum;}cur = cur->_left;}return Check(_root, 0, refNum);
}

紅黑樹代碼如下所示:

enum Colour
{BLACK,RED
};
template<class K, class V>
struct RBTreeNode
{pair<K, V> _kv;RBTreeNode<K, V>* _left;RBTreeNode<K, V>* _right;RBTreeNode<K, V>* _parent;Colour _col;RBTreeNode(const pair<K, V>& kv):_kv(kv), _left(nullptr), _right(nullptr), _parent(nullptr){}
};template<class K, class V>
class RBTree
{typedef RBTreeNode<K, V> Node;
public:bool Insert(const pair<K, V>& kv){if (_root == nullptr){_root = new Node(kv);_root->_col = BLACK;return true;}Node* cur = _root;Node* parent = nullptr;while (cur){if (cur->_kv.first > kv.first){parent = cur;cur = cur->_left;}else if (cur->_kv.first < kv.first){parent = cur;cur = cur->_right;}else{cout << "插入值已經存在,請重新插入" << endl;return false;}}cur = new Node(kv);cur->_col = RED;if (parent->_kv.first < kv.first){parent->_right = cur;}else{parent->_left = cur;}cur->_parent = parent;while (parent && parent->_col == RED){Node* grandfather = parent->_parent;if (grandfather->_left == parent){Node* uncle = grandfather->_right;if (uncle && uncle->_col == RED){parent->_col = uncle->_col = BLACK;grandfather->_col = RED;cur = grandfather;parent = cur->_parent;}else{if (parent->_left == cur){RotateR(grandfather);grandfather->_col = RED;parent->_col = BLACK;}else{RotateL(parent);RotateR(grandfather);cur->_col = BLACK;grandfather->_col = RED;}break;}}else{Node* uncle = grandfather->_left;if (uncle && uncle->_col == RED){parent->_col = uncle->_col = BLACK;grandfather->_col = RED;cur = grandfather;parent = cur->_parent;}else{if (parent->_right == cur){RotateL(grandfather);grandfather->_col = RED;parent->_col = BLACK;}else{RotateR(parent);RotateL(grandfather);cur->_col = BLACK;grandfather->_col = RED;}break;}}}_root->_col = BLACK;return true;}void RotateL(Node* parent){Node* subR = parent->_right;Node* subRL = subR->_left;parent->_right = subRL;subR->_left = parent;if (subRL)subRL->_parent = parent;Node* grandfather = parent->_parent;parent->_parent = subR;if (_root == parent){_root = subR;subR->_parent = nullptr;}else{if (grandfather->_left == parent){grandfather->_left = subR;}else{grandfather->_right = subR;}subR->_parent = grandfather;}}void RotateR(Node* parent){Node* subL = parent->_left;Node* subLR = subL->_right;parent->_left = subLR;subL->_right = parent;if (subLR)subLR->_parent = parent;Node* grandfather = parent->_parent;parent->_parent = subL;if (_root == parent){_root = subL;subL->_parent = nullptr;}else{if (grandfather->_left == parent){grandfather->_left = subL;}else{grandfather->_right = subL;}subL->_parent = grandfather;}}Node* Find(const pair<K, V>& kv){Node* cur = _root;Node* parent = nullptr;while (cur){if (cur->_kv.first > kv.first){parent = cur;cur = cur->_left;}else if (cur->_kv.first < kv.first){parent = cur;cur = cur->_right;}else{return cur;}}cout << "目標值不存在" << endl;return nullptr;}bool Check(Node* root, int blackNum, const int refNum){if (root == nullptr){// 前序遍歷?到空時,意味著?條路徑?完了//cout << blackNum << endl;if (refNum != blackNum){cout << "存在??結點的數量不相等的路徑" << endl;return false;}return true;}// 檢查孩?不太?便,因為孩?有兩個,且不?定存在,反過來檢查?親就?便多了if (root->_col == RED && root->_parent->_col == RED){cout << root->_kv.first << "存在連續的紅?結點" << endl;return false;}if (root->_col == BLACK){blackNum++;}return Check(root->_left, blackNum, refNum)&& Check(root->_right, blackNum, refNum);}bool IsBalance(){if (_root == nullptr)return true;if (_root->_col == RED)return false;// 參考值int refNum = 0;Node* cur = _root;while (cur){if (cur->_col == BLACK){++refNum;}cur = cur->_left;}return Check(_root, 0, refNum);}
private:Node* _root = nullptr;
};

小結

本篇文章我們著重介紹了紅黑樹的概念以及插入邏輯的實現,紅黑樹作為一顆不完全平衡二叉搜索樹有著獨特的地方,它是通過節點的顏色來判斷是否平衡以及是否需要旋轉操作,它省去了頻繁旋轉所帶來的消耗,從而更加追求效率,總的來說,紅黑樹是一顆非常優秀的數據結構

以上就是本篇博客的主要內容,如果對您有所幫助的話,記得點贊、評論、關注加轉發,您的支持就是我創作的最大動力

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

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

相關文章

c# 數據結構 鏈表篇 有關雙向鏈表的一切

本人能力有限,如有不足還請斧正 目錄 0.雙向鏈表的好處 1.雙向鏈表的分類 2.不帶頭節點的標準雙向鏈表 節點類:有頭有尾 鏈表類:也可以有頭有尾 也可以只有頭 增 頭插 尾插 刪 查 改 遍歷 全部代碼 3.循環雙向鏈表 節點類 鏈表類 增 頭插 尾插 刪 查 遍歷…

Numba 從零基礎到實戰:解鎖 Python 性能新境界

Numba 從零基礎到實戰&#xff1a;解鎖 Python 性能新境界 一、引言 在 Python 的世界里&#xff0c;性能一直是一個備受關注的話題。Python 以其簡潔易讀的語法和豐富的庫生態&#xff0c;深受開發者喜愛&#xff0c;但在處理一些計算密集型任務時&#xff0c;其執行速度往往…

單位門戶網站被攻擊后的安全防護策略

政府網站安全現狀與挑戰 近年來&#xff0c;隨著數字化進程的加速&#xff0c;政府門戶網站已成為政務公開和服務公眾的重要窗口。然而&#xff0c;網絡安全形勢卻日益嚴峻。國家互聯網應急中心的數據顯示&#xff0c;政府網站已成為黑客攻擊的重點目標&#xff0c;被篡改和被…

Spring Boot 項目三種打印日志的方法詳解。Logger,log,logger 解讀。

目錄 一. 打印日志的常見三種方法&#xff1f; 1.1 手動創建 Logger 對象&#xff08;基于SLF4J API&#xff09; 1.2 使用 Lombok 插件的 Slf4j 注解 1.3 使用 Spring 的 Log 接口&#xff08;使用頻率較低&#xff09; 二. 常見的 Logger&#xff0c;logger&#xff0c;…

NI的LABVIEW工具安裝及卸載步驟說明

一.介紹 最近接到個轉交的項目&#xff0c;項目主要作為上位機工具開發&#xff0c;在對接下位機時&#xff0c;有用到NI的labview工具。labview軟件是由美國國家儀器&#xff08;NI&#xff09;公司研制開發的一種程序開發環境&#xff0c;主要用于汽車測試、數據采集、芯片測…

cmd 終端輸出亂碼問題 |Visual Studio 控制臺輸出中文亂碼解決

在網上下載&#xff0c;或者移植別人的代碼到自己的電腦&#xff0c;使用VS運行后&#xff0c;控制臺輸出中文可能出現亂碼。這是因為源代碼的編碼格式和控制臺的編碼格式不一致。 文章目錄 查看源代碼文件編碼格式查看輸出控制臺編碼格式修改編碼格式修改終端代碼頁 補充總結 …

A009-基于pytest的網易云自動化測試

題 目 :基于pytest的網易云自動化測試 主要內容 綜合應用所學的軟件測試理論和方法,實現網易云的功能自動化測試。 (1)自動化測試介紹; (2)自動化功能測試框架介紹; (3)設計功能測試用例 (4)書寫自動化測試腳本; (5)測試評價與結論。 任務要求 (1)能…

LVGL Video控件和Radiobtn控件詳解

LVGL Video控件和Radiobtn控件詳解 一、 Video控件詳解1. 概述2. 創建和初始化3. 基本屬性設置4. 視頻控制5. 回調函數6. 高級功能7. 注意事項 二、Radiobtn控件詳解1. 概述2. 創建和初始化3. 屬性設置4. 狀態控制5. 組管理6. 事件處理7. 樣式設置8. 注意事項 三、效果展示四、…

AbortController:讓異步操作隨時說停就停

AbortController&#xff1a;讓異步操作隨時說停就停 一、什么是 AbortController&#xff1f; AbortController 是 JavaScript 在瀏覽器和部分 Node.js 環境中提供的全局類&#xff0c;用來中止正在進行或待完成的異步操作&#xff08;如 fetch() 請求、事件監聽、可寫流、數…

機器學習 從入門到精通 day_04

1. 決策樹-分類 1.1 概念 1. 決策節點 通過條件判斷而進行分支選擇的節點。如&#xff1a;將某個樣本中的屬性值(特征值)與決策節點上的值進行比較&#xff0c;從而判斷它的流向。 2. 葉子節點 沒有子節點的節點&#xff0c;表示最終的決策結果。 3. 決策樹的…

C++ Primer (第五版)-第十三章 拷貝控制

文章目錄 概述13.1拷貝、賦值與銷毀合成拷貝構造函數拷貝初始化參數和返回值拷貝初始化的限制編譯器可以繞過拷貝構造函數拷貝運算符析構函數三/五原則使用default阻止拷貝合成的拷貝控制成員可能是刪除的 private拷貝控制拷貝控制和資源管理行為像值的類類值拷貝賦值運算符定義…

Vue el-from的el-form-item v-for循環表單如何校驗rules(一)

實際業務需求場景&#xff1a; 新增或編輯頁面&#xff08;基礎信息表單&#xff0c;一個數據列表的表單&#xff09;&#xff0c;數據列表里面的表單數是動態添加的。數據可新增、可刪除&#xff0c;在表單保存前&#xff0c;常常需要做表單必填項的校驗&#xff0c;校驗通過以…

測試100問:http和https的區別是什么?

哈嘍&#xff0c;大家好&#xff0c;我是十二&#xff0c;今天給大家分享的問題是&#xff1a;http和https的區別是什么&#xff1f; 首先我們要知道 HTTP 協議傳播的數據都是未加密的&#xff0c;也就是明文的&#xff0c;因此呢使用 http協議傳輸一些隱私信息也就非常不安全&…

YOLOv3超詳細解讀(三):源碼解析:數據處理模塊

一、概述 YOLOv3&#xff08;You Only Look Once v3&#xff09;是一種高效的目標檢測算法&#xff0c;其數據處理模塊是訓練和推理流程的核心部分。本文將深入分析Ultralytics團隊基于PyTorch實現的YOLOv3源碼中的數據處理模塊&#xff0c;重點探討數據加載、預處理和數據增強…

每日算法(雙指針算法)(Day 1)

雙指針算法 1.算法題目&#xff08;移動零&#xff09;2.講解算法原理3.編寫代碼 1.算法題目&#xff08;移動零&#xff09; 2.講解算法原理 數組劃分&#xff0c;數組分塊&#xff08;快排里面最核心的一步&#xff09;只需把0改為tmp 雙指針算法&#xff1a;利用數組下標來…

2025藍橋杯python A組省賽 題解

真捐款去了&#xff0c;好長時間沒練了&#xff0c;感覺腦子和手都不轉悠了。 B F BF BF 賽時都寫假了&#xff0c; G G G 也只寫了爆搜。 題解其實隊友都寫好了&#xff0c;我就粘一下自己的代碼&#xff0c;稍微提點個人的理解水一篇題解 隊友題解 2025藍橋杯C A組省賽 題…

測試基礎筆記第四天(html)

提示&#xff1a;文章寫完后&#xff0c;目錄可以自動生成&#xff0c;如何生成可參考右邊的幫助文檔 文章目錄 html介紹1. 介紹2.骨架標簽3.常用標簽標題標簽段落標簽超鏈接標簽圖片標簽換行和空格標簽布局標簽input標簽&#xff08;變形金剛&#xff09;form標簽列表標簽 htm…

10 穴 汽車連接器的15個設計特點

汽車行業嚴重依賴卓越的電氣系統來確保功能和可靠性。這些系統的關鍵組件是 10 腔連接器&#xff0c;它為布線和信號傳輸提供解決方案。制造商和工程師必須仔細評估這些連接器的設計特性&#xff0c;以優化性能和安全性。 本博客研究了汽車 10 腔連接器的 15 個設計特征&#…

Summary

一、數據結構 1.1 哈希 主要是HashMap和HashSet&#xff1b;其中HashSet底層是一個HashMap屬性。 // 獲取HashMap元素,HashSet均不支持 map.keySet (); // Set<k> map.values (; // Collection<V> map.entrySet();//Set<Map.Entry<K,V>> for (Map.E…

【Leetcode-Hot100】最小覆蓋子串

題目 解答 想到使用雙指針哈希表來實現&#xff0c;雙指針的left和right控制實現可滿足字符串。 class Solution(object):def minWindow(self, s, t):""":type s: str:type t: str:rtype: str"""len_s, len_t len(s), len(t)hash_map {}for…