C++进阶:4_map和set

map和set

零.前置知识扩展

1. 搜索的方式:

(1).暴力查找
(2).排序+二分查找 -> 底层是数组,插入和删除代价大O(N)
(3).搜索树 -> 二叉搜索树(O(N)->平衡树(AVL树、红黑树)[内存]0(logN)->多叉平衡树(B树系列)[硬盘]内存磁盘
(4).哈希
(5).后续还有以表和字典树

一.关联式容器

在初阶阶段,我们已经接触过STL中的部分容器,比如:vector、list、deque、forward_list(C++11)等,这些容器统称为序列式容器,因为其底层为线性序列的数据结构,里面存储的是元素本身。那什么是关联式容器?它与序列式容器有什么区别?

关联式容器也是用来存储数据的,与序列式容器不同的是,其里面存储的是<key, value>结构的键值对,在数据检索时比序列式容器效率更高。

vector/list/deque…序列式容器;单纯的存储数据,存储的数据和数据之前没啥关联
map/set…关联式容器;不仅仅是存储数据,一般还可以查找数据,存储的数据和数据之间很强关联性

image-20250122224134904

二.键值对

**用来表示具有一一对应关系的一种结构,该结构中一般只包含两个成员变量key和value,key代表键值,value表示与key对应的信息。**比如:现在要建立一个英汉互译的字典,那该字典中必然有英文单词与其对应的中文含义,而且,英文单词与其中文含义是一一对应的关系,即通过该应该单词,在词典中就可以找到与其对应的中文含义。

SGI-STL中关于键值对的定义:

image-20250122224200181

image-20250122224222242

template <class T1, class T2>
struct pair
{
	typedef T1 first_type;
	typedef T2 second_type;
	T1 first;
	T2 second;
	pair() : first(T1()), second(T2())
	{}
	pair(const T1& a, const T2& b) : first(a), second(b)
	{}
};

image-20250122224246421

三.树形结构的关联式容器

根据应用场景的不桶,STL总共实现了两种不同结构的管理式容器:树型结构与哈希结构树型结构的关联式容器主要有四种:map、set、multimap、multiset。这四种容器的共同点是:使用平衡搜索树(即红黑树)作为其底层结果,容器中的元素是一个有序的序列。下面一依次介绍每一个容器。

(一).set

  • K模型搜索树(底层用的红黑树);
  • set不允许修改).
1. set的介绍

set文档介绍

  • 翻译:
  1. set是按照一定次序存储元素的容器
  2. 在set中,元素的value也标识它(value就是key,类型为T),并且每个value必须是唯一的。
    set中的元素不能在容器中修改(元素总是const),但是可以从容器中插入或删除它们
  3. 在内部,set中的元素总是按照其内部比较对象(类型比较)所指示的特定严格弱排序准则进行排序
  4. set容器通过key访问单个元素的速度通常比unordered_set容器慢,但它们允许根据顺序对子集进行直接迭代。
  5. set在底层是用二叉搜索树(红黑树)实现的。
  • 注意:
  1. 与map/multimap不同,map/multimap中存储的是真正的键值对<key, value>set中只放value,但在底层实际存放的是由<value, value>构成的键值对

  2. set中插入元素时,只需要插入value即可,不需要构造键值对。

  3. set中的元素不可以重复(因此可以使用set进行去重)。

  4. **使用set的迭代器遍历set中的元素,可以得到有序序列。**中序是升序

  5. set中的元素默认按照小于来比较

  6. set中查找某个元素,时间复杂度为:
    l o g 2 n log_2 n log2n

  7. set中的元素不允许修改

  8. set中的底层使用二叉搜索树(红黑树)来实现。

2. set的使用
(1).set的模板参数列表

image-20250122230037420

T: set中存放元素的类型,实际在底层存储<value, value>的键值对。
Compare:set中元素默认按照小于来比较
Alloc:set中元素空间的管理方式,使用STL提供的空间配置器管理

(2).set的构造

image-20250122230149647

image-20250122230211726

(3).set的迭代器(迭代器走的中序(默认升序))

image-20250122230343467

image-20250122230317093

(4).set的容量

image-20250122230417138

(5).set修改操作

image-20250122231450307

image-20250122230459784

image-20250122230658347

image-20250122231015033

image-20250122231024169

image-20250122231034752

image-20250122231050176

image-20250122231320715

image-20250122231431831

(6).set的使用举例
#include <set>
void TestSet()
{
	// 用数组array中的元素构造set
	int array[] = { 1, 3, 5, 7, 9, 2, 4, 6, 8, 0, 1, 3, 5, 7, 9, 2, 4, 6, 8, 0 };
	set<int> s(array, array + sizeof(array) / sizeof(array));
    //set能用vector的迭代器区间去构造,这个在库内的实现是一个模板,内部遍历vector然后插入set.
	cout << s.size() << endl;
	// 正向打印set中的元素,从打印结果中可以看出:set可去重
	for (auto& e : s)
		cout << e << " ";
	cout << endl;
	// 使用迭代器逆向打印set中的元素
	for (auto it = s.rbegin(); it != s.rend(); ++it)
		cout << *it << " ";
	cout << endl;
	// set中值为3的元素出现了几次
	cout << s.count(3) << endl;
}

(二).map

  • KV模型的搜索树(底层用红黑黑树)
1. map的介绍

map的文档简介

翻译:

  1. map是关联容器它按照特定的次序(按照key来比较)存储由键值key和值value组合而成的元素。
  2. 在map中,键值key通常用于排序和惟一地标识元素,而值value中存储与此键值key关联的内容。键值key和值value的类型可能不同,并且在map的内部,key与value通过成员类型value_type绑定在一起,为其取别名称为pair:
    typedef pair<const key, T> value_type;
  3. 在内部,map中的元素总是按照键值key进行比较排序的。
  4. map中通过键值访问单个元素的速度通常比unordered_map容器慢,但map允许根据顺序对元素进行直接迭代(即对map中的元素进行迭代时,可以得到一个有序的序列)。
  5. map支持下标访问符,即在[]中放入key,就可以找到与key对应的value。
  6. map通常被实现为二叉搜索树(更准确的说:平衡二叉搜索树(红黑树))。
2. map的使用
(1).map的模板参数说明

image-20250122234027669

image-20250122234224066

  • Compare: 比较器的类型,map中的元素是按照key来比较的,缺省情况下按照小于来比较,一般情况下(内置类型元素)该参数不需要传递,如果无法比较时(自定义类型),需要用户自己显式传递比较规则(一般情况下按照函数指针或者仿函数来传递)
  • Alloc:通过空间配置器来申请底层空间,不需要用户传递,除非用户不想使用标准库提供的空间配置器
  • 注意:在使用map时,需要包含头文件。
(2).map的构造

image-20250122234411619

(3).map的迭代器

image-20250122234432376

(4).map的容量与元素访问

image-20250122234504299

问题:当key不在map中时,通过operator获取对应value时会发生什么问题?

image-20250122234524734

注意:在元素访问时,有一个与operator[]类似的操作at()(该函数不常用)函数,都是通过key找到与key对应的value然后返回其引用,不同的是:当key不存在时,operator[]用默认value与key构造键值对然后插入,返回该默认value,at()函数直接抛异常。

(5).map中元素的修改

image-20250122234618086

image-20250122234745393

image-20250122234840225

image-20250122234941735

image-20250122235000194

image-20250122235042558

image-20250122235202883

image-20250122235255764

