C++:set和map的底層封裝模擬實現

目錄

底層對比:

底層紅黑樹結構和set、map:?

底層模擬:

傳值調用:

迭代器:

operator ++()

?find函數

operator()?、仿函數?

set和map的仿函數 :

圖解:?

insert函數:

構造函數,析構函數:?

?析構函數:

拷貝構造函數:?

賦值函數:?

map的封裝:

set的封裝:?

紅黑樹的修改:?



底層對比:

上圖以kv模型為例?

通過底層可以看出:

  • set和map的底層調用都是調用了紅黑樹 rb_tree
  • set和map在底層最大的區別就是value的不同,set的value仍然是key類型的,但是map的value是pair<const key,T>類型的
  • 也因為類型的不同,二者使用的template 類模板也相差了一個class T ,而這個class T表示的其實就是map的value中的second的數值類型
  • 同時因為value的不同,所以導致了map和set在紅黑樹中存儲的數據也并不相同

底層紅黑樹結構和set、map:?

底層模擬:

傳值調用:

迭代器:

  • 不論是set還是map 的迭代器,關鍵得是看紅黑樹的迭代器
  • 樹的內部迭代器其實就是和鏈表相差不多,需要進行一個內部的重載調用
  • 也就是說 operator-> operator * operator ++ operator --都是需要進行內部重載,然后進行封裝的!
 RBTree.htemplate<class T, class Ref, class Ptr>
