紅黑樹介紹

1 問題引入

為什么有AVL樹,還要引入紅黑樹?
在進行多次的插入和刪除時:
??????? 1)AVL樹會存在大量的旋轉操作,追求的是嚴格平衡;
??????? 2)紅黑樹通過為節點增加顏色來換取增刪節點時旋轉次數的降低,任何不平衡都會在三次旋轉之內解決,追求的是部分平衡;
因此,在增刪節點時,根據不同的情況,AVL樹旋轉的次數比紅黑樹多。

2 紅黑樹的概念

1)是一種二叉搜索樹,但在每個節點上增加一個存儲位表示節點的顏色(紅色/黑色);
2)通過對任何一條從根到葉子的路徑上各個結點著色方式的限制,紅黑樹確保沒有一條路徑會比其他路徑長出倆倍,因而是接近平衡。即最長路徑不會超過最短路徑的2倍;比如,假設最短路徑是h,最長就是2h,其他路徑的長度則為[h,2h]。

2.1 紅黑樹的性質

1)每個節點不是紅色就是黑色;
2)根節點是黑色的;
3)如果一個節點是紅色的,則它的倆個孩子節點必須是黑色的。即沒有連續的紅色節點;但是并沒有說黑色節點的孩子必須是紅色節點;
4)對于每個節點,從該節點到其所有后代葉節點的簡單路徑上,均包含相同數目的黑色節點。即每條路徑都會包含相同數量的黑色節點;
5)每個葉子結點都是黑色的(此處的葉子結點指的是空節點,即NIL節點),避免數錯了路徑個數(路徑:從根走到NIL節點)。

所以我們可以根據上面的性質思考一下,為什么滿足上面的性質,紅黑樹就能保證:從根結點到NULL節點,其最長路徑中節點個數不會超過最短路徑節點個數的倆倍?

?

在極端場景下我們可以進行分析:

整體紅黑樹圖,此時滿足紅黑樹的性質:

?

最短路徑:全黑

?最長路徑:一黑一紅

?

2.2 AVL樹與紅黑樹的比較

1)AVL樹的高度接近log2N;
2)紅黑樹的高度,如果是從最短路徑看,則是全黑,相當于一個滿二叉樹;

?

而滿二叉樹的高度計算如下:
???????????????????????????????? N=2h?1 --》 h=log2(N+1) ≈ log2N;
那么我們可以知道,最長路徑(一黑一紅),則是2* log2N;
我們可以舉一個實際的例子:
假如有10億個數,那么log2N=10億 --》N=30;
AVL樹:需要尋找30次;
紅黑樹:需要尋找60次;
雖然紅黑樹尋找的次數比AVL數多,但是對于現有的cpu來說,并沒有太大的影響。

總結:
1)從查找效率來說,AVL樹與紅黑樹相差不大;
2)從增刪操作來說,AVL樹需要通過多次旋轉來降低高度來保持嚴格平衡,但是旋轉是有代價的。而紅黑樹并不是這樣,只是近似平衡,從而減少了旋轉的次數。
總的來說,紅黑樹的效率要比AVL樹好

3 紅黑樹的定義及插入操作討論

3.1 紅黑樹的定義

enum Colour
{RED,BLACK
};template<class K,class V>
struct RBTreeNode
{RBTreeNode<K, V>* _left;RBTreeNode<K, V>* _right;RBTreeNode<K, V>* _parent;pair<K, V> _kv;Colour _col;RBTreeNode(const pair<K,V>& kv):_left(nullptr),_right(nullptr),_parent(nullptr),_kv(kv),_col(RED)//新增節點給為紅色{}
};

3.2 紅黑樹的插入操作討論

紅黑樹也是一顆二叉搜索樹,所以當進行插入操作的時候我們也可以通過之前所學系的AVL樹的插入操作進行實行。 但是在插入過程中我們得遵循紅黑樹的性質,因此,我們對其進行了一些討論,如下:

