【C++】map和set的封裝

目錄

    • 前言
    • 一、紅黑樹的設計
      • 1.1 紅黑樹存儲節點的設計
      • 1.2 紅黑樹的迭代器
      • 1.3 map的設計
      • 1.4 set的設計
      • 1.5關于map與set的const_iterator設計


前言

我們知道map和set的底層都是用紅黑樹實現的,但是set和map的結構不一樣,set只有一個參數K,而map有兩個參數K/V,難道set和map是利用倆份紅黑樹來實現的嗎?從設計者的角度是不可能這樣復現的,這樣實現的話兩個容器有著大量的重復代碼,只是參數的個數不同就要使用兩份紅黑樹復現是完全不符合設計規則的,那么紅黑樹做了怎樣的處理使得傳入參數的不同來復現這兩個容器呢?下面我們就一起來進行學習!!

一、紅黑樹的設計

1.1 紅黑樹存儲節點的設計

既然map和set的參數不同,那么紅黑樹就要統一的對它們的參數進行處理,當為pair<K,V>類型時復現map,為K類型時復現set。我們先來看看stl庫中是如何來進行設計的:

在這里插入圖片描述

通過上圖可以看到,map 傳給紅黑樹的key_type是Key,value_type是pair<Key, T>;而 set 傳給紅黑樹的key_type和value_type都是Key。紅黑樹節點中存儲的數據就是value_type類型的數據。如果傳給value_type的是pair<Key, T>,那么就會實例化出 map;而傳給value_type的是Key,就會實例化出 set。

那么我們下面的問題就轉化成了,如何去提取出value_type類型的數據?

在插入節點的時候我們是需要根據二叉搜索樹的規則來調整的,set的Value為K直接提取出來對比即可,而map中的Vaule為pair<K, V>,我們只需提取出K來對比即可,stl庫中對pair的排序規則不符合并我們的要求,所以我們得自己設計一個仿函數來返回對應的數據!!

在這里插入圖片描述

紅黑樹節點存儲設計如下:

#pragma onceenum 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 K, class T, class KeyOfT>
class RBTree
{typedef RBTreeNode<T> Node;
public:bool Insert(const T& data){if (_root == nullptr){_root = new Node(data);_root->_col = BLACK;return true;}KeyOfT kot;Node* parent = nullptr;Node* cur = _root;while (cur){if (kot(cur->_data) < kot(data)){parent = cur;cur = cur->_right;}else if (kot(cur->_data) > kot(data)){parent = cur;cur = cur->_left;}else{return false;}}// ......return true;}private:Node* _root = nullptr;
};

1.2 紅黑樹的迭代器

我們知道紅黑樹本質上也是一顆二叉搜索樹,所以通過中序遍歷能得到一個升序的序列。

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* subL = _node->_right;while (subL->_left){subL = subL->_left;}_node = subL;}else{Node* cur = _node;Node* parent = cur->_parent;// 右子樹走完了  我們應該返回到該右子樹的父節點的祖先了while (parent && parent->_right == cur){cur = parent;parent = cur->_parent;}_node = parent;}return *this;}Self& operator--(){if (_node->_left){Node* subR = _node->_left;while (subR->_right){subR = subR->_right;}_node = subR;}else{Node* cur = _node;Node* parent = cur->_parent;while (parent && parent->_left == cur){cur = parent;parent = cur->_parent;}_node = parent;}return *this;}
};template<class K, class T, class KeyOfT>
struct RBTree
{typedef RBTreeNode<T> Node;typedef __RBTreeIterator<T, T&, T*> iterator;
public:// begin()為樹的最左節點iterator begin(){// 注: 需要避免_root為nullptrNode* left = _root;while (left && left->_left){left = left->_left;}return iterator(left);}iterator end(){return iterator(nullptr);}private:Node* _root = nullptr;
}

1.3 map的設計