#include <string>
#include <map>
#include <iostream>
using namespace std;
void TestMap()
{
	map<string, string> m;
	// 向map中插入元素的方式:
	// 将键值对<"peach","桃子">插入map中,用pair直接来构造键值对
	m.insert(pair<string, string>("peach", "桃子"));
	// 将键值对<"peach","桃子">插入map中,用make_pair函数来构造键值对
	m.insert(make_pair("banan", "香蕉"));
	// 借用operator[]向map中插入元素
	/*
	operator[]的原理是:
	用<key, T()>构造一个键值对,然后调用insert()函数将该键值对插入到map中
	如果key已经存在,插入失败,insert函数返回该key所在位置的迭代器
	如果key不存在,插入成功,insert函数返回新插入元素所在位置的迭代器
	operator[]函数最后将insert返回值键值对中的value返回
	*/
	// 将<"apple", "">插入map中,插入成功,返回value的引用,将“苹果”赋值给该引	用结果,
		m["apple"] = "苹果";
	// key不存在时抛异常
	//m.at("waterme") = "水蜜桃";
	cout << m.size() << endl;
	// 用迭代器去遍历map中的元素,可以得到一个按照key排序的序列
	for (auto& e : m)
		cout << e.first << "--->" << e.second << endl;
	cout << endl;
	// map中的键值对key一定是唯一的,如果key存在将插入失败
	auto ret = m.insert(make_pair("peach", "桃色"));
	if (ret.second)
		cout << "<peach, 桃色>不在map中, 已经插入" << endl;
	else
		cout << "键值为peach的元素已经存在:" << ret.first->first << "--->"
		<< ret.first->second << " 插入失败" << endl;
	// 删除key为"apple"的元素
	m.erase("apple");
	if (1 == m.count("apple"))
		cout << "apple还在" << endl;
	else
		cout << "apple被吃了" << endl;
}
  • 【总结】
  1. map中的的元素是键值对

  2. map中的key是唯一的,并且不能修改.value可以修改(即pair中second成员)

  3. 默认按照小于的方式对key进行比较

  4. map中的元素如果用迭代器去遍历,可以得到一个有序的序列.中序遍历,按照key(即first)的升序

  5. map的底层为平衡搜索树(红黑树),查找效率比较高
    O ( l o g 2 N ) O(log_2 N) O(log2N)

  6. 支持[]操作符,operator[]中实际进行插入查找。

(三).multiset

  • 不需要刻意学习,除了插入相等的值不会失败剩下的接口与set类似
1. multiset的介绍
  • 头文件还是set
  • 和set区别:
    • 1.插入时,允许相等的值插入(允许键值冗余)
    • 2.find查找x时,多个x在树中,返回中序第一个x(注:相等的值一定是连续的)

image-20250123002124924

multiset文档介绍

  • [翻译]:
  1. multiset是按照特定顺序存储元素的容器其中元素是可以重复的
  2. 在multiset中,元素的value也会识别它(因为multiset中本身存储的就是<value, value>组成的键值对,因此value本身就是key,key就是value,类型为T). multiset元素的值不能在容器中进行修改(因为元素总是const的),但可以从容器中插入或删除。
  3. 在内部,multiset中的元素总是按照其内部比较规则(类型比较)所指示的特定严格弱排序准则进行排序。(同set)
  4. multiset容器通过key访问单个元素的速度通常比unordered_multiset容器慢,但当使用迭代器遍历时会得到一个有序序列。
  5. multiset底层结构为二叉搜索树(红黑树)。
  • 注意:
  1. multiset中再底层中存储的是<value, value>的键值对

  2. mtltiset的插入接口中只需要插入即可

  3. 与set的区别是,multiset中的元素可以重复,set是中value是唯一的

  4. 使用迭代器对multiset中的元素进行遍历,可以得到有序的序列(同set)

  5. multiset中的元素不能修改

  6. 在multiset中找某个元素,时间复杂度为
    O ( l o g 2 N ) O(log_2 N) O(log2N)

  7. multiset的作用:可以对元素进行排序

2. multiset的使用

image-20250123002437883

image-20250123002508492

image-20250123002600192

image-20250123002623520

此处只简单演示set与multiset的不同,其他接口接口与set相同,同学们可参考set。

#include <set>
void TestSet()
{
	int array[] = { 2, 1, 3, 9, 6, 0, 5, 8, 4, 7 };
	// 注意:multiset在底层实际存储的是<int, int>的键值对
	multiset<int> s(array, array + sizeof(array) / sizeof(array[0]));
	for (auto& e : s)
		cout << e << " ";
	cout << endl;
	return 0;
}

(四).multimap

  • 和map使用的唯一区别没有[]
1.multimap的介绍

multimap文档介绍

  • 翻译:
  1. Multimaps是关联式容器,它按照特定的顺序存储由key和value映射成的键值对<key,value>,其中多个键值对之间的key是可以重复的
  2. 在multimap中,通常按照key排序和惟一地标识元素,而映射的value存储与key关联的内容。key和value的类型可能不同,通过multimap内部的成员类型value_type组合在一起,value_type是组合key和value的键值对:
typedef pair<const Key, T> value_type;
  1. 在内部,multimap中的元素总是通过其内部比较对象,按照指定的特定严格弱排序标准对key进行排序的。(同map )
  2. multimap通过key访问单个元素的速度通常比unordered_multimap容器慢,但是使用迭代器直接遍历multimap中的元素可以得到关于key有序的序列。
  3. multimap在底层用二叉搜索树(红黑树)来实现。

注意:multimap和map的唯一不同就是:map中的key是唯一的,而multimap中key是可以重复的。

2. multimap的使用

image-20250123003246804

multimap中的接口可以参考map,功能都是类似的。
注意:

  1. multimap中的key是可以重复的。
  2. multimap中的元素默认将key按照小于来比较
  3. multimap中没有重载operator[]操作
  4. 使用时与map包含的头文件相同:

(五).在OJ中的使用

1.随机链表的复制
/*
// Definition for a Node.
class Node {
public:
    int val;
    Node* next;
    Node* random;
    
    Node(int _val) {
        val = _val;
        next = NULL;
        random = NULL;
    }
};
*/
class Solution {
public:
    Node* copyRandomList(Node* head) {
        map<Node*,Node*> nodeMap;
        Node* copyhead=nullptr,*copytail=nullptr;
        Node* cur=head;
        while(cur)
        {
            if(copytail==nullptr)
            {
                copyhead=copytail=new Node(cur->val);
            }
            else
            {
                copytail->next=new Node(cur->val);
                copytail=copytail->next;
            }
            //原节点和拷贝节点map kv存储
            nodeMap[cur]=copytail;
            cur=cur->next;
        }
        //处理random
        cur=head;
        Node* copy=copyhead;
        while(cur)
        {
            if(cur->random==nullptr)
            {
                copy->random=nullptr;
            }
            else
            {
                copy->random=nodeMap[cur->random];
            }
            cur=cur->next;
            copy=copy->next;
        }
        return copyhead;
    }
};
2. 环形链表 II
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode *detectCycle(ListNode *head) {
        ListNode* cur=head;
        set<ListNode*> s;
        while(cur)
        {
            auto p=s.insert(cur);
            if(p.second)
            {
                cur=cur->next;
            }
            else
            {
                return cur;
            }
        }
        return nullptr;
    }
};
3. 前K个高频单词
  • 解法一:stable_sort稳定排序
class Solution {
public:
    struct Compare {
        //升序 <
        //降序 >
        bool operator()(const pair<string, int>& x,const pair<string, int>& y)
        {
            return x.second > y.second;
        }
    };
    vector<string> topKFrequent(vector<string>& words, int k) {
        map<string, int> countMap;
        for (auto e : words)
        {
            countMap[e]++;
        }

        vector<pair<string, int>> v(countMap.begin(), countMap.end());
        stable_sort(v.begin(), v.end(), Compare());

        vector<string> strV;
        for (int i = 0; i < k; ++i)
        {
            strV.push_back(v[i].first);
        }
        return strV;
    }
};
  • 解法二:仿函数重载比较大小