struct __RBTreeIterator
{typedef RBTreeNode<T> Node;typedef __RBTreeIterator<T, Ref, Ptr> Self;Node* _node;__RBTreeIterator(Node* node):_node(node){}Ref operator*(){return _node->_data;}Ptr operator->(){return &_node->_data;}bool operator!=(const Self& s){return _node != s._node;}Self& operator++(){if (_node->_right){// 下一個,右樹最左節點Node* leftMin = _node->_right;while (leftMin->_left){leftMin = leftMin->_left;}_node = leftMin;}else{// 下一個,孩子等于父親左的那個祖先Node* cur = _node;Node* parent = cur->_parent;while (parent && cur == parent->_right){cur = parent;parent = parent->_parent;}_node = parent;}return *this;}
};

operator ++()

是實現迭代器的難點之一,因為紅黑樹的本質其實是二叉搜索樹,所以遍歷是使用中序遍歷,所以++和--都是使用 中序遍歷的節點,如下圖所示,it所在位置的下一個節點是15?

?首先我們需要知道,中序遍歷的過程是 左子樹 根節點 右子樹 所以可以通過下圖右可以得出:

  • 當前it所在的位置是它父親節點的左節點,那么下一個需要遍歷的節點就是it的父親節點
  • 當前節點的右子樹是空的,那么表示這個節點的右子樹結束遍歷,下一個需要遍歷和++遞達的節點,應該往上進行查找祖先節點
  • 如果當前節點的右子樹是空的,且當前節點是其父親節點的右節點,那么表示要去父親節點更往上的方向尋找下一個需要遍歷的節點
  • 如果當前節點的右子樹是空的/遍歷完了,且當前節點是其父親節點的左節點,那么表示當前節點結束,返回父親節點且下一個訪問的節點就是父親節點,且同時訪問完后需要取父親節點的右子樹中進行遍歷和尋找下一個++位置的節點
  • 最后一個單獨的處理,那就是走到最后一個節點的時候,走到最后一個節點的右子樹是空的,就一直返回到根節點,這時候就需要給迭代器置空了!

?

  • ++的下一個節點是該節點右子樹中的最左節點!
  • leftMin = node->right 表示進入右子樹中尋找最左節點,當然現在的條件是右子樹存在,如果右子樹存在就是上面,如果不存在就需要往上邊尋找,直到找到某個節點是它父親節點的左節點為止,或者說找到的某個節點,他沒有父親節點它是根節點為止!

  • 要求當前的節點是他父親節點的右節點,如果一直是這樣就需要一直往上走,直到當前節點是它父親節點的左節點時,返回父親節點
  • 且需要注意如果當前節點是根節點,那么它的父親就是空的,空的不能再繼續往上尋找了,所以直接跳出循環,最后返回父親節點!
RBTree.h
//紅黑樹中調動迭代器的函數方法
typedef __RBTreeIterator<T, T&, T*> Iterator;
typedef __RBTreeIterator<T, const T&, const T*> ConstIterator;Iterator Begin(){Node* leftMin = _root;while (leftMin && leftMin->_left){leftMin = leftMin->_left;}return Iterator(leftMin);}Iterator End(){return Iterator(nullptr);}ConstIterator End() const{return ConstIterator(nullptr);}ConstIterator Begin() const{Node* leftMin = _root;while (leftMin && leftMin->_left){leftMin = leftMin->_left;}return ConstIterator(leftMin);}

?find函數

RBTree.hIterator Find(const K& key){Node* cur = _root;while (cur){if (cur->_key < key){cur = cur->_right;}else if (cur->_key > key){cur = cur->_left;}else{return Iterator(cur);}}return End();}

operator()?、仿函數?

?使用仿函數的原因其實是在insert函數中需要使用operator()的原因,因為在insert函數中,需要使用比較大小之間的問題,而為了讓紅黑樹同時適應set和map(主要是針對map的pair內部問題),需要對取值之間進行正確取值的操作。

如上圖所示,在set中data可以直接對于set中的value,但是對于map來說,需要在pair中進行取值,所以map需要一個詳細的對應關系,這就造成了仿函數Key0fT的誕生

RBTree.h//樹的最終的基礎結構
template<class K, class T, class KeyOfT>
class RBTree
{typedef RBTreeNode<T> Node;public:typedef __RBTreeIterator<T, T&, T*> Iterator;typedef __RBTreeIterator<T, const T&, const T*> ConstIterator;
private:Node* _root = nullptr;
};

set和map的仿函數 :

        //Myset.hstruct SetKeyOfT{const K& operator()(const K& key){return key;}};//Mymap.hstruct MapKeyOfT{const K& operator()(const pair<K, V>& kv){return kv.first;}};

圖解:?

insert函數:

pair<Iterator, bool> Insert(const T& data){if (_root == nullptr){_root = new Node(data);_root->_col = BLACK;return make_pair(Iterator(_root), true);}KeyOfT kot;//調用了仿函數,方便之后的比較Node* parent = nullptr;Node* cur = _root;while (cur){// K// pair<K, V>// kot對象,是用來取T類型的data對象中的keyif (kot(cur->_data) < kot(data)){parent = cur;cur = cur->_right;}else if (kot(cur->_data) > kot(data)){parent = cur;cur = cur->_left;}else{return make_pair(Iterator(cur), false);}}cur = new Node(data);Node* newnode = cur;cur->_col = RED; // 新增節點給紅色if (kot(parent->_data) < kot(data)){parent->_right = cur;}else{parent->_left = cur;}cur->_parent = parent;// parent的顏色是黑色也結束while (parent && parent->_col == RED){// 關鍵看叔叔Node* grandfather = parent->_parent;if (parent == grandfather->_left){Node* uncle = grandfather->_right;// 叔叔存在且為紅,-》變色即可if (uncle && uncle->_col == RED){parent->_col = uncle->_col = BLACK;grandfather->_col = RED;// 繼續往上處理cur = grandfather;parent = cur->_parent;}else // 叔叔不存在,或者存在且為黑{if (cur == parent->_left){//     g  //   p   u// c RotateR(grandfather);parent->_col = BLACK;grandfather->_col = RED;}else{//      g  //   p     u//      c 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 // 叔叔不存在,或者存在且為黑{// 情況二:叔叔不存在或者存在且為黑// 旋轉+變色//      g//   u     p//            cif (cur == parent->_right){RotateL(grandfather);parent->_col = BLACK;grandfather->_col = RED;}else{//		g//   u     p//      cRotateR(parent);RotateL(grandfather);cur->_col = BLACK;grandfather->_col = RED;}break;}}}_root->_col = BLACK;return make_pair(Iterator(newnode), true);}

構造函數,析構函數:?

?析構函數:

~RBTree(){Destroy(_root);_root = nullptr;}private:void Destroy(Node* root){if (root == nullptr)return;Destroy(root->_left);Destroy(root->_right);delete root;root = nullptr;}

拷貝構造函數:?

?拷貝構造函數使用的是前序遍歷,且內部需要三條鏈接的拷貝和構造,也就是左子樹、右子樹、和父親節點,同時也需要進行節點顏色的拷貝操作,所以拷貝構造的內部其實是一個前序遍歷+顏色的拷貝+左右子樹的拷貝和鏈接+遞歸遍歷

RBTree(const RBTree<K, T, KeyOfT>& t){_root = Copy(t._root);}private:Node* Copy(Node* root){//因為要有父親節點的鏈接,所以需要一個反向鏈接//其次也需要進行顏色的拷貝!因為是紅黑樹!if (root == nullptr)return nullptr;Node* newroot = new Node(root->_data);newroot->_col = root->_col;newroot->_left = Copy(root->_left);if (newroot->_left)newroot->_left->_parent = newroot;newroot->_right = Copy(root->_right);if (newroot->_right)newroot->_right->_parent = newroot;return newroot;}

賦值函數:?

//RBTree.hRBTree<K, T, KeyOfT>& operator=(RBTree<K, T, KeyOfT> t){swap(_root, t._root);return *this;}

map的封裝:

namespace bit
{template<class K, class V>class map{struct MapKeyOfT{const K& operator()(const pair<K, V>& kv){return kv.first;}};public:typedef typename RBTree<K, pair<const K, V>, MapKeyOfT>::Iterator iterator;typedef typename RBTree<K, const K, MapKeyOfT>::ConstIterator const_iterator;const_iterator begin() const{return _t.Begin();}const_iterator end() const{return _t.End();}iterator begin() {return _t.Begin();}iterator end() {return _t.End();}iterator find(const K& key){return _t.Find(key);}pair<iterator, bool> insert(const pair<K, V>& kv){return _t.Insert(kv);}V& operator[](const K& key){pair<iterator, bool> ret = _t.Insert(make_pair(key, V()));return ret.first->second;}private:RBTree<K, pair<const K, V>, MapKeyOfT> _t;};

set的封裝:?

namespace bit
{template<class K>class set{struct SetKeyOfT{const K& operator()(const K& key){return key;}};public:typedef typename RBTree<K, const K, SetKeyOfT>::Iterator iterator;typedef typename RBTree<K, const K, SetKeyOfT>::ConstIterator const_iterator;const_iterator begin() const{return _t.Begin();}const_iterator end() const{return _t.End();}iterator begin(){return _t.Begin();}iterator end(){return _t.End();}iterator find(const K& key){return _t.Find(key);}pair<iterator, bool> insert(const K& key){return _t.Insert(key);}private:RBTree<K, const K, SetKeyOfT> _t;};void PrintSet(const set<int>& s){for (auto e : s){cout << e << endl;}}

紅黑樹的修改:?

?

#pragma once
#include<vector>enum Colour
{RED,BLACK
};template<class T>
struct RBTreeNode
{RBTreeNode<T>* _left;RBTreeNode<T>* _right;RBTreeNode<T>* _parent;T _data;Colour _col;RBTreeNode(const T& data):_left(nullptr), _right(nullptr), _parent(nullptr), _data(data), _col(RED){}
};template<class T, class Ref, class Ptr>
struct __RBTreeIterator
{typedef RBTreeNode<T> Node;typedef __RBTreeIterator<T, Ref, Ptr> Self;Node* _node;__RBTreeIterator(Node* node):_node(node){}Ref operator*(){return _node->_data;}Ptr operator->(){return &_node->_data;}bool operator!=(const Self& s){return _node != s._node;}Self& operator++(){if (_node->_right){// 下一個,右樹最左節點Node* leftMin = _node->_right;while (leftMin->_left){leftMin = leftMin->_left;}_node = leftMin;}else{// 下一個,孩子等于父親左的那個祖先Node* cur = _node;Node* parent = cur->_parent;while (parent && cur == parent->_right){cur = parent;parent = parent->_parent;}_node = parent;}return *this;}
};template<class K, class T, class KeyOfT>
class RBTree
{typedef RBTreeNode<T> Node;public:typedef __RBTreeIterator<T, T&, T*> Iterator;typedef __RBTreeIterator<T, const T&, const T*> ConstIterator;RBTree() = default;RBTree(const RBTree<K, T, KeyOfT>& t){_root = Copy(t._root);}// t2 = t1RBTree<K, T, KeyOfT>& operator=(RBTree<K, T, KeyOfT> t){swap(_root, t._root);return *this;}~RBTree(){Destroy(_root);_root = nullptr;}Iterator Begin(){Node* leftMin = _root;while (leftMin && leftMin->_left){leftMin = leftMin->_left;}return Iterator(leftMin);}Iterator End(){return Iterator(nullptr);}ConstIterator End() const{return ConstIterator(nullptr);}ConstIterator Begin() const{Node* leftMin = _root;while (leftMin && leftMin->_left){leftMin = leftMin->_left;}return ConstIterator(leftMin);}Iterator Find(const K& key){Node* cur = _root;while (cur){if (cur->_key < key){cur = cur->_right;}else if (cur->_key > key){cur = cur->_left;}else{return Iterator(cur);}}return End();}// 20:10pair<Iterator, bool> Insert(const T& data){if (_root == nullptr){_root = new Node(data);_root->_col = BLACK;return make_pair(Iterator(_root), true);}KeyOfT kot;Node* parent = nullptr;Node* cur = _root;while (cur){// K// pair<K, V>// kot對象,是用來取T類型的data對象中的keyif (kot(cur->_data) < kot(data)){parent = cur;cur = cur->_right;}else if (kot(cur->_data) > kot(data)){parent = cur;cur = cur->_left;}else{return make_pair(Iterator(cur), false);}}cur = new Node(data);Node* newnode = cur;cur->_col = RED; // 新增節點給紅色if (kot(parent->_data) < kot(data)){parent->_right = cur;}else{parent->_left = cur;}cur->_parent = parent;// parent的顏色是黑色也結束while (parent && parent->_col == RED){// 關鍵看叔叔Node* grandfather = parent->_parent;if (parent == grandfather->_left){Node* uncle = grandfather->_right;// 叔叔存在且為紅,-》變色即可if (uncle && uncle->_col == RED){parent->_col = uncle->_col = BLACK;grandfather->_col = RED;// 繼續往上處理cur = grandfather;parent = cur->_parent;}else // 叔叔不存在,或者存在且為黑{if (cur == parent->_left){//     g  //   p   u// c RotateR(grandfather);parent->_col = BLACK;grandfather->_col = RED;}else{//      g  //   p     u//      c 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 // 叔叔不存在,或者存在且為黑{// 情況二:叔叔不存在或者存在且為黑// 旋轉+變色//      g//   u     p//            cif (cur == parent->_right){RotateL(grandfather);parent->_col = BLACK;grandfather->_col = RED;}else{//		g//   u     p//      cRotateR(parent);RotateL(grandfather);cur->_col = BLACK;grandfather->_col = RED;}break;}}}_root->_col = BLACK;return make_pair(Iterator(newnode), true);}void RotateR(Node* parent){Node* subL = parent->_left;Node* subLR = subL->_right;parent->_left = subLR;if (subLR)subLR->_parent = parent;subL->_right = parent;Node* ppNode = parent->_parent;parent->_parent = subL;if (parent == _root){_root = subL;_root->_parent = nullptr;}else{if (ppNode->_left == parent){ppNode->_left = subL;}else{ppNode->_right = subL;}subL->_parent = ppNode;}}void RotateL(Node* parent){Node* subR = parent->_right;Node* subRL = subR->_left;parent->_right = subRL;if (subRL)subRL->_parent = parent;subR->_left = parent;Node* ppNode = parent->_parent;parent->_parent = subR;if (parent == _root){_root = subR;_root->_parent = nullptr;}else{if (ppNode->_right == parent){ppNode->_right = subR;}else{ppNode->_left = subR;}subR->_parent = ppNode;}}void InOrder(){_InOrder(_root);cout << endl;}bool IsBalance(){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* Copy(Node* root){if (root == nullptr)return nullptr;Node* newroot = new Node(root->_data);newroot->_col = root->_col;newroot->_left = Copy(root->_left);if (newroot->_left)newroot->_left->_parent = newroot;newroot->_right = Copy(root->_right);if (newroot->_right)newroot->_right->_parent = newroot;return newroot;}void Destroy(Node* root){if (root == nullptr)return;Destroy(root->_left);Destroy(root->_right);delete root;root = 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);}void _InOrder(Node* root){if (root == nullptr){return;}_InOrder(root->_left);cout << root->_kv.first << ":" << root->_kv.second << endl;_InOrder(root->_right);}private:Node* _root = nullptr;//size_t _size = 0;
};

?

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

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

相關文章

地平線-旭日X3派(RDK X3)上手基本操作

0. 環境 - win10筆記本 - RDK X3 1.0&#xff08;地平線旭日X3派&#xff0c;后來改名為代號RDK X3&#xff09; 1. 下載資料 https://developer.horizon.ai/resource 主要下載鏡像 http://sunrise.horizon.cc/downloads/os_images/2.1.0/release/ 下載得到了 ubuntu-prei…

vs無法打開或包括文件”QTxxx“

vs創建項目時默認引入core、gui、和widgets等模塊&#xff0c;在需要網絡通訊或者圖表等開發時需要添加相應模塊。 點擊擴展 -> QT VS Tools -> QT Project Setting->Qt Modules&#xff0c;添加相應模塊即可

linux自動部署項目的腳本(從git中拉取并執行)

名字&#xff1a;bootStart.sh ?#!/bin/sh #記事本打開&#xff0c;修改編碼格式為utf8&#xff0c;可解決上傳centos后中文亂碼問題 echo echo 自動化部署腳本啟動 echo echo 停止原來運行中的工程 APP_NAME項目名 # 查詢系統中正在運行的項目進程&#xff0c;并停止進程 …

Ubuntu系統版本查看辦法

Ubuntu系統版本查看辦法 1. 使用 lsb_release 命令2. 查看 /etc/issue 文件3. 使用 hostnamectl 命令4. 查看 /etc/os-release 文件5. 查看內核版本6. 使用 neofetch 命令&#xff08;如果已安裝&#xff09;總結 在 Ubuntu 操作系統中&#xff0c;查看當前系統版本的詳細信息可…

頭歌05-排列樹實驗-旅行商問題

""" 題目&#xff1a;設有n個城市組成的交通圖&#xff0c;一個售貨員從住地城市q出發&#xff0c;到其它城市各一次去推銷貨物&#xff0c;最后回到住地城市。 要求&#xff1a;假定兩個城市a&#xff0c;b 從a到b的路程花費w_ab是已知的&#xff0c;問應該怎樣…

奇瑞控股攜手契約鎖推動客戶、供應商及內部業務全程數字化

奇瑞控股集團是安徽省排名第一的制造業企業&#xff0c;同時入選中國企業家協會發布的中國500強、《財富》中國500強&#xff0c;連續21年位居中國品牌乘用車出口第一。 面對汽車行業“新四化”主題及“數字化”時代變革&#xff0c;奇瑞控股持續創新求變&#xff0c;率先引入電…

WIndows常用輔助工具命令

文章目錄 目的pingnbtstatnetstattracert工作原理應用方案ipconfig作用arp作用at作用nslookup作用net作用格式用法ftp作用參數說明telnet作用參數說明tasklist作用參數說明結合篩選器目的 主要是整理windows 下常用cmd命令, 方便我們調試, 分析, 定位解決工程項目中遇到問題…

Java18的新特性介紹

一、概況 Java 18是Java編程語言的最新版本&#xff0c;它于2022年9月發布。Java 18引入了許多新特性和改進&#xff0c;以下是其中一些重要的新特性。 元編程功能&#xff1a;Java 18引入了元注釋和元類型聲明的功能&#xff0c;使開發人員能夠在編譯時對注解進行元處理。這為…

【C++】位圖/布隆過濾器+海量數據處理

目錄 一、位圖 1.1 位圖的概念 1.2 位圖的實現 1.3 位圖的應用&#xff08;面試題&#xff09; 二、布隆過濾器 2.1 布隆過濾器的引入 2.2 布隆過濾器概念 2.3 布隆過濾器的插入和查找 2.4 布隆過濾器的實現 2.5 布隆過濾器的優點和缺陷 2.6 布隆過濾器的應用&#…

Servlet 的 API

HttpServlet init&#xff1a;當 tomcat 收到了 /hello 這樣的路徑是請求后就會調用 HelloServlet&#xff0c;于是就需要對 HelloServlet 進行實例化&#xff08;只實例一次&#xff0c;后續再有請求也不實例了&#xff09;。 destory&#xff1a;如果是通過 smart tomcat 的停…

實驗六 Java流式編程與網絡程序設計 頭歌

實驗六 Java流式編程與網絡程序設計 頭歌 制作不易&#xff01;點個關注&#xff01;給大家帶來更多價值&#xff01; 第1關 字節輸入/輸出流實現數據的保存和讀取 package step1;import java.io.*; import java.util.*;public class SortArray {public static void main(St…

洗地機品牌哪個牌子好?實力派洗地機品牌TOP10榜單

洗地機依靠其洗、拖、吸、烘為一體的功能&#xff0c;能高效的完成地面清潔的工作&#xff0c;深受大家的喜愛。但是洗地機的型號越來越多&#xff0c;功能也越來越多&#xff0c;對于不想花大價錢&#xff0c;又想要高性價比的精致人群來說實在不友好&#xff0c;所以筆者今天…

C++ 中重寫重載和隱藏的區別

重寫&#xff08;override&#xff09;、重載&#xff08;overload&#xff09;和隱藏&#xff08;overwrite&#xff09;在C中是3個完全不同的概念。我們這里對其進行詳細的說明 1、重寫&#xff08;override&#xff09;是指派生類覆蓋了基類的虛函數&#xff0c;這里的覆蓋必…

如何寫好科研論文(討論)

討論1. 如何去選取第一批要閱讀的論文&#xff1f; 當我選擇第一批要閱讀的論文時&#xff0c;我會遵循以下幾個步驟&#xff0c;以確保所選的論文對我的研究最有幫助&#xff1a; 研究問題的相關性&#xff1a; 明確我的研究問題或主題&#xff1a;首先&#xff0c;我會確保自…

實例展示vue單元測試及難題解惑

通過生動詳實的例子帶你排遍vue單元測試過程中的所有疑惑與難題。 技術棧&#xff1a;jest、vue-test-utils。 共四個部分&#xff1a;運行時、Mock、Stub、Configuring和CLI。 運行時 在跑測試用例時&#xff0c;大家的第一個絆腳石肯定是各種undifned報錯。 解決這些報錯…

【HarmonyOS4學習筆記】《HarmonyOS4+NEXT星河版入門到企業級實戰教程》課程學習筆記(九)

課程地址&#xff1a; 黑馬程序員HarmonyOS4NEXT星河版入門到企業級實戰教程&#xff0c;一套精通鴻蒙應用開發 &#xff08;本篇筆記對應課程第 16 節&#xff09; P16《15.ArkUI-狀態管理-任務統計案例》 1、實現任務進度卡片 怎么讓進度條和進度展示文本堆疊展示&#xff1…

./scripts/Makefile.clean 文件分析

文章目錄 目標 $(subdir-ymn)目標__clean $(clean-dirs): ??? make -f ./scripts/Makefile.clean obj$(patsubst _clean_%,%,$) $(clean-dirs)$(patsubst _clean_%,%,$)_clean_api _clean_cmd _clean_common _clean_disk _clean_drivers _clean_drivers/ddr/altera _clean_d…

react中的useEffect()的使用

useEffect()是react中的hook函數&#xff0c;作用是用于創建由渲染本身引起的操作&#xff0c;而不是事件的觸發&#xff0c;比如Ajax請求&#xff0c;DOM的更改 首先useEffect()是個函數&#xff0c;接受兩個參數&#xff0c;第一個參數是一個方法&#xff0c;第二個參數是數…

數據結構--樹與二叉樹--編程求以孩子兄弟表示法存儲的森林的葉結點個數

數據結構–樹與二叉樹–編程求以孩子兄弟表示法存儲的森林的葉結點個數 題目 編程求以孩子兄弟表示法存儲的森林的葉結點個數 ps&#xff1a;題目來源2025王道數據結構 思路 樹上的操作大多數是通過遞歸進行的 我們可以從根節點開始遞歸 如果結點 N 沒有孩子指針&#xff…

【Entity Framework】如何理解EF中的級聯刪除

【Entity Framework】如何理解EF中的級聯刪除 文章目錄 【Entity Framework】如何理解EF中的級聯刪除一、概述二、發生級聯行為時2.1/刪除主體/父實體2.2/斷開關系 三、發生級聯行為的位置3.1/級聯刪除被跟蹤實體3.2/數據庫中的級聯刪除 四、級聯NULL 一、概述 Entity Framewo…