STL 簡單 set 和 multiset 的實現
set的特性是所有元素都會根據鍵值自動排序,set的元素不像map那樣同時擁有實值(value)和鍵值(key),set元素的鍵值就是實值,實值就是鍵值。Set不允許兩個元素擁有相同的鍵值。不能通過迭代器修改set元素的值。
multiset和set的唯一區別在於multiset允許鍵值重複。
我們採用紅黑樹作為set和multiset的底層資料結構,set和multiset的實現完完全全是在紅黑樹的基礎上封裝的一層介面,所有的set和multiset操作都轉而呼叫紅黑樹的API。有關STL紅黑樹的實現,請移步:STL簡單紅黑樹的實現
我用VS2013寫的程式(
在STL中,set和multiset的底層都需要以下幾個檔案:
1. globalConstruct.h,構造和解構函式檔案,位於cghSTL/allocator/cghAllocator/
2. cghAlloc.h,空間配置器檔案,位於cghSTL/allocator/cghAllocator/
set的測試檔案test_cghSet.cpp,位於/test/
multiset的實現檔案cghMultiset.h,位於//cghMultiset/
multiset的測試檔案test_cghMultiset.cpp,位於/
1.構造與析構
先看第一個,globalConstruct.h建構函式檔案
/******************************************************************* * Copyright(c) 2016 Chen Gonghao * All rights reserved. * * [email protected] * * 功能:全域性構造和析構的實現程式碼 ******************************************************************/ #include "stdafx.h" #include <new.h> #include <type_traits> #ifndef _CGH_GLOBAL_CONSTRUCT_ #define _CGH_GLOBAL_CONSTRUCT_ namespace CGH { #pragma region 統一的構造解構函式 template<class T1, class T2> inline void construct(T1* p, const T2& value) { new (p)T1(value); } template<class T> inline void destroy(T* pointer) { pointer->~T(); } template<class ForwardIterator> inline void destroy(ForwardIterator first, ForwardIterator last) { // 本來在這裡要使用特性萃取機(traits程式設計技巧)判斷元素是否為non-trivial // non-trivial的元素可以直接釋放記憶體 // trivial的元素要做呼叫解構函式,然後釋放記憶體 for (; first < last; ++first) destroy(&*first); } #pragma endregion } #endif
按照STL的介面規範,正確的順序是先分配記憶體然後構造元素。建構函式的實現採用placement new的方式;為了簡化起見,我直接呼叫解構函式來銷燬元素,而在考慮效率的情況下一般會先判斷元素是否為non-trivial型別。
關於 trivial 和 non-trivial 的含義,參見:stack overflow
2.空間配置器
cghAlloc.h是空間配置器檔案,空間配置器負責記憶體的申請和回收。
/*******************************************************************
* Copyright(c) 2016 Chen Gonghao
* All rights reserved.
*
* [email protected]
*
* 功能:cghAllocator空間配置器的實現程式碼
******************************************************************/
#ifndef _CGH_ALLOC_
#define _CGH_ALLOC_
#include <new>
#include <cstddef>
#include <cstdlib>
#include <climits>
#include <iostream>
namespace CGH
{
#pragma region 記憶體分配和釋放函式、元素的構造和解構函式
// 記憶體分配
template<class T>
inline T* _allocate(ptrdiff_t size, T*)
{
set_new_handler(0);
T* tmp = (T*)(::operator new((size_t)(size * sizeof(T))));
if (tmp == 0)
{
std::cerr << "out of memory" << std::endl;
exit(1);
}
return tmp;
}
// 記憶體釋放
template<class T>
inline void _deallocate(T* buffer)
{
::operator delete(buffer);
}
// 元素構造
template<class T1, class T2>
inline void _construct(T1* p, const T2& value)
{
new(p)T1(value);
}
// 元素析構
template<class T>
inline void _destroy(T* ptr)
{
ptr->~T();
}
#pragma endregion
#pragma region cghAllocator空間配置器的實現
template<class T>
class cghAllocator
{
public:
typedef T value_type;
typedef T* pointer;
typedef const T* const_pointer;
typedef T& reference;
typedef const T& const_reference;
typedef size_t size_type;
typedef ptrdiff_t difference_type;
template<class U>
struct rebind
{
typedef cghAllocator<U> other;
};
static pointer allocate(size_type n, const void* hint = 0)
{
return _allocate((difference_type)n, (pointer)0);
}
static void deallocate(pointer p, size_type n)
{
_deallocate(p);
}
static void deallocate(void* p)
{
_deallocate(p);
}
void construct(pointer p, const T& value)
{
_construct(p, value);
}
void destroy(pointer p)
{
_destroy(p);
}
pointer address(reference x)
{
return (pointer)&x;
}
const_pointer const_address(const_reference x)
{
return (const_pointer)&x;
}
size_type max_size() const
{
return size_type(UINT_MAX / sizeof(T));
}
};
#pragma endregion
#pragma region 封裝STL標準的空間配置器介面
template<class T, class Alloc = cghAllocator<T>>
class simple_alloc
{
public:
static T* allocate(size_t n)
{
return 0 == n ? 0 : (T*)Alloc::allocate(n*sizeof(T));
}
static T* allocate(void)
{
return (T*)Alloc::allocate(sizeof(T));
}
static void deallocate(T* p, size_t n)
{
if (0 != n)Alloc::deallocate(p, n*sizeof(T));
}
static void deallocate(void* p)
{
Alloc::deallocate(p);
}
};
#pragma endregion
}
#endif
classcghAllocator是空間配置器類的定義,主要的四個函式的意義如下:allocate函式分配記憶體,deallocate函式釋放記憶體,construct構造元素,destroy析構元素。這四個函式最終都是通過呼叫_allocate、_deallocate、_construct、_destroy這四個行內函數實現功能。
我們自己寫的空間配置器必須封裝一層STL的標準介面,
template<classT, class Alloc = cghAllocator<T>>
classsimple_alloc
3.紅黑樹的實現
紅黑樹作為set的底層資料結構,其實現比較複雜,這裡不展開講了,有興趣的童鞋請移步另一篇部落格:STL簡單紅黑樹的實現
4.cghSet的實現
cghSet的內部結構可以分為以下部分:
1. 一堆typedef,注意,set的底層資料結構是紅黑樹,cghSet自己沒有成員變數,所有的操作均轉而呼叫紅黑樹的API,有關STL紅黑樹的實現,請移步:STL簡單紅黑樹的實現;
2. cghSet的構造與解構函式;
3. 提供給使用者的api
cghSet的程式碼註釋已經寫得很詳細了,童鞋們可以參考cghSet的內部結構來總體把握cghSet的框架,通過註釋來理解cghSet的工作原理。
cghSet.h
/*******************************************************************
* Copyright(c) 2016 Chen Gonghao
* All rights reserved.
*
* [email protected]
*
* 檔案內容:cghSet的實現
******************************************************************/
#ifndef _CGH_SET_
#define _CGH_SET_
#include "globalConstruct.h"
#include "cghAlloc.h"
#include "rb_tree.h"
namespace CGH{
/* 預設情況下采用遞增排序 */
template<class key, class compare = std::less<key>, class Alloc = cghAllocator<key>>
class cghSet{
#pragma region typedef
private:
typedef key key_type;
typedef key value_type;
typedef compare key_compare;
typedef compare value_compare;
typedef cgh_rb_tree<key_type, value_type, std::identity<value_type>, key_compare, Alloc> rep_type;
rep_type t;
public:
typedef typename rep_type::const_pointer pointer;
typedef typename rep_type::const_pointer const_pointer;
typedef typename rep_type::const_reference reference;
typedef typename rep_type::const_reference const_ference;
typedef typename rep_type::iterator iterator;
typedef typename rep_type::size_type size_type;
typedef typename rep_type::difference_type difference_type;
#pragma endregion
#pragma region 建構函式
public:
cghSet() :t(compare()){}
cghSet(const cghSet<key, compare, Alloc>&x) :t(x.t){}
cghSet<key, compare, Alloc>& operator=(const cghSet<key, compare, Alloc>&x)
{
t = x.t;
return *this;
}
#pragma endregion
#pragma region 提供給使用者API
/* 返回鍵比較函式 */
key_compare key_comp()const{ return t.key_comp(); }
/* 返回值比較函式 */
value_compare value_comp()const{ return t.key_comp(); }
/* 返回迭代器,指定set的第一個元素 */
iterator begin(){ return t.begin(); }
/* 返回迭代器,指定set的最後一個元素 */
iterator end(){ return t.end(); }
/* set是否為空 */
bool empty() const{ return t.empty(); }
/* set大小 */
size_type size()const{ return t.size(); }
/* set最大容量 */
size_type max_size()const{ return t.max_size(); }
/* 插入元素到set中 */
std::pair<iterator, bool> insert(const value_type&x)
{
std::pair<typename rep_type::iterator, bool> p = t.insert_unique(x);
return std::pair<iterator, bool>(p.first, p.second);
}
/*
返回迭代器,指向要查詢的元素
如果沒有找到,返回end
*/
iterator find(const key_type&x){ return t.find(x); }
#pragma endregion
};
}
#endif
5.multiset的實現
cghMultiset的內部結構可以分為以下部分:
1. 一堆typedef,注意,multiset的底層資料結構是紅黑樹,multiset自己沒有成員變數,所有的操作均轉而呼叫紅黑樹的API,有關STL紅黑樹的實現,請移步:STL簡單紅黑樹的實現;
2. cghMultiset的構造與解構函式;
3. 提供給使用者的api
cghMultiset.h
/*******************************************************************
* Copyright(c) 2016 Chen Gonghao
* All rights reserved.
*
* [email protected]h.net
*
* 檔案內容:cghMultiset的實現
******************************************************************/
#ifndef _CGH_MULTI_SET_
#define _CGH_MULTI_SET_
#include "globalConstruct.h"
#include "cghAlloc.h"
#include "rb_tree.h"
namespace CGH{
template<class key, class compare = std::less<key>, class Alloc = cghAllocator<key>>/* 預設情況下采用遞增排序 */
class cghMultiset{
#pragma region typedef
private:
typedef key key_type;
typedef key value_type;
typedef compare key_compare;
typedef compare value_compare;
typedef cgh_rb_tree<key_type, value_type, std::identity<value_type>, key_compare, Alloc> rep_type;
rep_type t;
public:
typedef typename rep_type::const_pointer pointer;
typedef typename rep_type::const_pointer const_pointer;
typedef typename rep_type::const_reference reference;
typedef typename rep_type::const_reference const_ference;
typedef typename rep_type::iterator iterator;
typedef typename rep_type::size_type size_type;
typedef typename rep_type::difference_type difference_type;
#pragma endregion
#pragma region 建構函式
public:
cghMultiset() :t(compare()){}
cghMultiset(const cghMultiset<key, compare, Alloc>&x) :t(x.t){}
cghMultiset<key, compare, Alloc>& operator=(const cghMultiset<key, compare, Alloc>&x)
{
t = x.t;
return *this;
}
#pragma endregion
#pragma region 提供給使用者的API
/* 返回鍵比較函式 */
key_compare key_comp()const{ return t.key_comp(); }
/* 返回值比較函式 */
value_compare value_comp()const{ return t.key_comp(); }
/* 返回迭代器,指定cghMultiset的第一個元素 */
iterator begin(){ return t.begin(); }
/* 返回迭代器,指定cghMultiset的最後一個元素 */
iterator end(){ return t.end(); }
/* cghMultiset是否為空 */
bool empty() const{ return t.empty(); }
/* cghMultiset大小 */
size_type size()const{ return t.size(); }
/* cghMultiset最大容量 */
size_type max_size()const{ return t.max_size(); }
/* 插入元素到cghMultiset中 */
iterator insert(const value_type&x)
{
//std::pair<typename rep_type::iterator, bool> p = t.insert_equal(x);
return t.insert_equal(x);
}
/*
返回迭代器,指向要查詢的元素
如果沒有找到,返回end
*/
iterator find(const key_type&x){ return t.find(x); }
#pragma endregion
};
}
#endif
6.測試
6.1 測試cghSet
測試環節的主要內容已在註釋中說明
test_cghSet.cpp
/*******************************************************************
* Copyright(c) 2016 Chen Gonghao
* All rights reserved.
*
* [email protected]
*
* 檔案內容:cghSet的測試
******************************************************************/
#include "stdafx.h"
#include "cghSet.h"
using namespace::std;
int _tmain(int argc, _TCHAR* argv[])
{
using namespace::CGH;
cghSet<int> test;
test.insert(3);
test.insert(2);
test.insert(5);
test.insert(4);
test.insert(1);
test.insert(5);
std::cout << endl << "亂序插入:3,2,5,4,1,5" << endl << endl;
std::cout << "經set容器排序後的結果:" << endl << endl;
for (cghSet<int>::iterator iter = test.begin(); iter != test.end(); ++iter)
{
std::cout << *iter << ", ";
}
std::cout << endl << endl << "set不允許插入重複的鍵值,我們兩次插入5,但是輸出結果裡,5只出現了一次";
std::cout << endl << endl << "----------------------";
std::cout << endl << endl << "使用find函式在set中找值為3的元素" << endl << endl;
cghSet<int>::iterator iter = test.find(3);
if (iter != test.end())
{
std::cout << "iter != test.end(),找到了,*iter = " << *iter;
}
std::cout << endl << endl << "----------------------";
std::cout << endl << endl << "使用find函式在set中找值為6的元素" << endl << endl;
cghSet<int>::iterator iter2 = test.find(6);
if (iter2 == test.end())
{
std::cout << "iter2 == test.end(),沒有找到";
}
std::cout << endl << endl << "----------------------";
std::cout << endl << endl << "set的大小:" << test.size() << endl << endl;
system("pause");
return 0;
}
測試結果如下圖所示
6.2 測試cghMultiset
測試環節的主要內容已在註釋中說明
test_cghMultiset.cpp
/*******************************************************************
* Copyright(c) 2016 Chen Gonghao
* All rights reserved.
*
* [email protected]
*
* 檔案內容:cghMultiset的測試
******************************************************************/
#include "stdafx.h"
#include "cghMultiset.h"
using namespace::std;
int _tmain(int argc, _TCHAR* argv[])
{
using namespace::CGH;
cghMultiset<int> test;
test.insert(3);
test.insert(2);
test.insert(5);
test.insert(4);
test.insert(1);
test.insert(5);
std::cout << endl << "亂序插入:3,2,5,4,1,5" << endl << endl;
std::cout << "經set容器排序後的結果:" << endl << endl;
for (cghMultiset<int>::iterator iter = test.begin(); iter != test.end(); ++iter)
{
std::cout << *iter << ", ";
}
std::cout << endl << endl << "multiset允許插入重複的鍵值,我們兩次插入5,於是輸出結果裡,5出現了兩次";
std::cout << endl << endl << "----------------------";
std::cout << endl << endl << "使用find函式在set中找值為3的元素" << endl << endl;
cghMultiset<int>::iterator iter = test.find(3);
if (iter != test.end())
{
std::cout << "iter != test.end(),找到了,*iter = " << *iter;
}
std::cout << endl << endl << "----------------------";
std::cout << endl << endl << "使用find函式在set中找值為6的元素" << endl << endl;
cghMultiset<int>::iterator iter2 = test.find(6);
if (iter2 == test.end())
{
std::cout << "iter2 == test.end(),沒有找到";
}
std::cout << endl << endl << "----------------------";
std::cout << endl << endl << "set的大小:" << test.size() << endl << endl;
system("pause");
return 0;
}
測試結果如下圖所示
相關推薦
STL 簡單 set 和 multiset 的實現
set的特性是所有元素都會根據鍵值自動排序,set的元素不像map那樣同時擁有實值(value)和鍵值(key),set元素的鍵值就是實值,實值就是鍵值。Set不允許兩個元素擁有相同的鍵值。不能通過迭代器修改set元素的值。 multi
【STL】set和multiset的初步認知
set/multiset是一個集合容器,我們可以對這個容器進行插入,刪除,查詢等工作。set的元素內容只有一個鍵值(key,key和value為同一個值),不允許重複冗餘,當對它插入一個已經存在的元素時,它會自動忽略。set/multiset的底層是用紅黑樹(R
【C++ STL學習之五】容器set和multiset
一、set和multiset基礎 set和multiset會根據特定的排序準則,自動將元素進行排序。不同的是後者允許元素重複而前者不允許。 需要包含標頭檔案: #include <set> set和multiset都是定義在std空間裡的類模板: templ
Effective_STL 學習筆記(二十二) 避免原地修改 set 和 multiset 的健
情況下 刪除元素 一份 pre rpo cast set、map class color 正如所有標準關聯容器,set 和 multiset 保持它們的元素有序,容器的正確行為依賴於它們保持有序,如果改變一個元素的值,新值不在正確的位置,將破壞容器的有序性。 對於
c++ STL 之 set及multiset
/** template < class T, // set::key_type/value_type class Compare = less<T>, // set::key_compare/value_compar
迭代器的解釋&&set和multiset
莫名其妙的,要看stl了 不得不說,stl是一個優秀的東西。 雖然TA慢…… 不過,可以省去好多東西啦。 下面切入正題。。。 迭代器 迭代器簡單來說就是stl中的地址。是一種複雜的指標。 #include<set> { st
C++之容器set和multiset
有關set和multi的介紹和API參考:【C++ STL學習之五】容器set和multiset 在這裡分享一個使用multiset容器的劍指offer上的面試題最小的K個數。 題目描述: 輸入n個整數,找出其中最小的K個數。例如輸入4,5,1,6,2,7,3,8這8個數字,則最小的4個
STL 簡單 copy 演算法的實現
1.簡介 不論是對客戶端或對STL內部而言,copy() 都是一個常常被呼叫的函式。由於copy進行的是複製操作,而複製操作不外乎運用賦值運算子(assignment operator)或複製建構函式(copy constructor),但是某些元素的型別
C++之set和multiset紅黑樹
set和multiset 會根據特定的排序準則,自動將元素排序。兩者不同之處,在於multiset允許元素重複,而set不允許重複,如圖1所示。圖1 set和multiset在使用set和multiset之前,需要包含頭標頭檔案<set>,Set 和multise
多種排序算法的思路和簡單代碼的實現(一)
insert i++ 前後端 分享 size quicksort 執行 判斷 clas 就自己簡單的理解了一些排序算法(JAVA)思路和代碼分享給大家:歡迎大家進行交流。 直接插入排序,折半插入排序,冒泡排序,快速排序 1 public class Sort { 2
簡單兩步快速實現shiro的配置和使用,包含登錄驗證、角色驗證、權限驗證以及shiro登錄註銷流程(基於spring的方式,使用maven構建)
protect login uid sim isa 當前 sub efi inf 前言: shiro因為其簡單、可靠、實現方便而成為現在最常用的安全框架,那麽這篇文章除了會用簡潔明了的方式講一下基於spring的shiro詳細配置和登錄註銷功能使用之外,也會根據慣例在文章最
面向對象編程和簡單UI界面的實現
.com gae bcv hab iuc fan get dea lan 615E雲3419NDhttp://www.facebolw.com/space/2103772/following 20j怖境幟5盎XNhttp://weibo.com/p/100505636741
(九)數據結構之簡單排序算法實現:冒泡排序、插入排序和選擇排序
html lan 獎章 tmx 4tb wot 數據結構 lec get d59FG8075P7伊http://www.zcool.com.cn/collection/ZMTg2NTU2NjQ=.html 312V畏蝗淤ZP哦睬http://www.zcool.com.c
JavaScript和HTML實現的簡單計算機
位數 contain ner height src 標簽 lap rac weight 代碼案例<!DOCTYPE html><html><head><meta charset="UTF-8"><title>Ins
最簡單的排序算法(C和C++實現)
最簡單的排序算法(C和C++實現)1、算法思想如下圖:把待排序的數都存在對應的數組的下標中,如果待排序的數有重復的,就用對應的數組加一,最後把數組的下標打印出來即可。2、源碼(C)如下:#include <stdio.h>int main (void){ int a[100], i, j,
Java集合學習-源碼US現金盤平臺出租實現細節-Set和Map
鍵值對 ces res 大小 調整 hashmap 次方 use int set與mapUS現金盤平臺出租[ haozbbs.com ](http://haozbbs.com)Q1446595067 有非常大的關聯。簡單地說,把map的所有key拿出來就是一個set集合。m
SPOJ ADAFIELD Ada and Field(STL的使用:set,multiset,map的叠代器)題解
tdi 重復 pan eat iterator %d seed class ... 題意:n*m的方格,“0 x”表示x軸在x位置切一刀,“0 y”表示y軸在y位置切一刀,每次操作後輸出當前面積最大矩形。 思路:用set分別儲存x軸y軸分割的點,用multiset(可重復)
STL原始碼剖析——stack的實現原理和使用方法詳解
Stack 簡介 stack 是堆疊容器,是一種“先進後出”的容器。 stack 是簡單地裝飾 deque 容器而成為另外一種容器。 使用 stack 時需要加上標頭檔案 #include<s
STL原始碼剖析——deque的實現原理和使用方法詳解
Deque 簡介 deque是“double—ended queue”的縮寫,和vector一樣都是STL的容器,deque 是雙端陣列,而 vector 是單端的。 deque 在介面上和 vector 非常相似,在許多操作的地方
leetcode 20 簡單括號匹配(C++和python實現)
【題目描述】 給定一個只包括 '(',')','{','}','[',']' 的字串,判斷字串是否有效。 有效字串需滿足: 左括號必須用相同型別的右括號閉合。 左括號必須以正確的順序閉合。 注意空字串可被認為是有效字串。