class Solution {
public:
    struct Compare {
        //升序 <
        //降序 >
        bool operator()(const pair<string, int>& x,const pair<string, int>& y)
        {
            return x.second > y.second||(x.second == y.second&&x.first<y.first);
        }
    };
    vector<string> topKFrequent(vector<string>& words, int k) {
        map<string, int> countMap;
        for (auto e : words)
        {
            countMap[e]++;
        }

        vector<pair<string, int>> v(countMap.begin(), countMap.end());
        sort(v.begin(), v.end(), Compare());

        vector<string> strV;
        for (int i = 0; i < k; ++i)
        {
            strV.push_back(v[i].first);
        }
        return strV;
    }
};

image-20250123004104622

image-20250123004122120

image-20250123004142510

4. 两个数组的交集
  • 解法一:依次比较法

image-20250123004545883

class Solution {
public:
	vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
		// 先去重
		set<int> s1;
		for (auto e : nums1)
		{
			s1.insert(e);
		}
		set<int> s2;
		for (auto e : nums2)
		{
			s2.insert(e);
		}
		// set排过序,依次比较,小的一定不是交集,相等的是交集
		auto it1 = s1.begin();
		auto it2 = s2.begin();
		vector<int> ret;
		while (it1 != s1.end() && it2 != s2.end())
		{
			if (*it1 < *it2)
			{
				it1++;
			}
			else if (*it2 < *it1)
			{
				it2++;
			}
			else
			{
				ret.push_back(*it1);
				it1++;
				it2++;
			}
		}
		return ret;
	}
};
  • 解法二:先分别去重,再插入
class Solution {
public:
    vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
        set<int> s1(nums1.begin(),nums1.end());
        set<int> s2(nums2.begin(),nums2.end());
        vector<int> ret;
        for(auto e:s2)
        {
            auto p=s1.insert(e);
            if(p.second==false)
                ret.push_back(e);
        }
        return ret;
    }
};

image-20250123004602213

四.底层结构

前面对map/multimap/set/multiset进行了简单的介绍,在其文档介绍中发现,这几个容器有个共同点是:其底层都是按照二叉搜索树来实现的,但是二叉搜索树有其自身的缺陷,假如往树中插入的元素有序或者接近有序,二叉搜索树就会退化成单支树,时间复杂度会退化成O(N),因此map、set等关联式容器的底层结构是对二叉树进行了平衡处理,即采用平衡树来实现。

1. AVL 树

(1).AVL树的概念

二叉搜索树虽可以缩短查找的效率,但如果数据有序或接近有序二叉搜索树将退化为单支树,查找元素相当于在顺序表中搜索元素,效率低下。因此,两位俄罗斯的数学家G.M.Adelson-Velskii和E.M.Landis在1962年

发明了一种解决上述问题的方法:当向二叉搜索树中插入新结点后,如果能保证每个结点的左右子树高度之差的绝对值不超过1(需要对树中的结点进行调整),即可降低树的高度,从而减少平均搜索长度。

一棵AVL树或者是空树,或者是具有以下性质的二叉搜索树:

  • 它的左右子树都是AVL树
  • **左右子树高度之差(简称平衡因子)的绝对值不超过1(-1/0/1)**AVL树不是必须有平衡因子(平衡因子是它选择实现的一种方式)

image-20250504114622132

image-20250504114432496

如果一棵二叉搜索树是高度平衡的,它就是AVL树。如果它有n个结点,其高度可保持在 O ( l o g 2 n ) O(log_2 n) O(log2n),搜索时间复杂度O( l o g 2 n log_2 n log2n)。

(2).AVL树节点的定义

AVL树节点的定义:

template<class K, class V>
struct AVLTreeNode
{
	pair<K, V> _kv;
	AVLTreeNode<K, V>* _left;
	AVLTreeNode<K, V>* _right;
	AVLTreeNode<K, V>* _parent;
	int _bf;//balence factor平衡因子

	AVLTreeNode(const pair<K,V>& kv)
		:_kv(kv)
		, _left(nullptr)
		, _right(nullptr)
		, _parent(nullptr)
		, _bf(0)
	{}
};
(3).AVL树的插入

image-20250504115248143

AVL树就是在二叉搜索树的基础上引入了平衡因子,因此AVL树也可以看成是二叉搜索树。

那么AVL树的插入过程可以分为两步:

  1. 按照二叉搜索树的方式插入新节点
  2. 调整节点的平衡因子
bool Insert(const pair<K, V>& kv)
{
    if (_root == nullptr)
    {
        _root = new Node(kv);
        return true;
    }

    Node* parent = nullptr;
    Node* cur = _root;
    while (cur)
    {
        if (cur->_kv.first < kv.first)
        {
            parent = cur;
            cur = cur->_right;
        }
        else if (cur->_kv.first > kv.first)
        {
            parent = cur;
            cur = cur->_left;
        }
        else
        {
            return false;
        }
    }
    cur = new Node(kv);
    if (parent->_kv.first < kv.first)
    {
        parent->_right = cur;
    }
    else
    {
        parent->_left = cur;
    }

    cur->_parent = parent;

    //更新平衡因子
    while (parent)
    {
        if (cur == parent->_left)
            parent->_bf--;
        else
            parent->_bf++;

        if (parent->_bf == 0)
        {
            break;
        }
        else if (parent->_bf == 1 || parent->_bf == -1)
        {
            //继续向上更新
            cur = parent;
            parent = parent->_parent;
        }
        else if (parent->_bf == 2 || parent->_bf == -2)
        {
            //不平衡了,旋转处理
            if (parent->_bf == 2 && cur->_bf == 1)//插入较高右子树右侧(左旋)
            {
                RotateL(parent);
            }
            else if (parent->_bf == -2 && cur->_bf == -1)//插入较高左子树左侧(右旋)
            {
                RotateR(parent);
            }
            else if (parent->_bf == 2 && cur->_bf == -1)//插入较高右子树的左侧(先右单旋再左单旋)
            {
                RotateRL(parent);
            }
            else//插入较高左子树的右侧(先左单旋再右单旋)
            {
                RotateLR(parent);
            }

            break;
        }
        else
        {
            assert(false);
        }
    }

    return true;
}
(4).AVL树的旋转

如果在一棵原本是平衡的AVL树中插入一个新节点,可能造成不平衡,此时必须调整树的结构,使之平衡化。根据节点插入位置的不同,AVL树的旋转分为四种:

  1. 新节点插入较高左子树的左侧—左左:右单旋

image-20250504120010246

void RotateL(Node* parent)
{
    Node* subR = parent->_right;
    Node* subRL = subR->_left;

    parent->_right = subRL;
    if(subRL) //防止野指针访问
        subRL->_parent = parent;

    Node* parentParent = parent->_parent;

    subR->_left = parent;
    parent->_parent = subR;

    if (parentParent == nullptr)
    {
        _root = subR;
        subR->_parent = nullptr;
    }
    else
    {
        if (parent == parentParent->_left)
        {
            parentParent->_left = subR;
        }
        else
        {
            parentParent->_right = subR;
        }

        subR->_parent = parentParent;
    }

    parent->_bf = subR->_bf = 0;
}
  1. 新节点插入较高右子树的右侧—右右:左单旋

image-20250504124015552

image-20250504124133306

实现及情况考虑可参考右单旋。

  1. 新节点插入较高左子树的右侧—左右:先左单旋再右单旋

image-20250504124306104

将双旋变成单旋后再旋转,即:先对30进行左单旋,然后再对90进行右单旋,旋转完成后再考虑平衡因子的更新。

// 若⾼度h为0时,即上图中60即为新插⼊,60既是psubLR⼜是新插⼊数据.
// 旋转之前,60的平衡因子可能是-1/0/1,旋转完成之后,根据情况对其他节点的平衡因子进行调整
void RotateLR(Node* parent)
{
    Node* subL = parent->_left;
    Node* subLR = subL->_right;
    int bf = subLR->_bf;

    RotateL(parent->_left);
    RotateR(parent);

    if (bf == 0)//subL为新插入数据时
    {
        subL->_bf = 0;
        subLR->_bf = 0;
        parent->_bf = 0;
    }
    else if (bf == 1)
    {
        subL->_bf = -1;
        subLR->_bf = 0;
        parent->_bf = 0;
    }
    else if (bf == -1)
    {
        subL->_bf = 0;
        subLR->_bf = 0;
        parent->_bf = 1;
    }
    else
    {
        assert(false);
    }

}
  1. 新节点插入较高右子树的左侧—右左:先右单旋再左单旋