#pragma once
#include "RBTree.h"namespace curry
{template<class K, class V>class map{struct MapKeyOfT{const K& operator()(const pair<const K, V>& kv){return kv.first;   // 提取出kv的key值}};public:typedef typename RBTree<K, pair<K, V>, MapKeyOfT>::iterator iterator;iterator begin(){return _t.begin();}iterator end(){return _t.end();}V& operator[](const K& key){pair<iterator, bool> ret = _t.Insert(make_pair(key, V()));return ret.first->second;}pair<iterator, bool> insert(const pair<const K, V>& kv){return _t.Insert(kv);}private:RBTree<K, pair<K, V>, MapKeyOfT> _t;  // 底層調用RBTree進行插入操作};
};

1.4 set的設計

#pragma once
#include "RBTree.h"namespace curry
{template<class K>class set{struct SetKeyOfT{const K& operator()(const K& key){return key;}};public:typedef typename RBTree<K, K, SetKeyOfT>::iterator iterator;iterator begin(){return _t.begin();}iterator end(){return _t.end();}pair<iterator, bool> insert(const K& key){return _t.Insert(key);}private:RBTree<K, K, SetKeyOfT> _t;};
};

通過上述map和set的設計我們可以發現,實際它們的迭代器與功能操作都是由紅黑樹提供的!!

在這里插入圖片描述

1.5關于map與set的const_iterator設計

我們先來看看stl庫中對它的設計:

在這里插入圖片描述

由上圖可知,實際上在stl庫中map/set的普通迭代器都設計成了const迭代器,那么const迭代器是如何實現的呢?

它是利用普通迭代器構造出來的。self與iterator是不一樣的,我們的普通迭代器是self設計出來的,而const迭代器需要普通迭代器的構造,那么我們就需要顯式定義出普通迭代器的類型進行構造。

在這里插入圖片描述

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

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

相關文章

前端基礎:1-2 面向對象 + Promise

面向對象 對象是什么&#xff1f;為什么要面向對象&#xff1f; 通過代碼抽象&#xff0c;進而藐視某個種類物體的方式 特點&#xff1a;邏輯上遷移更加靈活、代碼復用性更高、高度的模塊化 對象的理解 對象是對于單個物體的簡單抽象對象是容器&#xff0c;封裝了屬性 &am…

如何安裝 Docker

引言 - 介紹 Docker 技術的重要性和應用場景 - 簡要解釋 Docker 的工作原理和優勢 Docker 的安裝 Docker 在不同平臺上的安裝方法&#xff08;Windows、Mac、Linux&#xff09; Docker 是一個開源的容器化平臺&#xff0c;可以幫助開發人員和運維團隊更輕松地打包、交付和運行…

python 裝飾器 帶參數和不帶參數

裝飾器是Python語言中一種特殊的語法&#xff0c;用于在不修改原函數代碼的情況下&#xff0c;為函數添加額外的功能或修改函數的行為。通過裝飾器&#xff0c;我們可以在函數執行前后執行一些額外的代碼&#xff0c;或者修改函數的參數。 要使用裝飾器引入函數和參數&#xf…

Linux_應用篇(07) 系統信息與系統資源

在應用程序當中&#xff0c;有時往往需要去獲取到一些系統相關的信息&#xff0c;譬如時間、日期、以及其它一些系統相關信息&#xff0c;本章將向大家介紹如何通過 Linux 系統調用或 C 庫函數獲取系統信息&#xff0c; 譬如獲取系統時間、日期以及設置系統時間、日期等&#x…

restTemplate返回報文亂碼問題

默認服務端使用UTF8編碼 排查1&#xff1a; 請求前手動設置UTF-8編碼解析報文 RestTemplate restTemplate new RestTemplate(); restTemplate.getMessageConverters().set(1, new StringHttpMessageConverter(StandardCharsets.UTF_8)); ResponseEntity<String> excha…

三能一體運營體系助力政企支撐水平提升

生產力的發展是現代社會孜孜不倦的追求&#xff0c;由此產生了我們熟悉的“機械化、電子化、信息化”乃至現今正在發生的“智能化”四次工業革命。這些是由技術的突破性發展帶來的&#xff0c;但我們也注意到生產力發展的另一個助力&#xff0c;即生產效率的提升&#xff0c;19…

【MySQL數據庫】mysql日志管理、備份與恢復

mysql日志管理、備份與恢復 MySQL數據庫備份及日志一、數據庫備份分類&#xff1a;如何選擇邏輯備份策略 (頻率)完全備份與恢復備份恢復 增量備份與恢復實現增量備份 基于時間點與位置恢復 二.MySQL日志管理 MySQL數據庫備份及日志 在生產環境中&#xff0c;數據的安全性是至關…

在未來你將何去何從?

在數字化的浪潮中&#xff0c;信息技術行業無疑是推動全球經濟和社會發展的重要動力。隨著科技的不斷迭代與進步&#xff0c;云計算、大數據、人工智能&#xff08;AI&#xff09;、物聯網&#xff08;IoT&#xff09;、5G通信和區塊鏈等技術已經深入到我們生活的每一個角落&am…

鴻蒙原生應用元服務開發-鴻蒙真機運行項目實戰與注意事項

一、解壓項目注意項目包不能為中文 二、用數據線將裝好DevEco Studio的電腦與設置為開發者模式的鴻蒙手機相連接。 三、將項目包托進DevEco Studio 中 注意項目包文件不能有嵌套 四、查看設備運行 五、點擊項目結構 六、勾選紅色框圈部分 登錄開發者賬號 七、選擇好公司 八、等…

我是如何使用 Next.js14 + Tailwindcss 重構個人項目的

前言 去年在學習 React 和 Nest 的時候&#xff0c;參考了大佬 imsyy 的項目 DailyHot&#xff0c;以此項目的靈感基于 React 開發&#xff0c;完成之后就沒怎么在意。 后來發現這個項目還有點小流量&#xff0c;每天差不多 200-400 的 IP 訪問量&#xff1a; 我又抽時間優…

深度學習之基于Pytorch框架手寫數字識別

歡迎大家點贊、收藏、關注、評論啦 &#xff0c;由于篇幅有限&#xff0c;只展示了部分核心代碼。 文章目錄 一項目簡介 二、功能三、系統四. 總結 一項目簡介 一、項目背景與意義 手寫數字識別是數字圖像處理領域的一個經典問題&#xff0c;也是深度學習技術的一個常用應用場…

AWS計算之Amazon Lightsail

Amazon Lightsail是亞馬遜提供的一種簡化的虛擬私有服務器(VPS)服務&#xff0c;旨在幫助開發人員快速、輕松地搭建和管理虛擬服務器。Lightsail提供了預配置地計算資源、網絡、存儲和數據傳輸選項&#xff0c;用戶可以通過簡單的界面選擇所需的配置&#xff0c;輕松部署應用程…

51匯編--數碼管時鐘

實現一個24小時制的電子鐘程序&#xff0c;在實驗箱的6個數碼管上顯示時分秒&#xff08;用定時器0中斷更新計時時間&#xff0c;時間值以壓縮BCD碼形式保存在內部RAM的30H31H和32H單元&#xff09;。 PC機可通過串行口發送要設置的時間給單片機&#xff08;發送的時間格式為壓…

java 重寫接口的default方法

在Java 8中&#xff0c;接口可以包含默認方法&#xff08;default methods&#xff09;&#xff0c;這些方法可以有默認實現。如果一個類實現了包含默認方法的接口&#xff0c;并且沒有提供這個方法的實現&#xff0c;則會使用接口中的默認實現。 如果需要重寫接口中的默認方法…

【MySQL精通之路】SQL優化(1)-查詢優化(11)-多范圍查詢優化

主博客&#xff1a; 【MySQL精通之路】SQL優化(1)-查詢優化-CSDN博客 上一篇&#xff1a; 【MySQL精通之路】SQL優化(1)-查詢優化(10)-外部聯接簡化-CSDN博客 下一篇&#xff1a; 當基表很大且未存儲在存儲引擎的緩存中時&#xff0c;使用輔助索引上的范圍掃描讀取行可能會…

uniappx 獲取設備唯一標識(OAID、AAID、AndroidID、IMEI等) Ba-IdCode-U

簡介&#xff08;下載地址&#xff09; Ba-IdCode-U 是一款可以獲取國內各大手機廠商 OAID&#xff08;開放匿名設備標識&#xff09;及海外手機平臺 AAID&#xff08;安卓廣告標識&#xff09;的uniapp插件。另外也支持獲取 IMEI/MEID、AndroidID、WidevineID、PseudoID、GUI…

Spring Cloud Alibaba-06-Sleuth鏈路追蹤

Lison <dreamlison163.com>, v1.0.0, 2024.4.03 Spring Cloud Alibaba-06-Sleuth鏈路追蹤 文章目錄 Spring Cloud Alibaba-06-Sleuth鏈路追蹤為什么使用鏈路追蹤常見鏈路追蹤解決方案Sleuth概述概述Sleuth術語 Sleuth Zipkin 原理Sleuth原理簡述Zipkin 原理簡述 Sleut…

Python庫之`lxml`的高級用法深度解析

Python庫之lxml的高級用法深度解析 簡介 lxml是一個功能強大的第三方庫&#xff0c;它提供了對XML和HTML文檔的高效處理能力。除了基本的解析和創建功能外&#xff0c;lxml還包含了一些高級用法&#xff0c;這些用法可以幫助開發者在處理復雜文檔時更加得心應手。 高級解析技…

代碼隨想錄——路徑總和(Leetcode113)需要回顧

題目鏈接 遞歸 本題遞歸需要遍歷整棵樹&#xff0c;所以遞歸沒有返回值 /*** Definition for a binary tree node.* public class TreeNode {* int val;* TreeNode left;* TreeNode right;* TreeNode() {}* TreeNode(int val) { this.val val; }* T…

蘋果M4性能分析:進步神速?還有多少空間?

2024年初&#xff0c;蘋果推出了M4處理器&#xff0c;令人意外的是&#xff0c;它的發布距離M3發布僅僅過去了半年時間。更讓人驚訝的是&#xff0c;M4首次亮相于iPad Pro。這一新處理器不僅僅是M3的簡單升級版本&#xff0c;而是一次全面的架構優化。本文將詳細分析M4處理器的…