1. 程式人生 > >STL 簡單 set 和 multiset 的實現

STL 簡單 set 和 multiset 的實現

         set的特性是所有元素都會根據鍵值自動排序,set的元素不像map那樣同時擁有實值(value)和鍵值(key),set元素的鍵值就是實值,實值就是鍵值。Set不允許兩個元素擁有相同的鍵值。不能通過迭代器修改set元素的值。

         multiset和set的唯一區別在於multiset允許鍵值重複。

         我們採用紅黑樹作為set和multiset的底層資料結構,set和multiset的實現完完全全是在紅黑樹的基礎上封裝的一層介面,所有的set和multiset操作都轉而呼叫紅黑樹的API。有關STL紅黑樹的實現,請移步:STL簡單紅黑樹的實現

我用VS2013寫的程式(

github),set和multiset版本的程式碼位於/version/

在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,位於/

test/

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

STLsetmultiset的初步認知

set/multiset是一個集合容器,我們可以對這個容器進行插入,刪除,查詢等工作。set的元素內容只有一個鍵值(key,key和value為同一個值),不允許重複冗餘,當對它插入一個已經存在的元素時,它會自動忽略。set/multiset的底層是用紅黑樹(R

【C++ STL學習之五】容器setmultiset

一、set和multiset基礎 set和multiset會根據特定的排序準則,自動將元素進行排序。不同的是後者允許元素重複而前者不允許。 需要包含標頭檔案: #include <set> set和multiset都是定義在std空間裡的類模板: templ

Effective_STL 學習筆記(二十二) 避免原地修改 set multiset 的健

情況下 刪除元素 一份 pre rpo cast set、map class color 正如所有標準關聯容器,set 和 multiset 保持它們的元素有序,容器的正確行為依賴於它們保持有序,如果改變一個元素的值,新值不在正確的位置,將破壞容器的有序性。 對於

c++ STLsetmultiset

  /** template < class T, // set::key_type/value_type class Compare = less<T>, // set::key_compare/value_compar

迭代器的解釋&&setmultiset

莫名其妙的,要看stl了 不得不說,stl是一個優秀的東西。 雖然TA慢…… 不過,可以省去好多東西啦。 下面切入正題。。。 迭代器 迭代器簡單來說就是stl中的地址。是一種複雜的指標。 #include<set> { st

C++之容器setmultiset

有關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++之setmultiset紅黑樹

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

JavaScriptHTML實現簡單計算機

位數 contain ner height src 標簽 lap rac weight 代碼案例<!DOCTYPE html><html><head><meta charset="UTF-8"><title>Ins

簡單的排序算法(CC++實現

最簡單的排序算法(C和C++實現)1、算法思想如下圖:把待排序的數都存在對應的數組的下標中,如果待排序的數有重復的,就用對應的數組加一,最後把數組的下標打印出來即可。2、源碼(C)如下:#include <stdio.h>int main (void){ int a[100], i, j,

Java集合學習-源碼US現金盤平臺出租實現細節-SetMap

鍵值對 ces res 大小 調整 hashmap 次方 use int set與mapUS現金盤平臺出租[ haozbbs.com ](http://haozbbs.com)Q1446595067 有非常大的關聯。簡單地說,把map的所有key拿出來就是一個set集合。m

SPOJ ADAFIELD Ada and Field(STL的使用:setmultiset,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實現)

【題目描述】 給定一個只包括 '(',')','{','}','[',']' 的字串,判斷字串是否有效。 有效字串需滿足: 左括號必須用相同型別的右括號閉合。 左括號必須以正確的順序閉合。 注意空字串可被認為是有效字串。