image-20250504124955784

参考右左双旋。

(5).AVL树的验证

AVL树是在二叉搜索树的基础上加入了平衡性的限制,因此要验证AVL树,可以分两步:

  1. 验证其为二叉搜索树
    如果中序遍历可得到一个有序的序列,就说明为二叉搜索树
  2. 验证其为平衡树
    • 每个节点子树高度差的绝对值不超过1(注意节点中如果没有平衡因子)
    • 节点的平衡因子是否计算正确
	bool IsBalanceTree()
	{
		return _IsBalanceTree(_root);
	}

	int Height()
	{
		return _Height(_root);
	}

private:
	int _Height(Node* root)
	{
		if (root == nullptr)
			return 0;

		int leftHeight = _Height(root->_left);
		int rightHeight = _Height(root->_right);

		return leftHeight > rightHeight ? leftHeight + 1 : rightHeight + 1;
	}

	bool _IsBalanceTree(Node* root)
	{
		// 空树也是AVL树
		if (nullptr == root) 
			return true;

		// 计算root节点的平衡因子:即root左右子树的高度差
		int leftHeight = _Height(root->_left);
		int rightHeight = _Height(root->_right);
		int diff = rightHeight - leftHeight;

		// 如果计算出的平衡因子与root的平衡因子不相等,或者root平衡因子的绝对值超过1,则一定不是AVL树
		if (abs(diff) >= 2) //abs算绝对值
		{
			cout <<root->_kv.first<< "高度差异常" << endl;
			return false;
		}

		if (root->_bf != diff)
		{
			cout << root->_kv.first << "平衡因子差异常" << endl;
			return false;
		}

		// root的左和右如果都是AVL树,则该树一定是AVL树
		return _IsBalanceTree(root->_left) && _IsBalanceTree(root->_right);
	}
  1. 验证用例

    请同学们结合上述代码按照以下的数据次序,自己动手画AVL树的创建过程,验证代码是否有漏洞。

    • 常规场景1
      {16, 3, 7, 11, 9, 26, 18, 14, 15}

    • 特殊场景2
      {4, 2, 6, 1, 3, 5, 15, 7, 16, 14}

      image-20250504130434637

(6).AVL树的删除(了解)

因为AVL树也是二叉搜索树,可按照二叉搜索树的方式将节点删除,然后再更新平衡因子,只不过与删除不同的时,删除节点后的平衡因子更新,最差情况下一直要调整到根节点的位置。

具体实现学生们可参考《算法导论》或《数据结构-用面向对象方法与C++描述》殷人昆版。

image-20250504130601095

(7).AVL树的性能

AVL树是一棵绝对平衡的二叉搜索树,其要求每个节点的左右子树高度差的绝对值都不超过1,这样可以保证查询时高效的时间复杂度,即 l o g 2 ( N ) log_2 (N) log2(N)。但是如果要对AVL树做一些结构修改的操作,性能非常低下,比如:插入时要维护其绝对平衡,旋转的次数比较多,更差的是在删除时,有可能一直要让旋转持续到根的位置。因此:如果需要一种查询高效且有序的数据结构,而且数据的个数为静态的(即不会改变),可以考虑AVL树,但一个结构经常修改,就不太适合。

(8).AVL实现
#pragma once
#include<iostream>
#include<assert.h>
#include<vector>
using namespace std;

template<class K, class V>
struct AVLTreeNode
{
	pair<K, V> _kv;
	AVLTreeNode<K, V>* _left;
	AVLTreeNode<K, V>* _right;
	AVLTreeNode<K, V>* _parent;
	int _bf;//balence factor平衡因子

	AVLTreeNode(const pair<K,V>& kv)
		:_kv(kv)
		, _left(nullptr)
		, _right(nullptr)
		, _parent(nullptr)
		, _bf(0)
	{}
};

template<class K, class V>
class AVLTree
{
	typedef AVLTreeNode<K, V> Node;
public:
	AVLTree() = default;

	AVLTree(const AVLTree<K, V>& t)
	{
		_root = Copy(t._root);
	}

	//现代写法
	AVLTree<K, V>& operator=(const AVLTree<K, V> t)
	{
		swap(_root, t._root);
		return *this;
	}

	~AVLTree()
	{
		Destroy(_root);
		_root = nullptr;
	}

	bool Insert(const pair<K, V>& kv)
	{
		if (_root == nullptr)
		{
			_root = new Node(kv);
			return true;
		}

		Node* parent = nullptr;
		Node* cur = _root;
		while (cur)
		{
			if (cur->_kv.first < kv.first)
			{
				parent = cur;
				cur = cur->_right;
			}
			else if (cur->_kv.first > kv.first)
			{
				parent = cur;
				cur = cur->_left;
			}
			else
			{
				return false;
			}
		}
		cur = new Node(kv);
		if (parent->_kv.first < kv.first)
		{
			parent->_right = cur;
		}
		else
		{
			parent->_left = cur;
		}
		
		cur->_parent = parent;
		
		//更新平衡因子
		while (parent)
		{
			if (cur == parent->_left)
				parent->_bf--;
			else
				parent->_bf++;

			if (parent->_bf == 0)
			{
				break;
			}
			else if (parent->_bf == 1 || parent->_bf == -1)
			{
				//继续向上更新
				cur = parent;
				parent = parent->_parent;
			}
			else if (parent->_bf == 2 || parent->_bf == -2)
			{
				//不平衡了,旋转处理
				if (parent->_bf == 2 && cur->_bf == 1)//插入较高右子树右侧(左旋)
				{
					RotateL(parent);
				}
				else if (parent->_bf == -2 && cur->_bf == -1)//插入较高左子树左侧(右旋)
				{
					RotateR(parent);
				}
				else if (parent->_bf == 2 && cur->_bf == -1)//插入较高右子树的左侧(先右单旋再左单旋)
				{
					RotateRL(parent);
				}
				else//插入较高左子树的右侧(先左单旋再右单旋)
				{
					RotateLR(parent);
				}

				break;
			}
			else
			{
				assert(false);
			}
		}
		
		return true;
	}

	Node* Find(const K& key)
	{
		Node* cur = _root;
		while (cur)
		{
			if (cur->_kv.first < key)
			{
				cur = cur->_right;
			}
			else if (cur->_kv.first > key)
			{
				cur = cur->_left;
			}
			else
			{
				return cur;
			}
		}
		return nullptr;
	}

	void InOrder()
	{
		_InOrder(_root);
		cout << endl;
	}

	bool IsBalanceTree()
	{
		return _IsBalanceTree(_root);
	}

	int Height()
	{
		return _Height(_root);
	}

	int Size()
	{
		return _Size(_root);
	}

private:
	int _Size(Node* root)
	{
		if (root == nullptr)
			return 0;

		return _Size(root->_left) + _Size(root->_right) + 1;
	}

	int _Height(Node* root)
	{
		if (root == nullptr)
			return 0;

		int leftHeight = _Height(root->_left);
		int rightHeight = _Height(root->_right);

		return leftHeight > rightHeight ? leftHeight + 1 : rightHeight + 1;
	}

	bool _IsBalanceTree(Node* root)
	{
		// 空树也是AVL树
		if (nullptr == root) 
			return true;

		// 计算root节点的平衡因子:即root左右子树的高度差
		int leftHeight = _Height(root->_left);
		int rightHeight = _Height(root->_right);
		int diff = rightHeight - leftHeight;

		// 如果计算出的平衡因子与root的平衡因子不相等,或者root平衡因子的绝对值超过1,则一定不是AVL树
		if (abs(diff) >= 2) //abs算绝对值
		{
			cout <<root->_kv.first<< "高度差异常" << endl;
			return false;
		}

		if (root->_bf != diff)
		{
			cout << root->_kv.first << "平衡因子差异常" << endl;
			return false;
		}

		// root的左和右如果都是AVL树,则该树一定是AVL树
		return _IsBalanceTree(root->_left) && _IsBalanceTree(root->_right);
	}