首先,我們先定義一個紅黑樹操作的框架:

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* 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 (cur->_kv.first < parent->_kv.first){parent->_left = cur;cur->_parent = parent;}else{parent->_right = cur;cur->_parent = parent;}//檢驗是否滿足紅黑樹的性質,所以在此進行討論}
private:Node* _root;
};
3.2.1 實施過程中的問題

根節點是黑色,這是紅黑樹的性質要求的。那么新插入結點開始為什么要是紅色?

因為根據紅黑樹的性質要求,每條路徑的黑色節點的數量都是相同的,那么如果插入結點是黑色的話,那么其他路徑就都少了一個黑色節點,這樣調整起來工程量較大,十分麻煩。所以插入結點顏色一定是紅色。但是如果插入節點的父親節點是黑色,那是沒有問題的。若插入結點的父親節點是紅色的,那么就會違反紅黑樹的性質(沒有連續的紅色節點),那就需要進行處理。

3.2.2 對于叔叔節點(u)的討論

接下來,我們插入紅色節點,將父親節點設定為紅色,從而破壞了紅黑樹(沒有連續的紅色節點)的性質。 因為爺爺節點(g)、父親節點(p)和當前節點(cur)都是固定的,所以下面我們將要對叔叔節點(u)進行討論。
假設 :
①g(grandfather):爺爺節點
②p(parent):父親節點
③u(uncle):叔叔節點
④cur(current):當前節點

1)情形一 :叔叔節點(u)存在且為紅色

g = 黑色,p = 紅色,u = 紅色 ,cur = 紅色。

?問題說明:
①孩子節點(cur)是紅色的,父親節點(p)是紅色的,爺爺節點(g)是黑色的,此時叔叔節點是我們的關鍵。
②根據紅黑樹的性質,倆個紅色不能相連,所以我們需要將父親節點(p)變為黑色,但是父親節點(p)變黑,那么叔叔節點(u)就少了一個黑色。
此時我們將爺爺節點(g)變為紅色。

?③但是,此時叔叔節點(u)的路徑就少了一個黑色節點。因此,我們將叔叔節點(u)變為黑色。

?④爺爺節點(g)是否為根節點?
????????1)若爺爺節點(g)是根節點,則將爺爺節點(g)變為黑色。

????????2)若爺爺節點(g)是子樹,爺爺節點(g)一定有父親節點,且爺爺節點(g)的父親如果是紅色,則需要繼續向上調整。?

?以上為第一種情況的抽象圖,下面我們對具體的圖形進行解析:

①a/b/c/d/e為空樹

②c/d/e是下面4個子樹中的一種(一個黑色節點):

?從上我們可以看一下共有多少種情況:
c/d/e為上面4種中的任意一種:4×4×4=64種
插入位置共有4中位置
所以此紅黑樹共有64×4=256種情況。

總結(情況一(叔叔節點(u)存在且為紅)的解決方案):

①p/u變為黑色,g變為紅色;
②如果g為根,再把g變為黑色;
③若g不為根,則繼續往上處理(g可以當做現在的cur);
④p/u是g的左或者右沒有影響,cur是p的左或者右也沒有影響,處理方式是相同的。

2)情況二:叔叔節點(u)不存在/叔叔節點(u)存在且為黑

①叔叔節點(u)不存在,則當前節點(cur)就是新增,因為如果cur不是新增節點,則cur和p一定有一個節點的顏色是黑色,就不滿足每條路徑黑色節點個數相同的性質。

?②叔叔節點(u)存在,則一定是黑色。(因為改為叔叔節點(u)存在,且為紅色的情況我們已經分析了),那么當前節點(cur)則必定為黑色。 第二步中的cur節點為紅色是因為在調整過程中由黑色變為了紅色。

?總結(情況二:叔叔節點(u)不存在/叔叔節點(u)存在且為黑的解決方案):

