11.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版本的代码位于cghSTL/version/cghSTL-0.4.2.rar

在STL中,set和multiset的底层都需要以下几个文件:

1.      globalConstruct.h,构造和析构函数文件,位于cghSTL/allocator/cghAllocator/

2.      cghAlloc.h,空间配置器文件,位于cghSTL/allocator/cghAllocator/

3.      rb_tree.h,红黑树的实现,位于cghSTL/associative
containers
/RB-tree/

set的实现文件cghSet.h,位于cghSTL/associative
containers
/cghSet/

set的测试文件test_cghSet.cpp,位于cghSTL/test/

multiset的实现文件cghMultiset.h,位于cghSTL/associative
containers
/cghMultiset/

multiset的测试文件test_cghMultiset.cpp,位于cghSTL/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类型,non-trivial的元素可以直接释放内存,可以理解为non-trivial全部分配在栈上,不用我们操心,由程序自动回收,trivial的元素要分配在堆上,需要手工调用析构函数来销毁,然后释放内存。

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.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]
*
*  文件内容: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;
}

测试结果如下图所示

时间: 2024-08-04 04:04:01

11.STL简单set和multiset的实现的相关文章

STL简单hashtable的实现

1. 简介 Hash_table可提供对任何有名项(nameditem)的存取.删除和查询操作.由于操作对象是有名项,所以hash_table可被视为是一种字典结构(dictionary). Hash_table使用名为hashfaction的散列函数来定义有名项与存储地址之间的映射关系.使用hash faction会带来一个问题:不同的有名项可能被映射到相同的地址,这便是所谓的碰撞(collision)问题,解决碰撞问题的方法主要有三种:线性探测(linear probing).二次探测(qu

STL简单&lt;stl_algorithms.h&gt;算法的实现

1.简介 STL标准中,没有区分基本算法和复杂算法,然而SGI STL却把常用的算法定义在<stl_algorithms.h>中.本文介绍部分<stl_algorithms.h>算法的实现,给出实现代码和测试代码. 本文介绍的算法包括: 1.      mismatch:比较两个序列,指出两者之间第一个不匹配的点,返回一对迭代器,分别指向两序列中不匹配的点: 2.      equal:如果两个序列在 [first, last ] 区间内相等,equal() 返回true,忽略第二

HDU 4022 Bombing(stl,map,multiset,iterater遍历)

题目 参考了     1     2 #define _CRT_SECURE_NO_WARNINGS //用的是STL中的map 和 multiset 来做的,代码写起来比较简洁,也比较好容易理解. //multiset可以允许重复 //multiset<int>::iterator it; 用来遍历 #include<stdio.h> #include<string.h> #include<algorithm> #include<iostream&g

STL简单 copy 算法的实现

1.简介 不论是对客户端或对STL内部而言,copy() 都是一个常常被调用的函数.由于copy进行的是复制操作,而复制操作不外乎运用赋值运算符(assignment operator)或复制构造函数(copy constructor),但是某些元素的类型是trivial assignment operator,因此如果能使用内存直接进行复制(例如使用C标准函数memmove.memcpy),便能节约大量时间.为此,copy算法用尽各种办法,包括函数重载(function overloading

(STL之set与multiset)SPOJ - Ada and Field(易)

ADAFIELD - Ada and Field #binary-search #datastructures Ada the Ladybug owns a beautiful field where she grows vegetables. She often visits local Farmers Market, where she buys new seeds. Since two types of vegetable can't share same field, she alway

&lt;&lt;C++标准程序库&gt;&gt;中的STL简单学习笔记

0. 内容为个人学习笔记, 仅供参考, 如有错漏, 欢迎指正! 1. STL中的所有组件都是由模板构成的, 所以其元素可以是任意型别的. 组件有: - 容器: 管理某类对象的集合. 不同的容器有各自的优缺点. - 迭代器: 用来在一个对象集群(Collection of Objects) 的元素上进行遍历. 这个CoB可以是容器/容器的一部分. 每种容器都提供了自己的迭代器. - 算法(Algorithm): 用来处理集群内的元素(比如: 查询,修改,排序等). - 适配器(adapter) -

STL中 set 和 multiset

1. 所在头文件: <set>, 命名空间: std ; 声明如下: 1 namespace std{ 2 template <class T, 3 class Compare = less<T>, 4 class Allocator = allocator<T> > 5 class set; 6 template <class T, 7 class Compare = less<T>, 8 class Allocator = alloca

C++ STL 简单记录

1,STL提供三种类型的组件:容器.迭代器.算法. 容器: 顺序容器(vector.list.deque.string等)是一系列元素的有序集合: 关联容器(set.multiset.map.multimap)包含查找元素的键值. 迭代器:作用是遍历容器. for (vector<int>::iterator it = v.begin(); it != v.end(); it++) { cout << *it << " "; } //迭代器是依附于容

10.STL简单红黑树的实现

1.红黑树简介 二叉搜索树能够提供对数的元素插入和访问.二叉搜索树的规则是:任何节点的键值一定大于其左子树的每一个节点值,并小于右子树的每一个节点值. 常见的二叉搜索树有AVL-tree.RB-tree(红黑树).红黑树具有极佳的增.删.查性能,故我们选择红黑树作为关联式容器(associative containers)的底层结构. 红黑树是每个节点都带有颜色属性的二叉查找树,颜色或红色或黑色.在二叉查找树强制一般要求以外,对于任何有效的红黑树我们增加了如下的额外要求: 1.      节点是