	void _InOrder(Node* root)	//root不能弄成缺省值,this是形参这块用不了
	{							//3种办法:1.友元2.get3.套一层
		if (root == nullptr)	//类里面写递归都会套一层
		{
			return;
		}
		_InOrder(root->_left);
		cout << root->_kv.first << ":" << root->_kv.second  << endl;
		_InOrder(root->_right);
	}

	void RotateL(Node* parent)
	{
		Node* subR = parent->_right;
		Node* subRL = subR->_left;

		parent->_right = subRL;
		if(subRL) //防止野指针访问
			subRL->_parent = parent;

		Node* parentParent = parent->_parent;

		subR->_left = parent;
		parent->_parent = subR;

		if (parentParent == nullptr)
		{
			_root = subR;
			subR->_parent = nullptr;
		}
		else
		{
			if (parent == parentParent->_left)
			{
				parentParent->_left = subR;
			}
			else
			{
				parentParent->_right = subR;
			}

			subR->_parent = parentParent;
		}

		parent->_bf = subR->_bf = 0;
	}

	void RotateR(Node* parent)
	{
		Node* subL = parent->_left;
		Node* subLR = subL->_right;

		parent->_left = subLR;
		if (subLR) //防止野指针访问
			subLR->_parent = parent;

		Node* parentParent = parent->_parent;

		subL->_right = parent;
		parent->_parent = subL;

		if (parentParent == nullptr)
		{
			_root = subL;
			subL->_parent = nullptr;
		}
		else
		{
			if (parent == parentParent->_left)
			{
				parentParent->_left = subL;
			}
			else
			{
				parentParent->_right = subL;
			}

			subL->_parent = parentParent;
		}
		parent->_bf = subL->_bf = 0;
	}

	void RotateRL(Node* parent)
	{
		Node* subR = parent->_right;
		Node* subRL = subR->_left;
		int bf = subRL->_bf;

		RotateR(parent->_right);
		RotateL(parent);

		if (bf == 0)//subR为新插入数据时
		{
			subR->_bf = 0;
			subRL->_bf = 0;
			parent->_bf = 0;
		}
		else if (bf == 1)
		{
			subR->_bf = 0;
			subRL->_bf = 0;
			parent->_bf = -1;
		}
		else if (bf == -1)
		{
			subR->_bf = 1;
			subRL->_bf = 0;
			parent->_bf = 0;
		}
		else
		{
			assert(false);
		}
	}

	void RotateLR(Node* parent)
	{
		Node* subL = parent->_left;
		Node* subLR = subL->_right;
		int bf = subLR->_bf;

		RotateL(parent->_left);
		RotateR(parent);

		if (bf == 0)//subL为新插入数据时
		{
			subL->_bf = 0;
			subLR->_bf = 0;
			parent->_bf = 0;
		}
		else if (bf == 1)
		{
			subL->_bf = -1;
			subLR->_bf = 0;
			parent->_bf = 0;
		}
		else if (bf == -1)
		{
			subL->_bf = 0;
			subLR->_bf = 0;
			parent->_bf = 1;
		}
		else
		{
			assert(false);
		}

	}

	void Destroy(Node* root)
	{
		if (root == nullptr)
			return;

		Destroy(root->_left);
		Destroy(root->_right);
		delete root;
	}

	Node* Copy(Node* root)
	{
		if (root == nullptr)
			return nullptr;
		Node* newRoot = new Node(root->_kv);
		newRoot->_left = Copy(root->_left);
		newRoot->_right = Copy(root->_right);
		return newRoot;
	}
private:
	Node* _root = nullptr;
};

2. 红黑树

(1).红黑树的概念

红黑树,是一种二叉搜索树,但在每个结点上增加一个存储位表示结点的颜色,可以是Red或Black。 通过对任何一条从根到叶子的路径上各个结点着色方式的限制,红黑树确保没有一条路径会比其他路径长出俩倍,因而是接近平衡的。

image-20250504131214660

image-20250504131225800

(2).红黑树的性质
  1. 每个结点不是红色就是黑色
  2. 根节点是黑色的
  3. 如果一个节点是红色的,则它的两个孩子结点是黑色的,即:一条路径中,没有连续红色节点
  4. 对于每个结点,从该结点到其所有后代叶结点的简单路径上,均 包含相同数目的黑色结点,即:每条路径黑色节点数量是相等
  5. 每个叶子结点都是黑色的(此处的叶子结点指的是空结点–NIL结点)

思考:为什么满足上面的性质,红黑树就能保证:其最长路径中节点个数不会超过最短路径节点个数的两倍?

*最短路径 2>=最长路径

  • 注:

AVL – 严格平衡
红黑 – 近似平衡

(3).红黑树节点的定义
// 节点的颜色
enum Color{RED, BLACK};
// 红黑树节点的定义
template<class ValueType>
struct RBTreeNode
{
 RBTreeNode(const ValueType& data = ValueType(),Color color = RED)
     : _pLeft(nullptr), _pRight(nullptr), _pParent(nullptr)
     , _data(data), _color(color)
 {}
 RBTreeNode<ValueType>* _pLeft;   // 节点的左孩子
 RBTreeNode<ValueType>* _pRight;  // 节点的右孩子
 RBTreeNode<ValueType>* _pParent; // 节点的双亲(红黑树需要旋转,为了实现简单给出该字段)
 ValueType _data;            // 节点的值域
 Color _color;               // 节点的颜色
};

思考:在节点的定义中,为什么要将节点的默认颜色给成红色的?

答:插入黑色结点一定违反规则4,且难调整;插入红色结点用可能规则3/2,容易调整,有可能不违反任何规则。

(4).红黑树结构

为了后续实现关联式容器简单,红黑树的实现中增加一个头结点,因为跟节点必须为黑色,为了与根节点进行区分,将头结点给成黑色,并且让头结点的 pParent 域指向红黑树的根节点,pLeft域指向红黑树中最小的节点,_pRight域指向红黑树中最大的节点,如下:

image-20250504214109509

(5).红黑树的插入操作