①p為g的左孩子,cur為p的左孩子,則進行右單旋;
②p為g的右孩子,cur為p的右孩子,則進行左單旋;
③p、g變色:p變為黑色,g變為紅色。

3)情況三:叔叔節點(u)不存在/叔叔節點(u)存在且為黑

雖然情況三和情況二的條件是一樣的,但是cur和p不在同一側。

1)叔叔節點(u)不存在,cur為新增節點

?2)叔叔節點(u)存在且為黑

?

總結(情況三:叔叔節點(u)不存在/叔叔節點(u)存在且為黑的解決方案):

1)p為g的左孩子,cur為p的右孩子,則進行左右雙旋+變色;
2)p為g的右孩子,cur為p的左孩子,則進行右左雙旋+變色;
3)g、cur變色:g變為紅色,cur變為黑色。

3.3 插入操作完整代碼

bool Insert(const pair<K,V>& kv){if (_root == nullptr){_root = new Node(kv);//但是要注意的是紅黑樹的根是黑色的_root->_col = BLACK;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 (cur->_kv.first < parent->_kv.first){parent->_left = cur;cur->_parent = parent;}else{parent->_right = cur;cur->_parent = parent;}//檢驗是否滿足紅黑樹的性質,所以在此進行討論//1.此時cur為紅,parent為紅,此時就違反了紅黑樹的性質//8.繼續向上處理的判斷是parent存在且為紅色的情況,所以記性增加parent存在的情況while (parent && parent->_col == RED){//3.插入之前可以認為是紅黑樹,cur為紅,parent為紅,則肯定有爺爺節點Node* grandfather = parent->_parent;//4.此時,cur、parent、grandfather是固定的,則需要找uncle節點,從而看如何變化使其不違反紅黑樹性質if (parent == grandfather->_left){Node* uncle = grandfather->_right;//5.情況一:叔叔節點uncle存在且為紅if (uncle && uncle->_col == RED){parent->_col = BLACK;uncle->_col = BLACK;grandfather->_col = RED;//6.繼續往上處理cur = grandfather;parent = cur->_parent;//7.第一種情況如果g為根,則將grandfather的顏色變為黑色,這個可以在循環外進行處理//如果parent->_colour == 黑色,則無需進行處理//所以我們需要進行處理的是parent->_colour == 紅色,所以我們可以在while循環處進行下一次向上處理判斷}else //8.情況二:叔叔節點uncle不存在/為黑色,但是此時不需要考慮uncle節點,因為旋轉和變色都與其無關{//9.上面滿足了p為g的左孩子,此時應滿足cur是p的左孩子條件if (cur == parent->_left){RotalR(grandfather);parent->_col = BLACK;grandfather->_col = RED;}//10.情況三:情況三和情況二的條件是一樣的,但是cur和p不在同一側。//此時要求cur是p的右孩子情況else{RotalLR(grandfather);grandfather->_col = RED;cur->_col = BLACK;}//11.如果是雙旋,那么因為parent是紅色,不能通過while循環判斷,并且也不會違反規則了,所以可以直接break;break;}}else{Node* uncle = grandfather->_left;//5.情況一:叔叔節點uncle存在且為紅if (uncle && uncle->_col == RED){parent->_col = BLACK;uncle->_col = BLACK;grandfather->_col = RED;//6.繼續往上處理cur = grandfather;parent = cur->_parent;//7.第一種情況如果g為根,則將grandfather的顏色變為黑色,這個可以在循環外進行處理//如果parent->_colour == 黑色,則無需進行處理//所以我們需要進行處理的是parent->_colour == 紅色,所以我們可以在while循環處進行下一次向上處理判斷}else //8.情況二:叔叔節點uncle不存在/為黑色,但是此時不需要考慮uncle節點,因為旋轉和變色都與其無關{//9.上面滿足了p為g的右孩子,此時應滿足cur是p的右孩子條件if (cur == parent->_right){RotalL(grandfather);parent->_col = BLACK;grandfather->_col = RED;}//10.情況三:情況三和情況二的條件是一樣的,但是cur和p不在同一側。//此時要求cur是p的右孩子情況else{RotalRL(grandfather);grandfather->_col = RED;cur->_col = BLACK;}//11.如果是雙旋,那么因為parent是紅色,不能通過while循環判斷,并且也不會違反規則了,所以可以直接break;break;}}}//7.如果grandfather是根,則可以再次進行處理_root->_col = BLACK;//2.如果parent不是紅色,則說明沒有問題return true;}//旋轉部分:與AVL樹是一樣的,只是減少了平衡因子//左單旋void RotalL(Node * parent){//定義階段Node* subR = parent->_right;Node* subRL = subR->_left;//更改階段parent->_right = subRL;if (subRL)//子樹可能是空{subRL->_parent = parent;}//先記錄parent的父結點,因為parent可能是一個子樹,也可能是一個根Node* ppnode = parent->_parent;subR->_left = parent;parent->_parent = subR;//如果是根if (parent == _root){_root = subR;subR->_parent = nullptr;}else//parent不是根,那么parent可能是父結點的左節點還是右結點{if (parent == ppnode->_left){ppnode->_left = subR;}else{ppnode->_right = subR;}subR->_parent = ppnode;}}//右單旋void RotalR(Node* parent){//1.定義節點//2.更改階段//3.注意子樹可能為空//4.parent是否為根//5.更新平衡因子Node* subL = parent->_left;Node* subLR = subL->_right;//更改階段parent->_left = subLR;if (subLR){subLR->_parent = parent;}Node* ppnode = parent->_parent;subL->_right = parent;parent->_parent = subL;if (parent == _root){_root = subL;subL->_parent = nullptr;}else{if (parent == ppnode->_left){ppnode->_left = subL;}else{ppnode->_right = subL;}subL->_parent = ppnode;}}//左右旋void RotalLR(Node* parent){Node* subL = parent->_left;Node* subLR = subL->_right;//進行旋轉RotalL(parent->_left);//以30為旋轉點進行旋轉RotalR(parent);//以90為旋轉點進行旋轉}//右左旋void RotalRL(Node* parent){Node* subR = parent->_right;Node* subRL = subR->_left;//先進行右旋轉,再左旋轉RotalR(subR);RotalL(parent);}

4 判斷是否為紅黑樹

如果讓你判斷一棵樹是否為紅黑樹,你會如何判斷?
1)A同學:紅黑樹中有這樣一條概念:最長路徑不會超過最短路徑的2倍,能否使用這個概念來進行判斷?
是不可以的,因為如果這顆樹的確滿足最長路徑不超過最短路徑2倍這個概念,但是如果路徑的顏色如果不滿足紅黑樹的性質,那么這棵樹顯而易見也不是紅黑樹。所以這個思路是不可行的。

2)B同學:我們可以通過紅黑樹的性質來進行判斷這顆樹是一顆紅黑樹?
是可以的,因為若滿足紅黑樹的性質,則可以判斷其是一顆紅黑樹。并且性質中的每條路徑的黑色節點數量相同,則可以限制最長路徑不會超過最短路徑倆倍這個概念。

