STL 简单红黑树的实现

这篇博客介绍了红黑树的基本概念,强调了它在STL中的重要性。作者详细阐述了红黑树在STL中的实现,包括构造与析构、空间配置器、节点实现、迭代器实现和红黑树的实现。博客还提供了测试用例,通过分析节点插入过程展示红黑树的调整策略。
摘要由CSDN通过智能技术生成

1.红黑树简介

二叉搜索树能够提供对数的元素插入和访问。二叉搜索树的规则是:任何节点的键值一定大于其左子树的每一个节点值,并小于右子树的每一个节点值。

常见的二叉搜索树有AVL-tree、RB-tree(红黑树)。红黑树具有极佳的增、删、查性能,故我们选择红黑树作为关联式容器(associative containers)的底层结构。

红黑树是每个节点都带有颜色属性的二叉查找树,颜色或红色或黑色。在二叉查找树强制一般要求以外,对于任何有效的红黑树我们增加了如下的额外要求:


1.      节点是红色或黑色。;

2.      根节点是黑色;

3.      每个叶节点(NILL节点,空节点)是黑色的;

4.      每个红色节点的两个子节点都是黑色(从每个叶子到根的所有路径上不能有两个连续的红色节点);

5.      从任一节点到其每个叶子的所有路径都包含相同数目的黑色节点


由性质4可以推出:一条路径上不能有两个毗连的红色节点。最短的可能路径都是黑色节点,最长的可能路径是交替的红色和黑色节点。又根据性质5,所有最长的路径都有相同数目的黑色节点,这就表明了没有路径能多于任何其他路径的两倍长。


以上约束条件强化了红黑树的关键性质: 从根到叶子的最长路径不多于最短路径的两倍长。所以红黑树是大致上是平衡的(不像AVL-tree,要求绝对平衡)。树的插入、删除和查找效率与树的高度成比例,红黑树的高度上限允许在最坏情况下都是高效的,这是红黑树相对于其他二叉搜索树最大的优势。


2.红黑树在STL中的实现

         要学习STL关联式容器,我们必须实现一颗红黑树。本文介绍红黑树在STL中的代码实现,为今后学习关联式容器打下基础。关于红黑树的详细特性(如增加、删除、旋转等)不在讨论范围内,请查阅相关资料。

我用VS2013写的程序(github),红黑树版本的代码位于cghSTL/version/cghSTL-0.4.0.rar

一颗STL红黑树的实现需要以下几个文件:

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

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

3.      rb_tree_node.h,红黑树节点,位于cghSTL/associative containers/RB-tree/

4.       rb_tree_iterator.h,红黑树迭代器,位于cghSTL/associative containers/RB-tree/

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

6.      test_rb_tree.cpp,红黑树的测试,位于cghSTL/test/


 

2.1构造与析构

先看第一个,globalConstruct.h构造函数文件

/*******************************************************************
*  Copyright(c) 2016 Chen Gonghao
*  All rights reserved.
*
*  chengonghao@yeah.net
*
*  功能:全局构造和析构的实现代码
******************************************************************/



#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.2空间配置器

cghAlloc.h是空间配置器文件,空间配置器负责内存的申请和回收。

/*******************************************************************
*  Copyright(c) 2016 Chen Gonghao
*  All rights reserved.
*
*  chengonghao@yeah.net
*
*  功能: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*	
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值