红黑树是在二叉搜索树的基础上加上其平衡限制条件,因此红黑树的插入可分为两步:

  1. 按照二叉搜索的树规则插入新节点

    template<class ValueType>
    class RBTree
    {
        //……
     bool Insert(const ValueType& data)
     {
     PNode& pRoot = GetRoot();
     if (nullptr == pRoot)
     {
     pRoot = new Node(data, BLACK);
     // 根的双亲为头节点
     pRoot->_pParent = _pHead;
                _pHead->_pParent = pRoot;
     }
     else
     {
     // 1. 按照二叉搜索的树方式插入新节点
                 // 2. 检测新节点插入后,红黑树的性质是否造到破坏,
     //   若满足直接退出,否则对红黑树进行旋转着色处理
     }
     // 根节点的颜色可能被修改,将其改回黑色
     pRoot->_color = BLACK;
     _pHead->_pLeft = LeftMost();
     _pHead->_pRight = RightMost();
     return true;
     }
    private:
     PNode& GetRoot(){ return _pHead->_pParent;}
     // 获取红黑树中最小节点,即最左侧节点
     PNode LeftMost();
     // 获取红黑树中最大节点,即最右侧节点
     PNode RightMost();
    private:
     PNode _pHead;
    };
    
  2. 检测新节点插入后,红黑树的性质是否造到破坏
    因为新节点的默认颜色是红色,因此:如果其双亲节点的颜色是黑色,没有违反红黑树任何性质,则不需要调整;但当新插入节点的双亲节点颜色为红色时,就违反了性质三不能有连在一起的红色节点,此时需要对红黑树分情况来讨论

    约定:cur为当前节点,p为父节点,g为祖父节点,u为叔叔节点

    parent,cur一定是红色,grandfather一定为黑色;uncle可能为黑可能为红可能不存在.

    • 情况一: cur为红,p为红,g为黑,u存在且为红

    image-20250504214747332

    cur和p均为红,违反了性质三,此处能否将p直接改为黑?

    解决方式:将p,u改为黑,g改为红,然后把g当成cur,继续向上调整。

    • 情况二: cur为红,p为红,g为黑,u不存在/u存在且为黑,cur是p左孩子

    image-20250504214942209

    解决方式:

    p为g的左孩子,cur为p的左孩子,则进行右单旋转;相反,
    p为g的右孩子,cur为p的右孩子,则进行左单旋转
    p、g变色–p变黑,g变红

    • 情况三: cur为红,p为红,g为黑,u不存在/u存在且为黑,cur是p右孩子

    image-20250504215107031

    解决方式:

    p为g的左孩子,cur为p的右孩子,则针对p做左单旋转;相反,
    p为g的右孩子,cur为p的左孩子,则针对p做右单旋转
    则转换成了情况2

  3. 红黑树的删除
    红黑树的删除本节不做讲解,有兴趣的同学可参考:《算法导论》或者《STL源码剖析》
    http://www.cnblogs.com/fornever/archive/2011/12/02/2270692.htm

  4. 红黑树与AVL树的比较
    红黑树和AVL树都是高效的平衡二叉树,增删改查的时间复杂度都是O( l o g 2 N log_2 N log2N),红黑树不追求绝对平衡,其只需保证最长路径不超过最短路径的2倍,相对而言,降低了插入和旋转的次数,所以在经常进行增删的结构中性能比AVL树更优,而且红黑树实现比较简单,所以实际运用中红黑树更多。

  5. 红黑树的应用

    1. C++ STL库 – map/set、mutil_map/mutil_set
    2. Java 库
    3. linux内核
    4. 其他一些库

    http://www.cnblogs.com/yangecnu/p/Introduce-Red-Black-Tree.htm

  6. 红黑树实现

#pragma once
#include<iostream>
#include<assert.h>
#include<vector>
using namespace std;

enum Colour
{
	RED,
	BLACK
};

template<class K, class V>
struct RBTreeNode
{
	pair<K, V> _kv;
	RBTreeNode<K, V>* _left;
	RBTreeNode<K, V>* _right;
	RBTreeNode<K, V>* _parent;
	Colour _col;

	RBTreeNode(const pair<K, V>& kv)
		: _kv(kv)
		, _left(nullptr)
		, _right(nullptr)
		, _parent(nullptr)
	{}
};

template<class K, class V>
class RBTree
{
	typedef RBTreeNode<K, V> Node;
public:
	RBTree() = default;

	RBTree(const RBTree<K, V>& t)
	{
		_root = Copy(t._root);
	}

	//现代写法
	RBTree<K, V>& operator=(const RBTree<K, V> t)
	{
		swap(_root, t._root);
		return *this;
	}

	~RBTree()
	{
		Destroy(_root);
		_root = nullptr;
	}

	bool Insert(const pair<K, V>& kv)
	{
		if (_root == nullptr)
		{
			_root = new Node(kv);
			_root->_col = BLACK;
			return true;
		}

		Node* parent = nullptr;
		Node* cur = _root;
		while (cur)
		{
			if (cur->_kv.first < kv.first)
			{
				parent = cur;
				cur = cur->_right;
			}
			else if (cur->_kv.first > kv.first)
			{
				parent = cur;
				cur = cur->_left;
			}
			else
			{
				return false;
			}
		}
		cur = new Node(kv);
		// 新增结点颜色给红色
		cur->_col = RED;
		if (parent->_kv.first < kv.first)
		{
			parent->_right = cur;
		}
		else
		{
			parent->_left = cur;
		}
		cur->_parent = parent;

		while (parent && parent->_col == RED)
		{
			Node* grandfather = parent->_parent;
			if (parent == grandfather->_left)
			{
				//    g
				//  p   u
				Node* uncle = grandfather->_right;

				if (uncle && uncle->_col == RED)
				{
					//u存在且为红->变色再继续往上处理
					parent->_col = uncle->_col = BLACK;
					grandfather->_col = RED;
					// 继续往上处理
					cur = grandfather;
					parent = cur->_parent;
					//1.parent不存在,cur就是根了,出去后把根处理成黑的,结束
					//2.parent存在,且为黑,结束
					//3.parent存在,且为红,继续循环处理
				}
				
				else
				{
					//u存在且为黑或不存在->旋转+变色
					if (cur == parent->_left)
					{
						//单旋
						//    g
						//  p   u
						//c
						RotateR(grandfather);
						parent->_col = BLACK;
						grandfather->_col = RED;
					}
					else
					{
						//双旋
						//    g
						//  p   u
						//    c
						RotateL(parent);
						RotateR(grandfather);
						cur->_col = BLACK;
						grandfather->_col = RED;
					}
					break;
				}
			}

			else
			{
				//    g
				//  u   p
				Node* uncle = grandfather->_left;

				if (uncle && uncle->_col == RED)
				{
					//u存在且为红->变色再继续往上处理
					parent->_col = uncle->_col = BLACK;
					grandfather->_col = RED;
					// 继续往上处理
					cur = grandfather;
					parent = cur->_parent;
					//1.parent不存在,cur就是根了,出去后把根处理成黑的,结束
					//2.parent存在,且为黑,结束
					//3.parent存在,且为红,继续循环处理
				}

				else
				{
					//u存在且为黑或不存在->旋转+变色
					if (cur == parent->_right)
					{
						//单旋
						//    g
						//  u   p
						//		  c
						RotateL(grandfather);
						parent->_col = BLACK;
						grandfather->_col = RED;
					}
					else
					{
						//双旋
						//    g
						//  u   p
						//    c
						RotateR(parent);
						RotateL(grandfather);
						cur->_col = BLACK;
						grandfather->_col = RED;
					}
					break;
				}
			}
		}

		_root->_col = BLACK;

		return true;
	}

	void InOrder()
	{
		_InOrder(_root);
		cout << endl;
	}

	int Height()
	{
		return _Height(_root);
	}

	int Size()
	{
		return _Size(_root);
	}

	bool IsBalance()
	{
		if (_root == nullptr)
			return true;

		if (_root->_col == RED)
		{
			return false;
		}

		//随便找一条路径做参考值
		int refNum = 0;
		Node* cur = _root;
		while (cur)
		{
			if (cur->_col == BLACK)
			{
				++refNum;
			}

			cur = cur->_left;
		}

		return Check(_root, 0, refNum);
	}

	Node* Find(const K& key)
	{
		Node* cur = _root;
		while (cur)
		{
			if (cur->_kv.first < key)
			{
				cur = cur->_right;
			}
			else if (cur->_kv.first > key)
			{
				cur = cur->_left;
			}
			else
			{
				return cur;
			}
		}
		return nullptr;
	}

private:
	bool Check(Node* root, int blackNum, const int refNum)
	{
		if (root == nullptr)
		{
			//cout << blackNum << endl;
			if (refNum != blackNum)
			{
				cout << "存在黑色节点的数量不相等的路径" << endl;
				return false;
			}

			return true;
		}

		if (root->_col == RED && root->_parent->_col == RED)
		{
			cout << root->_kv.first << "存在连续的红色节点" << endl;
			return false;
		}

		if (root->_col == BLACK)
		{
			blackNum++;
		}

		return Check(root->_left, blackNum, refNum)
			&& Check(root->_right, blackNum, refNum);
	}

	int _Size(Node* root)
	{
		if (root == nullptr)
			return 0;

		return _Size(root->_left) + _Size(root->_right) + 1;
	}