判斷一顆樹是否為紅黑樹,我們主要滿足紅黑樹性質的2、3、4條性質。
其中第2條性質很好判斷:

//性質2:根節點是黑色的if (_root->_col != BLACK){return false;}

性質3卻存在一個小問題:如果一個節點是紅色的,則它的倆個孩子節點必須是黑色的。
那么我們是那當前節點和它的孩子節點比較還是和它的父親節點進行比較? 如果是孩子節點,那么我們首先需要判斷它是否有孩子節點,其次,需判斷是它的左孩子還是右孩子,這樣寫起來,代碼就顯得比較繁瑣復雜。
因此,我們換種思路,如果當前節點和其父親節點進行比較,那么該節點只有一個父親節點,并且這樣判斷也可以滿足不能有連續紅色節點的性質,所以這樣是比較優秀的解法。

//檢查是否滿足性質3:沒有連續的紅色節點if (cur->_col == RED && cur->_parent->_col == RED){return false;}

但是性質4確實有些難度:
首先,我們可以使用遞歸來檢驗這棵樹。?

bool check(Node* cur)
{if(cur == nullptr){return true;}return check(cur->_left) && check(cur->_right);
}
bool IsRBTree()
{if(_root == nullptr){return true;}check(_root);
}

其次,我們可以將上面的我們寫好的性質的代碼添加進去。

bool check(Node* cur)
{if(cur == nullptr){return true;}//檢查是否滿足性質3:沒有連續的紅色節點if (cur->_col == RED && cur->_parent->_col == RED){return false;}return check(cur->_left) && check(cur->_right);
}
bool IsRBTree()
{if(_root == nullptr){return true;}//性質2:根節點是黑色的if (_root->_col != BLACK){return false;}check(_root);
}

最后,我們只需要滿足性質4:每條路徑有相同數量的黑色節點即可。

?

此時有這樣一個思路:

1)首先我們計算一條路徑中的黑色節點數量作為一個參考值;
2)遞歸每條路徑并計算黑色節點的數量與這個參考值進行比較,若相等則正確,不相等則報錯。

bool check(Node* cur,int BlackNum,int RefNum){if (cur == nullptr){//到達葉子結點時進行判定黑色節點數量與算出來的數量是否相同//檢查是否滿足性質4if (BlackNum != RefNum){return false;}return true;}//檢查是否滿足性質3:沒有連續的紅色節點if (cur->_col == RED && cur->_parent->_col == RED){return false;}//如果當前節點cur為黑色節點,則黑色節點數量++if (cur->_col == BLACK){BlackNum++;}//該節點的左節點和右結點繼續遞歸,沒有加&,則表示每一層多少個,不受下一層的影響return check(cur->_left, BlackNum, RefNum) && check(cur->_right,BlackNum,RefNum);}bool IsRBTree(){if (_root == nullptr){return true;}//性質2:根節點是黑色的if (_root->_col != BLACK){return false;}//因為每條路徑的黑色節點的個數是相同的//所以我們可以設置一個參考值int RefNum = 0;Node* left = _root;while (left){//如果節點的顏色為黑色,則++if (left->_col == BLACK){RefNum++;}left = left->_left;}//通過check()進行檢查return check(_root,0,RefNum);}

我們進行驗證這個紅黑樹是否正確:

int main()
{RBTree<int,int> r1;int a[] = { 3,2,4,5,6,7,8,10,9,1 };for (auto e : a){r1.Insert(make_pair(e,e));}r1.Inorder();cout << r1.IsRBTree() << endl;
}

?驗證正確。

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

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

相關文章

Java基礎:枚舉類enum入門案例

1.基礎枚舉定義與使用&#xff1a; package com.zxy;public class Main {public static void main(String[] args) { // 獲取枚舉值cars car cars.BMW;switch (car){case BMW :System.out.println("BMW");break;case BENZ :System.out.println("BENZ&…

torch numpy sort排序出現索引順序不正常

問題 torch 調用numpy ,numpy 的sort排序值的順序沒發現問題&#xff0c;但是排序的索引argsort()是有明顯問題。 方案 多一次取索引 arr.argsort().argsort()參考&#xff1a; https://blog.csdn.net/qq_50571974/article/details/123173118

大模型架構記錄5-向量數據庫

一 倒排索引、KNN、PQ 1.1 基礎版本 query -> requery 對問題做處理&#xff0c;處理上下文 對query 做 refined query 1.2 向量數據庫 二 搜索邏輯 2.1 knn 2.2 近似KNN 先和N個空間的均值比較再和空間內部的所有點比較&#xff0c;計算最近值。 優化一&#xff1a; …

Python :數據模型

一. 什么是數據模型&#xff1f; Python數據模型是Python對象系統的抽象&#xff0c;通過一組特殊方法?&#xff08;如__init__、__len__等&#xff09;和協議?&#xff08;如迭代協議、上下文管理協議&#xff09;&#xff0c;定義了對象如何與語言的內置功能&#xff08;如…

考研數學非數競賽復習之Stolz定理求解數列極限

在非數類大學生數學競賽中&#xff0c;Stolz定理作為一種強大的工具&#xff0c;經常被用來解決和式數列極限的問題&#xff0c;也被譽為離散版的’洛必達’方法&#xff0c;它提供了一種簡潔而有效的方法&#xff0c;使得原本復雜繁瑣的極限計算過程變得直觀明了。本文&#x…

電商平臺數據高效集成:旺店通旗艦版到MySQL方案解析

旺店通旗艦版-商品信息查詢到MySQL的高效數據集成方案 在現代電商平臺中&#xff0c;數據的高效集成和處理是業務成功的關鍵。本文將分享一個實際案例&#xff0c;展示如何通過輕易云數據集成平臺&#xff0c;將旺店通旗艦奇門的數據無縫對接到MySQL數據庫&#xff0c;實現商品…

ACL初級總結

ACL–訪問控制列表 1.訪問控制 在路由器流量流入或者流出的接口上,匹配流量,然后執行相應動作 permit允許 deny拒絕 2.抓取感興趣流 3.ACL匹配規則 自上而下逐一匹配,若匹配到了則按照對應規則執行動作,而不再向下繼續匹配 思科:ACL列表末尾隱含一條拒絕所有的規則 華為:AC…

【微知】plantuml在泳道圖中如何將多個泳道框起來分組并且設置顏色?(box “瀏覽器“ #LightGreen endbox)

泳道分組并且著色 分組用 box和endbox &#xff0c;顏色用#xxx&#xff0c;標注用"xxx" box "瀏覽器" #LightGreen participant "瀏覽器1" as Browser participant "瀏覽器2" as Browser2 endboxparticipant "服務端" as …

C語言:計算并輸出三個整數的最大值 并對三個數排序

這是《C語言程序設計》73頁的思考題。下面分享自己的思路和代碼 思路&#xff1a; 代碼&#xff1a; #include <stdio.h> int main() {int a,b,c,max,min,mid ; //設置大中小的數分別為max&#xff0c;mid&#xff0c;min&#xff0c;abc為輸入的三個數printf("ple…

【PyMySQL】Python操作MySQL

1、安裝pymysql pip install pymysql2、導包 import pymysql3、連接MySQL數據庫 db pymysql.connect(hostlocalhost # 本地localhost&#xff0c;或服務器IP地址,userroot # 用戶名,passwordpassword # 密碼,databasemysql) #數據庫名4、創建游標 cursor db.cursor()5、增…

藍橋備賽(18)- 紅黑樹和 set 與 map(上)

對于二叉搜索樹 &#xff0c; 平衡二叉樹 &#xff0c; 以及紅黑樹 &#xff0c; 目前只需要了解背后的原理 &#xff0c; 不做代碼實現的要求 &#xff0c; 重要的就是了解各種操作的時間復雜度即可 &#xff0c; 為set 與 map 做鋪墊 一、二叉搜索樹 1.1 基本概念 相較與于堆…

【Synchronized】不同的使用場景和案例

【Synchronized】不同的使用場景和案例 【一】鎖的作用范圍與鎖對象【1】實例方法&#xff08;對象鎖&#xff09;【2】靜態方法&#xff08;類鎖&#xff09;【3】代碼塊&#xff08;顯式指定鎖對象&#xff09;【4】類鎖&#xff08;通過Class對象顯式鎖定&#xff09; 【二】…

大模型在原發性急性閉角型青光眼預測及治療方案制定中的應用研究報告

目錄 一、引言 1.1 研究背景與意義 1.2 研究目的與方法 1.3 國內外研究現狀 二、原發性急性閉角型青光眼概述 2.1 疾病定義與分類 2.2 發病機制與危險因素 2.3 癥狀與診斷方法 三、大模型在原發性急性閉角型青光眼預測中的應用 3.1 大模型原理與優勢 3.2 術前風險預…

【藍橋杯—單片機】第十五屆省賽真題代碼題解析 | 思路整理

第十五屆省賽真題代碼題解析 前言賽題代碼思路筆記競賽板配置建立模板明確基本要求顯示功能部分頻率界面正常顯示高位熄滅 參數界面基礎寫法&#xff1a;兩個界面分開來寫優化寫法&#xff1a;兩個界面合一起寫 時間界面回顯界面校準校準過程校準錯誤顯示 DAC輸出部分按鍵功能部…

Vue3實戰學習(Vue3快速搭建后臺管理系統(網頁頭部、側邊導航欄、主體數據展示區的設計與實現)(超詳細))(9)

目錄 一、Vue3工程環境配置、項目基礎腳手架搭建、Vue3基礎語法、Vue3集成Element-Plus的詳細教程。(博客鏈接如下) 二、Vue3集成Element-Plus詳細教程。(博客鏈接如下) 三、Vue3集成Vue-Router詳細教程。(博客鏈接如下) 四、Vue3快速搭建后臺管理系統。(實戰學習) &#xff08…

halcon機器人視覺(四)calibrate_hand_eye_stationary_3d_sensor

目錄 一、準備數據和模型二、按照表面匹配的的結果進行手眼標定三、根據標定結果計算CalObjInCamPose一、準備數據和模型 1、讀3D模型:read_object_model_3d 2、創建表面匹配模板:create_surface_model 3、創建一個HALCON校準數據模型:create_calib_data read_object_mode…

【菜鳥飛】通過vsCode用python訪問deepseek-r1等模型

目標 通過vsCode用python訪問deepseek。 環境準備 沒有環境的&#xff0c;vscode環境準備請參考之前的文章&#xff0c;另外需安裝ollama&#xff1a; 【菜鳥飛】用vsCode搭建python運行環境-CSDN博客 AI入門1&#xff1a;AI模型管家婆ollama的安裝和使用-CSDN博客 選讀文章…

vue中,watch里,this為undefined的兩種解決辦法

提示&#xff1a;vue中&#xff0c;watch里&#xff0c;this為undefined的兩種解決辦法 文章目錄 [TOC](文章目錄) 前言一、問題二、方法1——使用function函數代替箭頭函數()>{}三、方法2——使用that總結 前言 ?????盡量使用方法1——使用function函數代替箭頭函數()…

【如何使用云服務器與API搭建專屬聊天系統:寶塔面板 + Openwebui 完整教程】

文章目錄 不挑電腦、不用技術&#xff0c;云服務器 API 輕松搭建專屬聊天系統&#xff0c;對接 200 模型&#xff0c;數據全在自己服務器&#xff0c;安全超高一、前置準備&#xff1a;3 分鐘快速上手指南云服務器準備相關賬號注冊 二、手把手部署教程&#xff08;含代碼塊&a…

使用 PresentMon 獲取屏幕幀率

PresentMon是一個用于捕獲和分析Windows上圖形應用程序高性能特性的工具集,最初由GameTechDev開發,現由英特爾維護和推廣。PresentMon能夠追蹤關鍵性能指標,如CPU、GPU和顯示器的幀持續時間和延遲等,并支持多種圖形API(如DirectX、OpenGL和Vulkan)以及不同的硬件配置和桌…