	int _Height(Node* root)
	{
		if (root == nullptr)
			return 0;

		int leftHeight = _Height(root->_left);
		int rightHeight = _Height(root->_right);

		return leftHeight > rightHeight ? leftHeight + 1 : rightHeight + 1;
	}



	void _InOrder(Node* root)	//root不能弄成缺省值,this是形参这块用不了
	{							//3种办法:1.友元2.get3.套一层
		if (root == nullptr)	//类里面写递归都会套一层
		{
			return;
		}
		_InOrder(root->_left);
		cout << root->_kv.first << ":" << root->_kv.second << endl;
		_InOrder(root->_right);
	}

	void RotateL(Node* parent)
	{
		Node* subR = parent->_right;
		Node* subRL = subR->_left;

		parent->_right = subRL;
		if (subRL) //防止野指针访问
			subRL->_parent = parent;

		Node* parentParent = parent->_parent;

		subR->_left = parent;
		parent->_parent = subR;

		if (parentParent == nullptr)
		{
			_root = subR;
			subR->_parent = nullptr;
		}
		else
		{
			if (parent == parentParent->_left)
			{
				parentParent->_left = subR;
			}
			else
			{
				parentParent->_right = subR;
			}

			subR->_parent = parentParent;
		}
	}

	void RotateR(Node* parent)
	{
		Node* subL = parent->_left;
		Node* subLR = subL->_right;

		parent->_left = subLR;
		if (subLR) //防止野指针访问
			subLR->_parent = parent;

		Node* parentParent = parent->_parent;

		subL->_right = parent;
		parent->_parent = subL;

		if (parentParent == nullptr)
		{
			_root = subL;
			subL->_parent = nullptr;
		}
		else
		{
			if (parent == parentParent->_left)
			{
				parentParent->_left = subL;
			}
			else
			{
				parentParent->_right = subL;
			}

			subL->_parent = parentParent;
		}
	}

	void Destroy(Node* root)
	{
		if (root == nullptr)
			return;

		Destroy(root->_left);
		Destroy(root->_right);
		delete root;
	}

	Node* Copy(Node* root)
	{
		if (root == nullptr)
			return nullptr;
		Node* newRoot = new Node(root->_kv);
		newRoot->_left = Copy(root->_left);
		newRoot->_right = Copy(root->_right);
		return newRoot;
	}
private:
	Node* _root = nullptr;
};
  1. 红黑树模拟实现STL中的map与set
  • RBTree.h
#pragma once
#include<iostream>
#include<assert.h>
#include<vector>
using namespace std;

enum Colour
{
	RED,
	BLACK
};

template<class T> 
struct RBTreeNode
{
	T _data;
	RBTreeNode<T>* _left;
	RBTreeNode<T>* _right;
	RBTreeNode<T>* _parent;
	Colour _col;

	RBTreeNode(const T& data)
		: _data(data)
		, _left(nullptr)
		, _right(nullptr)
		, _parent(nullptr)
	{}
};

template<class T,class Ref,class Ptr>
struct RBTreeIterator
{
	typedef RBTreeNode<T> Node;
	typedef RBTreeIterator<T, Ref, Ptr> Self;

	Node* _node;
	Node* _root;//解决--end()用

	RBTreeIterator() = default;

	RBTreeIterator(Node* node, Node* root)
		:_node(node)
		,_root(root)
	{}

	Self& operator++()
	{
		//1.右子树不为空
		if (_node->_right)
		{
			Node* leftMost = _node->_right;
			while (leftMost->_left)
			{
				leftMost = leftMost->_left;
			}
			_node = leftMost;
		}
		//2.右子树为空(代表当前节点所在的子树访问完了)
		//沿着到根节点的路径查找,孩子是父亲左的那个祖先节点就是下一个要访问的节点
		else
		{
			Node* cur = _node;
			Node* parent = _node->_parent;
			while (parent && cur == parent->_right)
			{
				cur = parent;
				parent = parent->_parent;
			}
			_node = parent;
		}
		return *this;
	}

	Self& operator--()
	{
		// --end(),特殊处理,走到中序最后一个节点,整棵树的最右节点
		if (_node == nullptr)
		{
			Node* rightMost = _root;
			while (rightMost && rightMost->_right)
			{
				rightMost = rightMost->_right;
			}
			_node = rightMost;
		}

		//1.左子树不为空
		else if (_node->_left)
		{
			Node* rightMost = _node->_left;
			while (rightMost->_right)
			{
				rightMost = rightMost->_right;
			}
			_node = rightMost;
		}
		//2.左子树为空(代表当前节点所在的子树访问完了)
		//沿着到根节点的路径查找,孩子是父亲右的那个祖先节点就是下一个要访问的节点
		else
		{
			Node* cur = _node;
			Node* parent = _node->_parent;
			while (parent && cur == parent->_left)
			{
				cur = parent;
				parent = parent->_parent;
			}
			_node = parent;
		}
		return *this;
	}

	Ref& operator*()
	{
		return _node->_data;
	}

	Ptr operator->()
	{
		return &_node->_data;
	}

	bool operator!=(const Self& s)
	{
		return s._node != _node;
	}

	bool operator==(const Self& s)
	{
		return s._node == _node;
	}
};

//第一个模板参数传的都是key;
//set第二个模板参数传的还是key;map第二个模板参数传的pair<key, value>;
template<class K, class T,class KeyOfT>//K是Find,Erase用的;T是Insert,Node用的;KeyOfT拿出T中的key比较时用;
class RBTree
{
	typedef RBTreeNode<T> Node;
public:
	typedef RBTreeIterator<T, T&, T*> Iterator;
	typedef RBTreeIterator<T, const T&, const T*> ConstIterator;

	ConstIterator Begin() const
	{
		Node* leftMost = _root;
		while (leftMost && leftMost->_left)
		{
			leftMost = leftMost->_left;
		}
		return ConstIterator(leftMost,_root);
	}

	ConstIterator End() const
	{
		return ConstIterator(nullptr,_root);
	}

	Iterator Begin()
	{
		Node* leftMost = _root;
		while (leftMost && leftMost->_left)
		{
			leftMost = leftMost->_left;
		}
		return Iterator(leftMost, _root);
	}

	Iterator End()
	{
		return Iterator(nullptr, _root);
	}

	RBTree() = default;

	//类里面可以直接写类名,不写模板参数
	//RBTree<K, T, KeyOfT>等价RBTree
	RBTree(const RBTree& t)
	{
		_root = Copy(t._root);
	}

	//现代写法
	RBTree& operator=(const RBTree t)
	{
		swap(_root, t._root);
		return *this;
	}

	~RBTree()
	{
		Destroy(_root);
		_root = nullptr;
	}

	pair<Iterator,bool> Insert(const T& data)
	{
		if (_root == nullptr)
		{
			_root = new Node(data);
			_root->_col = BLACK;
			return { Begin(),true }; //等价return make_pair(Iterator(_root,_root),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 make_pair(Iterator(cur, _root), false);
			}
		}
		cur = new Node(data);
		Node* newnode = cur;

		// 新增结点颜色给红色
		cur->_col = RED;
		if (kot(parent->_data) < kot(data))
		{
			parent->_right = cur;
		}
		else
		{
			parent->_left = cur;
		}
		cur->_parent = parent;

		while (parent && parent->_col == RED)
		{
			Node* grandfather = parent->_parent;
			if (parent == grandfather->_left)
			{
				//    g
				//  p   u
				Node* uncle = grandfather->_right;

				if (uncle && uncle->_col == RED)
				{
					//u存在且为红->变色再继续往上处理
					parent->_col = uncle->_col = BLACK;
					grandfather->_col = RED;
					// 继续往上处理
					cur = grandfather;
					parent = cur->_parent;
					//1.parent不存在,cur就是根了,出去后把根处理成黑的,结束
					//2.parent存在,且为黑,结束
					//3.parent存在,且为红,继续循环处理
				}

				else
				{
					//u存在且为黑或不存在->旋转+变色
					if (cur == parent->_left)
					{
						//单旋
						//    g
						//  p   u
						//c
						RotateR(grandfather);
						parent->_col = BLACK;
						grandfather->_col = RED;
					}
					else
					{
						//双旋
						//    g
						//  p   u
						//    c
						RotateL(parent);
						RotateR(grandfather);
						cur->_col = BLACK;
						grandfather->_col = RED;
					}
					break;
				}
			}

			else
			{
				//    g
				//  u   p
				Node* uncle = grandfather->_left;

				if (uncle && uncle->_col == RED)
				{
					//u存在且为红->变色再继续往上处理
					parent->_col = uncle->_col = BLACK;
					grandfather->_col = RED;
					// 继续往上处理
					cur = grandfather;
					parent = cur->_parent;
					//1.parent不存在,cur就是根了,出去后把根处理成黑的,结束
					//2.parent存在,且为黑,结束
					//3.parent存在,且为红,继续循环处理
				}

				else
				{
					//u存在且为黑或不存在->旋转+变色
					if (cur == parent->_right)
					{
						//单旋
						//    g
						//  u   p
						//		  c
						RotateL(grandfather);
						parent->_col = BLACK;
						grandfather->_col = RED;
					}
					else
					{
						//双旋
						//    g
						//  u   p
						//    c
						RotateR(parent);
						RotateL(grandfather);
						cur->_col = BLACK;
						grandfather->_col = RED;
					}
					break;
				}
			}
		}

		_root->_col = BLACK;

		return make_pair(Iterator(newnode, _root), true);
	}

	void InOrder()
	{
		_InOrder(_root);
		cout << endl;
	}

	int Height()
	{
		return _Height(_root);
	}

	int Size()
	{
		return _Size(_root);
	}

	bool IsBalance()
	{
		if (_root == nullptr)
			return true;

		if (_root->_col == RED)
		{
			return false;
		}

		//随便找一条路径做参考值
		int refNum = 0;
		Node* cur = _root;
		while (cur)
		{
			if (cur->_col == BLACK)
			{
				++refNum;
			}

			cur = cur->_left;
		}

		return Check(_root, 0, refNum);
	}

	Iterator Find(const K& key)
	{
		Node* cur = _root;
		while (cur)
		{
			if (cur->_kv.first < key)
			{
				cur = cur->_right;
			}
			else if (cur->_kv.first > key)
			{
				cur = cur->_left;
			}
			else
			{
				return Iterator(cur,_root);
			}
		}
		return End();
	}

private:
	bool Check(Node* root, int blackNum, const int refNum)
	{
		if (root == nullptr)
		{
			//cout << blackNum << endl;
			if (refNum != blackNum)
			{
				cout << "存在黑色节点的数量不相等的路径" << endl;
				return false;
			}

			return true;
		}

		if (root->_col == RED && root->_parent->_col == RED)
		{
			cout << root->_kv.first << "存在连续的红色节点" << endl;
			return false;
		}

		if (root->_col == BLACK)
		{
			blackNum++;
		}

		return Check(root->_left, blackNum, refNum)
			&& Check(root->_right, blackNum, refNum);
	}

	int _Size(Node* root)
	{
		if (root == nullptr)
			return 0;

		return _Size(root->_left) + _Size(root->_right) + 1;
	}

	int _Height(Node* root)
	{
		if (root == nullptr)
			return 0;

		int leftHeight = _Height(root->_left);
		int rightHeight = _Height(root->_right);

		return leftHeight > rightHeight ? leftHeight + 1 : rightHeight + 1;
	}



	void _InOrder(Node* root)	//root不能弄成缺省值,this是形参这块用不了
	{							//3种办法:1.友元2.get3.套一层
		if (root == nullptr)	//类里面写递归都会套一层
		{
			return;
		}
		_InOrder(root->_left);
		cout << root->_kv.first << ":" << root->_kv.second << endl;
		_InOrder(root->_right);
	}

	void RotateL(Node* parent)
	{
		Node* subR = parent->_right;
		Node* subRL = subR->_left;

		parent->_right = subRL;
		if (subRL) //防止野指针访问
			subRL->_parent = parent;

		Node* parentParent = parent->_parent;

		subR->_left = parent;
		parent->_parent = subR;

		if (parentParent == nullptr)
		{
			_root = subR;
			subR->_parent = nullptr;
		}
		else
		{
			if (parent == parentParent->_left)
			{
				parentParent->_left = subR;
			}
			else
			{
				parentParent->_right = subR;
			}

			subR->_parent = parentParent;
		}
	}

	void RotateR(Node* parent)
	{
		Node* subL = parent->_left;
		Node* subLR = subL->_right;

		parent->_left = subLR;
		if (subLR) //防止野指针访问
			subLR->_parent = parent;

		Node* parentParent = parent->_parent;

		subL->_right = parent;
		parent->_parent = subL;

		if (parentParent == nullptr)
		{
			_root = subL;
			subL->_parent = nullptr;
		}
		else
		{
			if (parent == parentParent->_left)
			{
				parentParent->_left = subL;
			}
			else
			{
				parentParent->_right = subL;
			}

			subL->_parent = parentParent;
		}
	}

	void Destroy(Node* root)
	{
		if (root == nullptr)
			return;

		Destroy(root->_left);
		Destroy(root->_right);
		delete root;
	}

	Node* Copy(Node* root)
	{
		if (root == nullptr)
			return nullptr;
		Node* newRoot = new Node(root->_kv);
		newRoot->_left = Copy(root->_left);
		newRoot->_right = Copy(root->_right);
		return newRoot;
	}
private:
	Node* _root = nullptr;
};
  • MySet.h
#pragma once

#include"RBTree.h"

namespace bit
{
	template<class K>
	class set
	{
		struct SetKeyOfT
		{
			const K& operator()(const K& key)
			{
				return key;
			}
		};
	public:
		typedef  typename RBTree<K, const K, SetKeyOfT>::Iterator iterator;//模板没有实例化,不知道是静态成员变量还是类型
		typedef  typename RBTree<K, const K, SetKeyOfT>::ConstIterator const_iterator;//模板没有实例化,不知道是静态成员变量还是类型

		iterator begin()
		{
			return _t.Begin();
		}

		iterator end()
		{
			return _t.End();
		}

		const_iterator begin() const
		{
			return _t.Begin();
		}

		const_iterator end() const
		{
			return _t.End();
		}

		pair<iterator, bool> insert(const K& key)
		{
			return _t.Insert(key);
		}

		iterator find(const K& key)
		{
			return _t.Find(key);
		}

	private:
		RBTree<K, const K, SetKeyOfT> _t;
	};
}
  • MyMap.h
#pragma once

#include"RBTree.h"

namespace bit
{
	template<class K,class V>
	class map
	{
		struct MapKeyOfT
		{
			const K& operator()(const pair<K, V>& kv)
			{
				return kv.first;
			}
		};
	public:
		typedef  typename RBTree<K, pair<const K, V>, MapKeyOfT>::Iterator iterator;//模板没有实例化,不知道是静态成员变量还是类型
		typedef  typename RBTree<K, pair<const K, V>, MapKeyOfT>::ConstIterator const_iterator;//模板没有实例化,不知道是静态成员变量还是类型

		iterator begin()
		{
			return _t.Begin();
		}

		iterator end()
		{
			return _t.End();
		}

		const_iterator begin() const
		{
			return _t.Begin();
		}

		const_iterator end() const
		{
			return _t.End();
		}

		pair<iterator, bool> insert(const pair<K, V>& kv)
		{
			return _t.Insert(kv);
		}

		iterator find(const K& key)
		{
			return _t.Find(key);
		}

		V& operator[](const K& key)
		{
			pair<iterator, bool> ret = insert({ key,V() });//等价_t.insert(make_pair(key,V()));
			return  (ret.first)->second;
		}
	private:
		RBTree<K, pair<const K,V>, MapKeyOfT> _t;
	};
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值