目录
序列是容器:vector、list、deque、forward_list等,主要用来存储数据的。
关联式容器也是用来存储数据的,与序列式容器不同的是,其里面存储的是
键值对:用来表示具有一一对应关系的一种结构,该结构中一般只包含两个成员变量key和value,key代表键值,value表示与key对应的消息。
set是去重+排序

1.insert 插入函数以及迭代器:

- #include
- #include
- using namespace std;
-
- void test1()
- {
- set<int> s;
- s.insert(1);
- s.insert(3);
- s.insert(2);
- s.insert(4);
- s.insert(5);
- //插入两个相等值时,直接去重
- s.insert(6);
- s.insert(6);
- s.insert(8);
-
- //迭代器 为中序
- set<int>::iterator it = s.begin();
- while (it != s.end())
- {
- cout << *it << " ";
- ++it;
- }
- cout << endl;
- }
- int main()
- {
- test1();
- return 0;
- }
迭代器默认打印就是中序打印:

2.erase 删除函数


值删除和迭代器删除的区别:
size_type erase (const value_type& val);
会返回一个size_type,是一个无符号整数,返回删除的数据个数。

在set传值删除的时候,只有1或0.
- #include
- #include
- using namespace std;
- void test2()
- {
- set<int> s;
- s.insert(1);
- s.insert(3);
- s.insert(2);
- s.insert(4);
- s.insert(5);
- s.insert(6);
- s.insert(8);
- //迭代器删除
- set<int>::iterator pos = s.find(3);
- if (pos!=s.end()) //这里是必须检查的,必须保证迭代器有效
- {
- s.erase(pos);
- }
- //set
::iterator pos = s.find(30); - //s.erase(pos); //这里删除30 不存在的,迭代器失效会报错的
-
- //直接值删除,是不用考虑迭代器失效的问题
- //当值存在时我就删除。当值不存在时我也不会报错,不会删除。
- s.erase(30);//这里不会报错,值不存在就不删除,相当于封装了一个迭代器检查if (pos!=s.end())
- s.erase(8);
-
- for (auto e : s)
- {
- cout << e << " ";
- }
- cout << endl;
- }
- int main()
- {
- test2();
- return 0;
- }

3.swap

这个函数交换的是两个对象的根节点指针。
仅排序(不去重)

1.插入函数
- #include
- #include
- using namespace std;
- void test3()
- {
- //排序
- multiset<int> s;
- s.insert(1);
- s.insert(3);
- s.insert(2);
- s.insert(4);
- s.insert(5);
- s.insert(6);
- s.insert(6);
- s.insert(6);
- s.insert(8);
-
- for (auto e : s)
- {
- cout << e << " ";
- }
- cout << endl;
-
- }
- int main()
- {
- test3();
- return 0;
- }

2.erase删除函数

当有多个相同的值时:find返回中序的第一个值。
- #include
- #include
- using namespace std;
- void test3()
- {
- //排序
- multiset<int> s;
- s.insert(1);
- s.insert(1);
- s.insert(1);
- s.insert(3);
- s.insert(2);
- s.insert(4);
- s.insert(5);
- s.insert(5);
-
- //find的val有多个值时,返回中序第一个val值所在节点的迭代器。
- multiset<int>::iterator pos = s.find(5);
- while (pos!=s.end())
- {
- cout << *pos << " ";
- ++pos;
- }
- cout << endl;
-
- for (auto e : s)
- {
- cout << e << " ";
- }
- cout << endl;
-
- }
- int main()
- {
- test3();
- return 0;
- }

如果我想删除所有的1这个值呢?
使用迭代器:
- #include
- #include
- using namespace std;
- void test3()
- {
- //排序
- multiset<int> s;
- s.insert(1);
- s.insert(1);
- s.insert(1);
- s.insert(3);
- s.insert(2);
- s.insert(4);
- s.insert(5);
- s.insert(5);
-
- //find的val有多个值时,返回中序第一个val值所在节点的迭代器。
- multiset<int>::iterator pos = s.find(1);
- while (pos!=s.end() && *pos==1)
- {
- auto next = pos;
- ++next;
- s.erase(pos); //删除所有的1
- pos = next;
- }
-
-
- for (auto e : s)
- {
- cout << e << " ";
- }
- cout << endl;
-
- }
- int main()
- {
- test3();
- return 0;
- }

使用值删除时最方便的,它会自己删除所有等于1的节点:会返回删除数据的个数。它其实是对上面迭代器的封装。
- #include
- #include
- using namespace std;
- void test3()
- {
- //排序
- multiset<int> s;
- s.insert(1);
- s.insert(1);
- s.insert(1);
- s.insert(3);
- s.insert(2);
- s.insert(4);
- s.insert(5);
- s.insert(5);
-
- cout << s.erase(1)<
//这里会返回删除1的个数 - for (auto e : s)
- {
- cout << e << " ";
- }
- cout << endl;
-
- }
- int main()
- {
- test3();
- return 0;
- }

set能否借助迭代器进行节点值的修改呢?
- #include
- #include
- using namespace std;
- void test3()
- {
- //排序
- multiset<int> s;
- s.insert(1);
- s.insert(1);
- s.insert(1);
- s.insert(3);
- s.insert(2);
- s.insert(4);
- s.insert(5);
- s.insert(5);
- auto pos = s.find(1);
- if (pos != s.end())
- {
- *pos += 10;//这里是不能进行修改的,如果修改了有可能破坏搜索二叉树,因此这里去调用operator*()的时候,返回的是const 的引用
- }
-
- for (auto e : s)
- {
- cout << e << " ";
- }
- cout << endl;
-
- }
- int main()
- {
- test3();
- return 0;
- }
直接标红报错:


在map里面不是存的两个值key和value,而是有进行了一层的封装,封装了一层类(结构体) ,map里面存的就是一个对象。封装的这个结构体就是pair,pair里面有2个值。


1.插入函数

pair的构造函数:

make_pair:对pair的封装,是函数模板


- #include
- #include
- #include
- using namespace std;
- void test1()
- {
- map
dict; - //直接创建pair对象,就行插入
- pair
kv1("sort","排序") ; - dict.insert(kv1);
- //创建匿名对象插入
- dict.insert(pair
("left","左")); - //使用make_pair,可以自动推导类型
- dict.insert(make_pair("string","字符串"));//常用
- //map
::iterator it = dict.begin(); - auto it = dict.begin();
- while (it!=dict.end())
- {
- //cout << *it << " "; //去调用*重载,_node->value_type 去调用pair结构体,结构体没有重载流提取,因此不行
- //cout << (*it).first << ':' << (*it).second <
- //最好使用-> 看起来就知道是个自定义类型的数据
- cout << it->first << ':' << it->second << endl;
- ++it;
- }
- cout << endl;
- //支持迭代器,就支持范围for
- for (auto & kv : dict) //最好使用引用,存在拷贝构造
- {
- //kv=*it
- cout << kv.first << ':' << kv.second << endl;
- }
- }
- int main()
- {
- test1();
- return 0;
- }
其他函数和set的基本差不多的。省略
2.水果统计次数
第一种代码:
- #include
- #include
- #include
- using namespace std;
- void test2()
- {
- string arr[] = { "苹果", "香蕉", "草莓", "樱桃", "香蕉", "苹果" };
- map
int> countMap; - //map的key不支持修改,但是value是支持修改的
- for (auto& str:arr)
- {
- auto ret = countMap.find(str);
- if (ret==countMap.end())
- {
- //没有对应的水果,就插入
- countMap.insert(make_pair(str,1));
- }
- else
- {
- //有对应的水果就次数+1
- ret->second++;
- }
- }
-
- for (auto & kv : countMap)
- {
- //kv=*it
- cout << kv.first << ':' << kv.second << endl;
- }
-
- }
- int main()
- {
- test2();
- return 0;
- }
上面的代码比较冗余:你find查找了依次,若是没有查找到,insert又去查找了依次,所以查找了2次。
对代码进行优化:

insert函数的返回值,是一个pair对象
insert函数的返回值,是一个pair对象,如果插入的元素或该元素与map中的key相等,那么就返回这个节点的迭代器,pair的first存储这个迭代器。pair的second 存储bool值,当插入一个新节点,则返回true,若是map中存在该key,那么就返回false。
代码改进:
- #include
- #include
- #include
- using namespace std;
- void test3()
- {
- string arr[] = { "苹果", "香蕉", "草莓", "樱桃", "香蕉", "苹果" };
- map
int> countMap; - for (auto &str : arr)
- {
- //pair
- auto ret = countMap.insert(make_pair(str, 1));
- if (ret.second==false)//说明这个节点key存在了
- {
- ret.first->second++; //次数+1,这里去调用迭代器中的operator->()重载
- }
- }
- for (auto & kv : countMap)
- {
- //kv=*it
- cout << kv.first << ':' << kv.second << endl;
- }
-
- }
- int main()
- {
- test3();
- return 0;
- }
方括号:

这里是去调用insert,返回一个pair的对象,pair对象中第一个first存的map是迭代器,*这个迭代器,会去调用迭代器的*重载,找到map节点的value_type(pair对象), .second 取第二个值。


返回值是mapped_type& ,这里是引用,对pair对象第二个变量(value)的引用。

对代码改进:
- #include
- #include
- #include
- using namespace std;
- void test3()
- {
- string arr[] = { "苹果", "香蕉", "草莓", "樱桃", "香蕉", "苹果" };
- map
int> countMap; - for (auto &str : arr)
- {
- countMap[str]++; //直接取调用operator[]
- }
- for (auto & kv : countMap)
- {
- //kv=*it
- cout << kv.first << ':' << kv.second << endl;
- }
-
- }
- int main()
- {
- test3();
- return 0;
- }
[ ]的功能:1.插入2.查找3.修改(value的值)
- void test4()
- {
- map
dict; - dict.insert(make_pair("sort","排序"));
- dict.insert(make_pair("left", "左边"));
- dict.insert(make_pair("left", "剩余"));//不会插入
-
- dict["left"] = "剩余"; //修改
- dict["test"]; //插入
- cout << dict["sort"] << endl;//查找
- dict["string"] = "字符串"; //插入+修改
- }
-
-
- int main()
- {
- test4();
- return 0;
- }
multimap 用法基本都是差不多的,key相同也会插入。
- #include
- #include
- #include
- using namespace std;
-
- void test5()
- {
- multimap
dict; - dict.insert(make_pair("sort", "排序"));
- dict.insert(make_pair("left", "左边"));
- dict.insert(make_pair("left", "剩余"));//会插入
- dict.insert(make_pair("left", "向左"));
-
- cout << dict.count("left") << endl;//查找次数
-
- auto it = dict.begin();
- while (it!=dict.end())
- {
- cout << it->first<<':'<
second << endl; - ++it;
- }
- cout << endl;
- }
-
- int main()
- {
- test5();
- return 0;
- }

3.排序
找出前K个最喜欢吃的水果
1.利用sort
第一种写法:
- #include
- #include
- #include
- #include
- #include
- using namespace std;
-
- //提供一个仿函数
- struct CountVal
- {
- bool operator()(const pair
int >& p1,const pairint >& p2) - {
- return p1.second > p2.second;
- }
- };
- void GetFavoriteFruit1(const vector
& fruits, size_t k) - {
- map
int> coutMap; - for (auto& str:fruits)
- {
- coutMap[str]++;//统计次数
- }
- //找前k个
- vector
int>> Vsort; - for (auto& e : coutMap)
- {
- Vsort.push_back(e);
- }
- //因为sort是随机迭代器,而map是双向迭代器,不行的,所以必须使用vector<>
- sort(Vsort.begin(), Vsort.end(), CountVal()); //pair提供了排序,但是不是我们想要的。需要自己写仿函数,排降序
-
- for (size_t i = 0; i < k;i++)
- {
- cout << Vsort[i].first << ':' << Vsort[i].second << endl;
- }
- cout << endl;
-
- }
- int main()
- {
- vector
v = { "苹果", "苹果", "香蕉", "草莓", "草莓", "苹果", "苹果", "樱桃", "香蕉","葡萄"}; - GetFavoriteFruit1(v,3);
- return 0;
- }

pair提供的排序,不适合我们这里


第一种写法存在大量的深拷贝,因此使用存迭代器,继续改进第二种:
- #include
- #include
- #include
- #include
- #include
- using namespace std;
- //提供一个仿函数
- struct CountVal
- {
- bool operator()(map
int >::iterator& it1, mapint >::iterator& it2) - {
- return it1->second > it2->second;
- }
- };
- void GetFavoriteFruit2(const vector
& fruits, size_t k) - {
- map
int> coutMap; - for (auto& str : fruits)
- {
- coutMap[str]++;//统计次数
- }
- vector
- auto it = coutMap.begin();
- while (it != coutMap.end())
- {
- Vsort.push_back(it);
- ++it;
- }
- sort(Vsort.begin(), Vsort.end(), CountVal());
- for (size_t i = 0; i < k;i++)
- {
- cout << Vsort[i]->first << ':' << Vsort[i]->second << endl;
- }
- cout << endl;
-
- }
-
- int main()
- {
- vector
v = { "苹果", "苹果", "香蕉", "草莓", "草莓", "苹果", "苹果", "樱桃", "香蕉","葡萄"}; - GetFavoriteFruit2(v,3);
- return 0;
- }

2.使用multimap
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- using namespace std;
- void GetFavoriteFruit3(const vector
& fruits, size_t k) - {
- map
int> coutMap; - for (auto& str : fruits)
- {
- coutMap[str]++;//统计次数
- }
-
- multimap<int, string, greater<int>> sortMap; //这里可以控制升降序,利用仿函数
- for (auto& kv : coutMap)
- {
- sortMap.insert(make_pair(kv.second,kv.first)); //这里使用second做为第一个值,first做第二个值
- }
-
- auto it = sortMap.begin();
- for (size_t i = 0; i < k; i++)
- {
- cout << it->second << ':' << it->first << endl;
- ++it;
- }
-
-
- }
-
- int main()
- {
- vector
v = { "苹果", "苹果", "香蕉", "草莓", "草莓", "苹果", "苹果", "樱桃", "香蕉","葡萄"}; - GetFavoriteFruit3(v,3);
- return 0;
- }
3.使用优先级队列
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- using namespace std;
- //提供一个仿函数
- struct CountVal
- {
- bool operator()(const pair
int >&it1, const pairint >& it2) - {
- return it1.second < it2.second; //给小于建大堆
- }
- };
- void GetFavoriteFruit4(const vector
& fruits, size_t k) - {
- map
int> coutMap; - for (auto& str : fruits)
- {
- coutMap[str]++;//统计次数
- }
-
- priority_queue
int>, vectorint>>, CountVal> pq; - for (auto&kv : coutMap)
- {
- pq.push(kv);
- }
-
- while (k--)
- {
- cout << pq.top().first << ':' << pq.top().second << endl;
- pq.pop();
- }
- cout << endl;
- }
- int main()
- {
- vector
v = { "苹果", "苹果", "香蕉", "草莓", "草莓", "苹果", "苹果", "樱桃", "香蕉","葡萄"}; - GetFavoriteFruit4(v,3);
- return 0;
- }
这里也存在多次深拷贝,因此继续改进:存迭代器
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- using namespace std;
- //提供一个仿函数
- struct CountVal
- {
- bool operator()(const map
int >::iterator&it1, const mapint >::iterator& it2) - {
- return it1->second < it2->second;
- }
- };
- void GetFavoriteFruit5(const vector
& fruits, size_t k) - {
- map
int> coutMap; - for (auto& str : fruits)
- {
- coutMap[str]++;//统计次数
- }
-
- priority_queue
- auto it = coutMap.begin();
- while (it!=coutMap.end())
- {
- pq.push(it);
- ++it;
- }
-
- while (k--)
- {
- cout << pq.top()->first << ':' << pq.top()->second << endl;
- pq.pop();
- }
- cout << endl;
- }
- int main()
- {
- vector
v = { "苹果", "苹果", "香蕉", "草莓", "草莓", "苹果", "苹果", "樱桃", "香蕉","葡萄"}; - GetFavoriteFruit5(v,3);
- return 0;
- }
4.OJ题
力扣
https://leetcode.cn/problems/top-k-frequent-words/description/
- class Solution {
- public:
- vector
topKFrequent(vector& words, int k) { - map
int> countMap; - for(auto& kv:words)
- {
- countMap[kv]++; //统计次数
- }
-
- multimap<int,string,greater<int>> sortmap;//利用mutilmap进行排序
- for(auto& e:countMap)
- {
- //e=*it
- sortmap.insert(make_pair(e.second,e.first));//这里反着使用,使用value进行排序
- }
- auto it=sortmap.begin();
- vector
v; - for(int i=0;i
- {
- v.push_back(it->second);
- ++it;
-
- }
- return v;
-
- }
- };
4.AVL树
4.1AVL树的概念
二叉搜索树随可以缩短查找效率,但是如果数据有序或接近有序二叉树搜索树将退化为单支树,查找元素相当于顺序表中搜索元素,效率低下。因此,两位俄罗斯的数学家G.M.Adelson-Velskii和E.Landis在1962年发明了一种解决上述问题的方法:当向二叉搜索树中插入新节点,如果能保证每个节点的左右子树高度之差绝对值不超过1(需要对树中的结点进行调整),即可降低树的高度,从而减少平均搜索长度。
一棵AVL树或者是空树,或者是具有以下性质的二叉搜索树:
1.它的左右子树都是AVL树
2.左右子树高度之差(简称平衡因子)的绝对值不超过1(-1/0/1)。平衡因子不一定需要,使用平衡因子是一种控制实现方式。平衡因子:右子树-左子树。这里规定一下,后面代码都是这样。也可以左减右。

如果一棵二叉搜索树是高度平衡的,它就是AVL树。如果它又n个结点,其高度保持在
,搜索时间复杂度O(
)。
4.2AVL树的实现
1.AVL树的框架
- template<class K,class V>
- struct AVLTreeNode
- {
- //3叉链
- AVLTreeNode
* _left; - AVLTreeNode
* _right; - AVLTreeNode
* _parent; - pair
_kv; - int _bf;//平衡因子
-
- AVLTreeNode(const pair
& kv) - :_left(nullptr)
- ,_right(nullptr)
- ,_parent(nullptr)
- ,_kv(kv)
- ,_bf(0)
- {}
-
- };
-
-
- template<class K,class V>
- class AVLTree
- {
- typedef struct AVLTreeNode
Node; - public:
- AVLTree()
- :_root(nullptr)
- {}
-
- ///在这里写成员函数
-
- private:
- Node* _root;
- };
2.插入函数
插入函数:
1.搜索二叉树一样的插入,但是需要注意3叉链,需要链上
2.对树进行平衡控制
2.1插入结点后先更新平衡因子
2.2根据平衡因子,是旋转还是不旋转
更新平衡因子的过程,根据平衡因子分析什么时候需要旋转:

更新平衡因子的结束条件:while(parent) ,插入节点有可能影响整条子树。

这段代码就是插入结点,并且更新平衡因子:
- bool insert(const pair
& kv) - {
- //1.插入数据,和搜索二叉树一样,先插入数据
-
- if (_root == nullptr)
- {
- _root = new Node(kv);
- return true;
- }
-
- Node*cur = _root;
- Node*parent = nullptr;
- while (cur)
- {
- if (cur->_kv.first > kv.first)
- {
- parent = cur;
- cur = cur->_left;
- }
- else if (cur->_kv.first < kv.first)
- {
- parent = cur;
- cur = cur->_right;
- }
- else
- {
- //有这个结点之后,就去重了
- return false;
- }
- }
- //没有这个结点
- cur = new Node(kv);
- if (parent->_kv.first > kv.first)
- {
- parent->_left = cur;
- cur->_parent = parent;
- }
- else
- {
- parent->_right = cur;
- cur->_parent = parent;
- }
-
- //2.控制平衡
- // 1.更新平衡因子
- // 2.出现异常平衡因子,那么需要旋转平衡处理
-
- while (parent) //判断条件是根结点的_parent 为nullptr
- {
- //更新平衡因子
- if (parent->_left == cur)
- {
- parent->_bf--;
- }
- else
- {
- parent->_bf++;
- }
-
- if (parent->_bf==0)
- {
- break;
- }
- //根据平衡因子判断是旋转还是继续更新平衡因子
- else if (parent->_bf==1 || parent->_bf==-1)
- {
- //子树变成了1或-1,需要继续向上更新
- cur = parent;
- parent = parent->_parent;
- }
- else if (parent->_bf == -2 || parent->_bf == 2)
- {
- //此时需要进行旋转了。
- if (parent->_bf == -2 && cur->_bf == -1)//向右单旋的条件
- {
- RoateR(parent);
- }
- else if (parent->_bf==2 && cur->_bf ==1)//向左单旋的条件
- {
- RoateL(parent);
- }
- else if (parent->_bf==-2 && cur->_bf==1)//左右旋的条件
- {
- RoateLR(parent);
- }
- else if (parent->_bf == 2 && cur->_bf == -1)//右左旋的条件
- {
- RoateRL(parent);
- }
-
- break;//旋转完,就结束了,树已经平衡了
- }
- else
- {
- //这里说明上一次平衡因子出现了问题,不出问题不会到这里
- assert(false); //出现问题了。
- }
-
- }
- return true;
-
- }
简单的插入过程:

3.AVL树的旋转
1.右单旋
由下图看出:parent->_bf==-2 && cur->_bf==-1 进行右单旋

右单旋代码:
- void RoateR(Node*parent)//右单旋
- {
- Node*subL = parent->_left;
- Node*subLR = subL->_right;
-
- parent->_left = subLR;
- if (subLR) //subL右边可能为空
- subLR->_parent = parent;
-
- subL->_right = parent;
- Node*parentPrev = parent->_parent; //记录parent的上一个结点
- parent->_parent = subL;
- if (parent == _root) //若parent是根节点
- {
- _root = subL;
- subL->_parent = nullptr;
- }
- else //若不是根,而是子树
- {
- if (parentPrev->_left == parent)//是左边子树
- {
- parentPrev->_left = subL;
- }//是右边子树
- else
- {
- parentPrev->_right = subL;
- }
- subL->_parent = parentPrev;
- }
- parent->_bf = subL->_bf = 0;//更新一下平衡因子
- }
2.左单旋
由下图看出:parent->_bf==2 && cur->_bf==1 进行左单旋

左单旋代码:
- void RoateL(Node*parent)//左单旋
- {
- Node* subR = parent->_right;
- Node*subRL = subR->_left;
-
- parent->_right = subRL;
- if (subRL) //subRL有可能为空
- subRL->_parent = parent;
- subR->_left = parent;
-
- Node*parentPrev = parent->_parent;
- parent->_parent = subR;
-
- if (_root==parent)//如果是根节点
- {
- _root = subR;
- subR->_parent = nullptr;
- }
- else
- {
- if (parentPrev->_left==parent)
- {
- parentPrev->_left = subR;
- }
- else
- {
- parentPrev->_right = subR;
- }
- subR->_parent = parentPrev;
- }
- subR->_bf = parent->_bf = 0;//更新平衡因子
- }
有了左单旋和右单旋的条件,以及函数的实现,因此insert继续完善左单旋和右单旋 :
- bool insert(const pair
& kv) - {
- //1.插入数据,和搜索二叉树一样,先插入数据
-
- if (_root == nullptr)
- {
- _root = new Node(kv);
- return true;
- }
-
- Node*cur = _root;
- Node*parent = nullptr;
- while (cur)
- {
- if (cur->_kv.first > kv.first)
- {
- parent = cur;
- cur = cur->_left;
- }
- else if (cur->_kv.first < kv.first)
- {
- parent = cur;
- cur = cur->_right;
- }
- else
- {
- //有这个结点之后,就去重了
- return false;
- }
- }
- //没有这个结点
- cur = new Node(kv);
- if (parent->_kv.first > kv.first)
- {
- parent->_left = cur;
- cur->_parent = parent;
- }
- else
- {
- parent->_right = cur;
- cur->_parent = parent;
- }
-
- //2.控制平衡
- // 1.更新平衡因子
- // 2.出现异常平衡因子,那么需要旋转平衡处理
-
- while (parent) //判断条件是根结点的_parent 为nullptr
- {
- //更新平衡因子
- if (parent->_left == cur)
- {
- parent->_bf--;
- }
- else
- {
- parent->_bf++;
- }
- //根据平衡因子判断是旋转还是继续更新平衡因子
- if (parent->_bf==1 || parent->_bf==-1)
- {
- //子树变成了1或-1,需要继续向上更新
- cur = parent;
- parent = parent->_parent;
- }
- else if (parent->_bf == -2 || parent->_bf == 2)
- {
- //此时需要进行旋转了。
- if (parent->_bf == -2 && cur->_bf == -1)//向右单旋的条件
- {
- RoateR(parent);
- }
- else if (parent->_bf==2 && cur->_bf ==1)//向左单旋的条件
- {
- RoateL(parent);
- }
-
- break;//旋转完,就结束了,树已经平衡了
- }
- else
- {
- //这里说明上一次平衡因子出现了问题,不出问题不会到这里
- assert(false); //出现问题了。
- }
-
- }
- return true;
-
- }
当插入结点后,发现并不是单独的一侧高,而是左高和右高,或右高和左高,因此需要双旋。双旋又分为左右旋和右左旋。
3.先进行左单旋再右旋(左右旋)
在b或c插入新的结点,都会发生双旋,左右双旋的条件parent->_bf==-2 && cur->_bf==1 进行左单旋再右单旋。

或者在b点插入新的结点,是一样的:

因此代码为:但是这里更新平衡因子的时候都是更新成0,也就是上面图,最后的结果是0,0。但是这样是不对的。后面会讲解如何更新平衡因子。
- void RoateLR(Node*parent)//左右旋
- {
- RoateL(parent->_left);//先进行左单旋
- RoateR(parent);//在进行右单旋
- }
4.先进行右单旋再左旋(右左旋)
在b或c插入新的结点,都会发生双旋,右左双旋的条件parent->_bf==2 && cur->_bf==-1 进行右单旋再左单旋。

或者在c插入结点:也是一样的

因此代码为:但是这里更新平衡因子的时候都是更新成0,也就是上面图,最后的结果是0,0。但是这样是不对的。后面会讲解如何更新平衡因子。
- void RoateRL(Node*parent)//右左旋
- {
- RoateR(parent->_right);//先进行右单旋
- RoateL(parent);//在进行左单旋
- }
所以有了上面的情况代码可以继续更新为:
- #include
- #include
- using namespace std;
- #include
- template<class K,class V>
- struct AVLTreeNode
- {
- //3叉链
- AVLTreeNode
* _left; - AVLTreeNode
* _right; - AVLTreeNode
* _parent; - pair
_kv; - int _bf;//平衡因子
-
- AVLTreeNode(const pair
& kv) - :_left(nullptr)
- ,_right(nullptr)
- ,_parent(nullptr)
- ,_kv(kv)
- , _bf(0)
- {}
-
- };
-
-
- template<class K,class V>
- class AVLTree
- {
- typedef struct AVLTreeNode
Node; - public:
- AVLTree()
- :_root(nullptr)
- {}
-
-
- bool insert(const pair
& kv) - {
- //1.插入数据,和搜索二叉树一样,先插入数据
-
- if (_root == nullptr)
- {
- _root = new Node(kv);
- return true;
- }
-
- Node*cur = _root;
- Node*parent = nullptr;
- while (cur)
- {
- if (cur->_kv.first > kv.first)
- {
- parent = cur;
- cur = cur->_left;
- }
- else if (cur->_kv.first < kv.first)
- {
- parent = cur;
- cur = cur->_right;
- }
- else
- {
- //有这个结点之后,就去重了
- return false;
- }
- }
- //没有这个结点
- cur = new Node(kv);
- if (parent->_kv.first > kv.first)
- {
- parent->_left = cur;
- cur->_parent = parent;
- }
- else
- {
- parent->_right = cur;
- cur->_parent = parent;
- }
-
- //2.控制平衡
- // 1.更新平衡因子
- // 2.出现异常平衡因子,那么需要旋转平衡处理
-
- while (parent) //判断条件是根结点的_parent 为nullptr
- {
- //更新平衡因子
- if (parent->_left == cur)
- {
- parent->_bf--;
- }
- else
- {
- parent->_bf++;
- }
- //根据平衡因子判断是旋转还是继续更新平衡因子
- if (parent->_bf==1 || parent->_bf==-1)
- {
- //子树变成了1或-1,需要继续向上更新
- cur = parent;
- parent = parent->_parent;
- }
- else if (parent->_bf == -2 || parent->_bf == 2)
- {
- //此时需要进行旋转了。
- if (parent->_bf == -2 && cur->_bf == -1)//向右单旋的条件
- {
- RoateR(parent);
- }
- else if (parent->_bf==2 && cur->_bf ==1)//向左单旋的条件
- {
- RoateL(parent);
- }
- else if (parent->_bf==-2 && cur->_bf==1)//左右旋的条件
- {
- RoateLR(parent);
- }
- else if (parent->_bf == 2 && cur->_bf == -1)//右左旋的条件
- {
- RoateRL(parent);
- }
-
- break;//旋转完,就结束了,树已经平衡了
- }
- else
- {
- //这里说明上一次平衡因子出现了问题,不出问题不会到这里
- assert(false); //出现问题了。
- }
-
- }
- return true;
-
- }
-
-
- void RoateR(Node*parent)//右单旋
- {
- Node*subL = parent->_left;
- Node*subLR = subL->_right;
-
- parent->_left = subLR;
- if (subLR) //subL右边可能为空
- subLR->_parent = parent;
-
- subL->_right = parent;
- Node*parentPrev = parent->_parent; //记录parent的上一个结点
- parent->_parent = subL;
- if (parent == _root) //若parent是根节点
- {
- _root = subL;
- subL->_parent = nullptr;
- }
- else //若不是根,而是子树
- {
- if (parentPrev->_left == parent)//是左边子树
- {
- parentPrev->_left = subL;
- }//是右边子树
- else
- {
- parentPrev->_right = subL;
- }
- subL->_parent = parentPrev;
- }
- parent->_bf = subL->_bf = 0;//更新一下平衡因子
- }
-
-
- void RoateL(Node*parent)//左单旋
- {
- Node* subR = parent->_right;
- Node*subRL = subR->_left;
-
- parent->_right = subRL;
- if (subRL) //subRL有可能为空
- subRL->_parent = parent;
- subR->_left = parent;
-
- Node*parentPrev = parent->_parent;
- parent->_parent = subR;
-
- if (_root==parent)//如果是根节点
- {
- _root = subR;
- subR->_parent = nullptr;
- }
- else
- {
- if (parentPrev->_left==parent)
- {
- parentPrev->_left = subR;
- }
- else
- {
- parentPrev->_right = subR;
- }
- subR->_parent = parentPrev;
- }
- subR->_bf = parent->_bf = 0;//更新平衡因子
- }
-
-
- void RoateLR(Node*parent)//左右旋
- {
- //保存好2个结点的指针
- Node* subL = parent->_left;
- Node*subLR = subL->_right;
- int bf = subL->_bf;
-
- RoateL(parent->_left);//先进行左单旋
- RoateR(parent);//在进行右单旋
- //if (bf==1)
- //{
- // subL->_bf = -1;
- // parent->_bf = 0;
- // subLR->_bf = 0;
- //}
- //else if (bf==-1)
- //{
- // subL->_bf = 0;
- // parent->_bf = 1;
- // subLR->_bf = 0;
- //}
- //else if (bf==0)
- //{
- // subL->_bf = 0;
- // parent->_bf =0;
- // subLR->_bf = 0;
- //}
- //else
- //{
- // //这里就是出错了
- // assert(false);
- //}
- }
-
- void RoateRL(Node*parent)//右左旋
- {
- Node* subR = parent->_right;
- Node* subRL = subR->_left;
- int bf = subRL->_bf;
-
- RoateR(parent->_right);//先进行右单旋
- RoateL(parent);//在进行左单旋
- //if (bf == 1)
- //{
- // subRL->_bf = subR->_bf = 0;
- // parent->_bf = -1;
- //}
- //else if (bf=-1)
- //{
- // subRL->_bf = parent->_bf = 0;
- // subR->_bf = 1;
- //}
- //else if (bf == 0)
- //{
- // subRL->_bf = subR->_bf = parent->_bf = 0;
- //}
- //else
- //{
- // //这里是不可能到达的。
- // assert(false);
- //}
- }
-
- void Inorder()
- {
- _Inorder(_root);
- }
-
- void _Inorder(Node* root)//中序遍历
- {
- if (root == nullptr)
- return;
- _Inorder(root->_left);
- cout << root->_kv.first << ":" << root->_kv.second << endl;
- _Inorder(root->_right);
- }
-
-
- private:
- Node* _root;
- };
5.AVL树调试
1.中序遍历只是检查是否为搜索二叉树,没办法检查是否为平衡二叉树。
- void Inorder()
- {
- _Inorder(_root);
- }
-
- void _Inorder(Node* root)//中序遍历
- {
- if (root == nullptr)
- return;
- _Inorder(root->_left);
- cout << root->_kv.first << ":" << root->_kv.second << endl;
- _Inorder(root->_right);
- }
2.检查高度,仅检查高度是不行的,有可能平衡因子有问题。
- int TreeHigh(Node*root)
- {
- if (root == nullptr)
- return 0;
- int left_high = TreeHigh(root->_left);
- int right_high = TreeHigh(root->_right);
-
- return left_high > right_high ? left_high + 1 : right_high + 1;
- }
-
- bool IsBanlance()
- {
- return _IsBanlance(_root);
- }
-
- bool _IsBanlance(Node*root)
- {
- if (root==nullptr)
- {
- return true;
- }
- int left_high = TreeHigh(root->_left);
- int right_high = TreeHigh(root->_right);
- //平衡因子有异常
- if (right_high - left_high != root->_bf)
- {
- cout << root->_kv.first << "现在是:" << root->_bf << endl;
- cout << root->_kv.first << "应该是:" << right_high - left_high << endl;
- return false;
- }
- return abs(left_high - right_high)< 2 && _IsBanlance(root->_left) && _IsBanlance(root->_right);
- }
测试代码:在插入的时候顺便检查一下平衡因子
- #include"AVLTreeNode.h"
- using namespace std;
-
- int main()
- {
-
- //int a[] = { 5, 4, 3, 2, 1 ,0};
- //int a[] = { 16, 3, 7, 11, 9, 26, 18, 14, 15 };
- int a[] = { 4, 2, 6, 1, 3, 5, 15, 7, 16, 14 };
- AVLTree<int, int> AVL;
- for (auto&e :a)
- {
- AVL.insert(make_pair(e,e));
- cout << "insert"<
":"<IsBanlance() << endl; - }
- AVL.Inorder();
-
-
- return 0;
- }
是在插入14的时候出现了问题:

因此对照插入数据,进行画图分析,怎么错了。就能找到问题所在。
6.分析平衡因子
1.左右旋的平衡因子分析
所以我们需要把平衡因子修改对,对于平衡因子的分析:
第一种情况:

第二种情况:

第三种情况:

因此代码左右旋的正确代码:
- void RoateLR(Node*parent)//左右旋
- {
- //保存好2个结点的指针
- Node* subL = parent->_left;
- Node*subLR = subL->_right;
- int bf = subL->_bf;
-
- RoateL(parent->_left);//先进行左单旋
- RoateR(parent);//在进行右单旋
- //从新更新一下平衡因子
- if (bf==1)
- {
- subL->_bf = -1;
- parent->_bf = 0;
- subLR->_bf = 0;
- }
- else if (bf==-1)
- {
- subL->_bf = 0;
- parent->_bf = 1;
- subLR->_bf = 0;
- }
- else if (bf==0)
- {
- subL->_bf = 0;
- parent->_bf =0;
- subLR->_bf = 0;
- }
- else
- {
- //这里就是出错了
- assert(false);
- }
- }
2.右左旋的平衡因子分析
第一种情况:

第二种情况:

第三种情况:60本身这个结点就是新增结点,也就是h==0

因此右左旋代码实现:
- void RoateRL(Node*parent)//右左旋
- {
- Node* subR = parent->_right;
- Node* sunRL = subR->_left;
- int bf = subRL->_bf; //提前保存好结点的平衡因子
-
- RoateR(parent->_right);//先进行右单旋
- RoateL(parent);//在进行左单旋
- //更新平衡因子
- if (bf == 1)
- {
- subRL->_bf = subR->_bf = 0;
- parent->_bf = -1;
- }
- else if (bf==-1)
- {
- subRL->_bf = parent->_bf = 0;
- subR->_bf = 1;
- }
- else if (bf == 0)//别忘了这种情况
- {
- subRL->_bf = subR->_bf = parent->_bf = 0;
- }
- else //平衡因子出现问题
- {
- //这里是不可能到达的。
- //说明上一次插入已经出现错误了
- assert(false);
- }
- }
都分析完成后,所有的代码:
AVLTreeNode.h
- #include
- #include
- using namespace std;
- #include
- template<class K,class V>
- struct AVLTreeNode
- {
- //3叉链
- AVLTreeNode
* _left; - AVLTreeNode
* _right; - AVLTreeNode
* _parent; - pair
_kv; - int _bf;//平衡因子
-
- AVLTreeNode(const pair
& kv) - :_left(nullptr)
- ,_right(nullptr)
- ,_parent(nullptr)
- ,_kv(kv)
- , _bf(0)
- {}
-
- };
-
-
- template<class K,class V>
- class AVLTree
- {
- typedef struct AVLTreeNode
Node; - public:
- AVLTree()
- :_root(nullptr)
- {}
-
-
- bool insert(const pair
& kv) - {
- //1.插入数据,和搜索二叉树一样,先插入数据
-
- if (_root == nullptr)
- {
- _root = new Node(kv);
- return true;
- }
-
- Node*cur = _root;
- Node*parent = nullptr;
- while (cur)
- {
- if (cur->_kv.first > kv.first)
- {
- parent = cur;
- cur = cur->_left;
- }
- else if (cur->_kv.first < kv.first)
- {
- parent = cur;
- cur = cur->_right;
- }
- else
- {
- //有这个结点之后,就去重了
- return false;
- }
- }
- //没有这个结点
- cur = new Node(kv);
- if (parent->_kv.first > kv.first)
- {
- parent->_left = cur;
- cur->_parent = parent;
- }
- else
- {
- parent->_right = cur;
- cur->_parent = parent;
- }
-
- //2.控制平衡
- // 1.更新平衡因子
- // 2.出现异常平衡因子,那么需要旋转平衡处理
-
- while (parent) //判断条件是根结点的_parent 为nullptr
- {
- //更新平衡因子
- if (parent->_left == cur)
- {
- parent->_bf--;
- }
- else
- {
- parent->_bf++;
- }
-
- if (parent->_bf==0)
- {
- break;
- }
- //根据平衡因子判断是旋转还是继续更新平衡因子
- else if (parent->_bf==1 || parent->_bf==-1)
- {
- //子树变成了1或-1,需要继续向上更新
- cur = parent;
- parent = parent->_parent;
- }
- else if (parent->_bf == -2 || parent->_bf == 2)
- {
- //此时需要进行旋转了。
- if (parent->_bf == -2 && cur->_bf == -1)//向右单旋的条件
- {
- RoateR(parent);
- }
- else if (parent->_bf==2 && cur->_bf ==1)//向左单旋的条件
- {
- RoateL(parent);
- }
- else if (parent->_bf==-2 && cur->_bf==1)//左右旋的条件
- {
- RoateLR(parent);
- }
- else if (parent->_bf == 2 && cur->_bf == -1)//右左旋的条件
- {
- RoateRL(parent);
- }
-
- break;//旋转完,就结束了,树已经平衡了
- }
- else
- {
- //这里说明上一次平衡因子出现了问题,不出问题不会到这里
- assert(false); //出现问题了。
- }
-
- }
- return true;
-
- }
-
-
- void RoateR(Node*parent)//右单旋
- {
- Node*subL = parent->_left;
- Node*subLR = subL->_right;
-
- parent->_left = subLR;
- if (subLR) //subL右边可能为空
- subLR->_parent = parent;
-
- subL->_right = parent;
- Node*parentPrev = parent->_parent; //记录parent的上一个结点
- parent->_parent = subL;
- if (parent == _root) //若parent是根节点
- {
- _root = subL;
- subL->_parent = nullptr;
- }
- else //若不是根,而是子树
- {
- if (parentPrev->_left == parent)//是左边子树
- {
- parentPrev->_left = subL;
- }//是右边子树
- else
- {
- parentPrev->_right = subL;
- }
- subL->_parent = parentPrev;
- }
- parent->_bf = subL->_bf = 0;//更新一下平衡因子
- }
-
-
- void RoateL(Node*parent)//左单旋
- {
- Node* subR = parent->_right;
- Node*subRL = subR->_left;
-
- parent->_right = subRL;
- if (subRL) //subRL有可能为空
- subRL->_parent = parent;
- subR->_left = parent;
-
- Node*parentPrev = parent->_parent;
- parent->_parent = subR;
-
- if (_root==parent)//如果是根节点
- {
- _root = subR;
- subR->_parent = nullptr;
- }
- else
- {
- if (parentPrev->_left==parent)
- {
- parentPrev->_left = subR;
- }
- else
- {
- parentPrev->_right = subR;
- }
- subR->_parent = parentPrev;
- }
- subR->_bf = parent->_bf = 0;//更新平衡因子
- }
-
-
- void RoateLR(Node*parent)//左右旋
- {
- //保存好2个结点的指针
- Node* subL = parent->_left;
- Node*subLR = subL->_right;
- int bf = subLR->_bf;
-
- RoateL(parent->_left);//先进行左单旋
- RoateR(parent);//在进行右单旋
- if (bf==1)
- {
- subL->_bf = -1;
- parent->_bf = 0;
- subLR->_bf = 0;
- }
- else if (bf==-1)
- {
- subL->_bf = 0;
- parent->_bf = 1;
- subLR->_bf = 0;
- }
- else if (bf==0)
- {
- subL->_bf = 0;
- parent->_bf =0;
- subLR->_bf = 0;
- }
- else
- {
- //这里就是出错了
- assert(false);
- }
- }
-
- void RoateRL(Node*parent)//右左旋
- {
- Node* subR = parent->_right;
- Node* subRL = subR->_left;
- int bf = subRL->_bf;
-
- RoateR(parent->_right);//先进行右单旋
- RoateL(parent);//在进行左单旋
- if (bf == 1)
- {
- subRL->_bf = subR->_bf = 0;
- parent->_bf = -1;
- }
- else if (bf==-1)
- {
- subRL->_bf = parent->_bf = 0;
- subR->_bf = 1;
- }
- else if (bf == 0)
- {
- subRL->_bf = subR->_bf = parent->_bf = 0;
- }
- else
- {
- //这里是不可能到达的。
- assert(false);
- }
- }
-
- void Inorder()
- {
- _Inorder(_root);
- }
-
- void _Inorder(Node* root)//中序遍历
- {
- if (root == nullptr)
- return;
- _Inorder(root->_left);
- cout << root->_kv.first << ":" << root->_kv.second << endl;
- _Inorder(root->_right);
- }
-
- int TreeHigh(Node*root)
- {
- if (root == nullptr)
- return 0;
- int left_high = TreeHigh(root->_left);
- int right_high = TreeHigh(root->_right);
-
- return left_high > right_high ? left_high + 1 : right_high + 1;
- }
-
- bool IsBanlance()
- {
- return _IsBanlance(_root);
- }
-
- bool _IsBanlance(Node*root)
- {
- if (root==nullptr)
- {
- return true;
- }
- int left_high = TreeHigh(root->_left);
- int right_high = TreeHigh(root->_right);
- //平衡因子有异常
- if (right_high - left_high != root->_bf)
- {
- cout << root->_kv.first << "现在是:" << root->_bf << endl;
- cout << root->_kv.first << "应该是:" << right_high - left_high << endl;
- return false;
- }
- return abs(left_high - right_high)< 2 && _IsBanlance(root->_left) && _IsBanlance(root->_right);
- }
-
- private:
- Node* _root;
- };
main.cpp
- #include"AVLTreeNode.h"
- using namespace std;
-
- int main()
- {
-
- //int a[] = { 5, 4, 3, 2, 1 ,0};
- int a[] = { 16, 3, 7, 11, 9, 26, 18, 14, 15 };
- //int a[] = { 4, 2, 6, 1, 3, 5, 15, 7, 16, 14 };
- AVLTree<int, int> AVL;
- for (auto&e :a)
- {
- AVL.insert(make_pair(e,e));
- cout << "insert"<
":"<IsBanlance() << endl; - }
- AVL.Inorder();
-
-
- return 0;
- }
测试结果:3个用例,没问题。


AVL树的性能:AVL树是一颗绝对平衡的二叉树,其要求每个节点的左右子树高度差的绝对值不超过1,这样可以保证查询时高效的时间复杂度,即
5.红黑树
5.1红黑树的概念
红黑树,是一种二叉树搜索树,但在每个节点上增加一个存储位表示结点的颜色,可以是Red或BLACK。通过对任何一条从根到叶子的路径上各个结点着色方式的限制,红黑树确保没有一条路径会比其他路径长出两倍,因而是接近平衡。

5.2红黑树的性质
1.每个结点不是红色就是黑色
2.根结点是黑色
3.如果一个结点是红色,则它的两个孩子结点是黑色的。(没有连续的红色结点)
4.每条路径上所包含的黑色结点数目是一样多的。
5.每个叶子结点都是黑色的(此处的叶子结点指的是空节点,即NIL 都是黑色的) 。但是这个黑色结点可以不算,不用管就是了。
以上的前4个红黑树性质,确定了最长路径不超过最短路径的2倍,从NIL开始到根节点代表一条路径。下图一共10条路径。
通过下图理解为什么满足上面的性质,红黑树就能保证:其最长路径中结点个数不会超过最短路径结点个数的两倍。

最短路径:全黑
最长路径:一黑一红
假设每条路径黑节点的数量是N, N<=任意路径长度<=2N
红黑树复杂度分析:

红黑树是近似平衡与AVL树完全平衡相比,AVL树的复杂度是
,比红黑树效率高,但是它需要大量的旋转才能达到平衡付出代价很大,所以红黑树更吃香。
5.3红黑树的实现
1.红黑树的基本框架
- #include
- #include
- using namespace std;
-
- enum color
- {
- RED,
- BLACK
- };
-
-
- template<class K,class V>
- struct RBtreeNode
- {
- RBtreeNode
* _left; - RBtreeNode
* _right; - RBtreeNode
* _parent; - pair
_kv; - color _col;
-
- RBtreeNode(const pair
& kv) - :_left(nullptr)
- , _right(nullptr)
- , _parent(nullptr)
- , _kv(kv)
- , _col(RED)
- {}
- };
-
- template<class K,class V>
- class RBTtree
- {
- typedef RBtreeNode
Node; - public:
- RBTtree()
- :_root(nullptr)
- {}
- //这里增加成员函数
-
- private:
- Node* _root;
- };
2.insert函数
思考一个问题:在插入节点时,是插入红色的节点还是黑色的。答案:红色的,若是插入黑色的,根据红黑树的定义,每条路径上所包含相同数目的黑色节点,若是插入黑色节点,那么将会对整棵树产生影响,反而插入红色节点,最差只会影响这一条路径上的,最好不影响。
这里的插入节点和前面的平衡二叉树是一样的,旋转也一样,只是不更新平衡因子。不同的是控制上的不同,所以只介绍控制。
以下分析都是分析的左边插入节点,右边插入就是相反了。
情况1:只是变色

情况2:单旋



情况3:

红黑树insert代码:
- bool insert(const pair
& kv) - {
- //
- if (_root == nullptr)
- {
- _root = new Node(kv);
- _root->_col = BLACK;
- return true;
- }
- Node* cur = _root;
- Node* parent = nullptr;
-
- while (cur)
- {
- if (cur->_kv.first > kv.first)
- {
- parent = cur;
- cur = cur->_left;
- }
- else if (cur->_kv.first < kv.first)
- {
- parent = cur;
- cur = cur->_right;
- }
- else
- {
- return false;
- }
- }
- cur = new Node(kv);
- cur->_col = RED;//把结点以红色插入
- if (parent->_kv.first < kv.first)
- {
- parent->_right = cur;
- cur->_parent = parent;
- }
- else
- {
- parent->_left = cur;
- cur->_parent = parent;
- }
-
- //进行红黑树的控制
- while (parent && parent->_col==RED)//父节点位黑或者不存在就结束了
- {
- Node*gradparent = parent->_parent; //这里的祖父一定是存在的,因为parent->_col==RED,所以parent不可能是根节点
- if (gradparent->_left==parent)//在左边进行插入的
- {
- Node* uncle = gradparent->_right;
- if (uncle && uncle->_col == RED)//u存在且为红色,情况1
- {
- uncle->_col = BLACK;
- parent->_col = BLACK;
- gradparent->_col = RED;
- cur = gradparent;//把祖父当成新增结点
- parent = cur->_parent;
- }
- else //u不存在或为黑
- {
- if (parent->_left==cur) //在parent插入左边插入节点,情况2
- {
- //单旋
- // g
- // p u(存在为黑,或不存在)
- // c
- RoateR(gradparent);
- gradparent->_col = RED;
- parent->_col = BLACK;
- }
- else //在parent的右边插入节点 ,情况3
- {
- //双旋
- // g
- // p u
- // c
- RoateL(parent);
- RoateR(gradparent);
- cur->_col = BLACK;
- gradparent->_col = RED;
- }
- break;
-
- }
- }
- else //gradparent->_left==parent,在树的右边插入了节点
- {
-
- Node*uncle = gradparent->_left;
- if (uncle && uncle->_col==RED)//情况1 u存在且为红
- {
- parent->_col = BLACK;
- uncle->_col = BLACK;
- gradparent->_col = RED;
- cur = gradparent;
- parent = cur->_parent;
- }
- else //u不存在或着u为黑
- {
- // g
- //u p
- // cur
- if (parent->_right == cur)//情况2
- {
- //进行左单旋
- RoateL(gradparent);
- parent->_col = BLACK;
- gradparent->_col = RED;
- }
- // g
- //u p
- // c
- else //情况3
- {
- //双旋
- RoateR(parent);
- RoateL(gradparent);
- cur->_col = BLACK;
- gradparent->_col = RED;
- }
- break;
- }
- }
- }
- _root->_col = BLACK;//不管是子树还是根节点都需要是黑色
- return true;
- }
测试代码:使用中序只能证明是搜索二叉树。
- #include"RBTree.h"
- int main()
- {
- //int a[] = { 5, 4, 3, 2, 1 ,0};
- //int a[] = { 16, 3, 7, 11, 9, 26, 18, 14, 15 };
- int a[] = { 4, 2, 6, 1, 3, 5, 15, 7, 16, 14 };
-
- RBTtree<int, int> t;
- for (auto e : a)
- {
- t.insert(make_pair(e,e));
- }
- t.Inoder();
-
- return 0;
- }
那如何证明是红黑树呢?
主要是检验性质3和4。
注意这里检查是否存在连续红色节点的时候,去检查红色节点的父亲节点。
这里检查每条路径上的黑色节点是否一样,需要选取一个基准值,这里选的树的最左边那条路径。
- bool IsBanlance()
- {
- if (_root && _root->_col == RED) //检查根节点是不是黑色的
- {
- cout << "根节点不是黑色" << endl;
- return false;
- }
-
- int benchmark = 0; //取一个路径上黑色节点的数量作为基准值
- Node* cur = _root;
- while (cur)//这里取的最左路径为基准,以方便检验黑色节点的数量
- {
- if (cur->_col == BLACK)
- {
- benchmark++;
- }
- cur = cur->_left;
- }
- int BlackCount = 0;
-
- return _IsBanlance(_root, benchmark, BlackCount);
- }
- bool _IsBanlance(Node* root, int benchmark, int BlackCount)
- {
- if (root == nullptr)
- {
- //检查每一条黑色节点数量
- if (BlackCount != benchmark)
- {
- cout << "路径黑色节点不一样" << endl;
- return false;
- }
- return true;
- }
- //这里不去检查孩子节点,而是检查父亲节点,检查孩子太麻烦了,情况比较多
- if (root->_col==RED && root->_parent->_col==RED) //root->_col==RED 一定存在父节点,因为根黑色的
- {
- cout << "连续的红节点" << endl;
- return false;
- }
- if (root->_col == BLACK)
- {
- BlackCount++;
- }
-
-
- return _IsBanlance(root->_left, benchmark, BlackCount) && _IsBanlance(root->_right, benchmark, BlackCount);
- }
测试代码:
RBTree.h
- #include
- #include
- using namespace std;
-
-
- enum color
- {
- RED,
- BLACK
- };
-
-
- template<class K,class V>
- struct RBtreeNode
- {
- RBtreeNode
* _left; - RBtreeNode
* _right; - RBtreeNode
* _parent; - pair
_kv; - color _col;
-
- RBtreeNode(const pair
& kv) - :_left(nullptr)
- , _right(nullptr)
- , _parent(nullptr)
- , _kv(kv)
- , _col(RED)
- {}
- };
-
- template<class K,class V>
- class RBTtree
- {
- typedef RBtreeNode
Node; - public:
- RBTtree()
- :_root(nullptr)
- {}
-
- void RoateR(Node*parent)
- {
- Node*subL = parent->_left;
- Node* subLR = subL->_right;
-
- parent->_left = subL->_right;
- if (subLR)
- subLR->_parent = parent;
- subL->_right = parent;
- Node*prevparent = parent->_parent;
- parent->_parent = subL;
- if (parent == _root)
- {
- _root = subL;
- subL->_parent = nullptr;
- }
- else if (prevparent->_left==parent)
- {
- prevparent->_left = subL;
- subL->_parent = prevparent;
- }
- else if (prevparent->_right==parent)
- {
- prevparent->_right = subL;
- subL->_parent = prevparent;
- }
- else
- {
- assert(false);
- }
- }
-
-
- void RoateL(Node* parent)
- {
- Node*subR = parent->_right;
- Node*subRL = subR->_left;
- parent->_right = subRL;
- if (subRL)
- {
- subRL->_parent = parent;
- }
- subR->_left = parent;
- Node*prevparent = parent->_parent;
- parent->_parent = subR;
- if (parent==_root)
- {
- _root = subR;
- subR->_parent = nullptr;
- }
- else if (prevparent->_left == parent)
- {
- prevparent->_left = subR;
- subR->_parent = prevparent;
- }
- else if (prevparent->_right == parent)
- {
- prevparent->_right = subR;
- subR->_parent = prevparent;
- }
- else
- {
- assert(false);
- }
- }
-
-
- bool insert(const pair
& kv) - {
- //
- if (_root == nullptr)
- {
- _root = new Node(kv);
- _root->_col = BLACK;
- return true;
- }
- Node* cur = _root;
- Node* parent = nullptr;
-
- while (cur)
- {
- if (cur->_kv.first > kv.first)
- {
- parent = cur;
- cur = cur->_left;
- }
- else if (cur->_kv.first < kv.first)
- {
- parent = cur;
- cur = cur->_right;
- }
- else
- {
- return false;
- }
- }
- cur = new Node(kv);
- cur->_col = RED;//把结点以红色插入
- if (parent->_kv.first < kv.first)
- {
- parent->_right = cur;
- cur->_parent = parent;
- }
- else
- {
- parent->_left = cur;
- cur->_parent = parent;
- }
-
- //进行红黑树的控制
- while (parent && parent->_col==RED)//父节点位黑或者不存在就结束了
- {
- Node*gradparent = parent->_parent; //这里的祖父一定是存在的,因为parent->_col==RED,所以parent不可能是根节点
- if (gradparent->_left==parent)//在左边进行插入的
- {
- Node* uncle = gradparent->_right;
- if (uncle && uncle->_col == RED)//u存在且为红色,情况1
- {
- uncle->_col = BLACK;
- parent->_col = BLACK;
- gradparent->_col = RED;
- cur = gradparent;//把祖父当成新增结点
- parent = cur->_parent;
- }
- else //u不存在或为黑
- {
- if (parent->_left==cur) //在parent插入左边插入节点,情况2
- {
- //单旋
- // g
- // p u(存在为黑,或不存在)
- // c
- RoateR(gradparent);
- gradparent->_col = RED;
- parent->_col = BLACK;
- }
- else //在parent的右边插入节点 ,情况3
- {
- //双旋
- // g
- // p u
- // c
- RoateL(parent);
- RoateR(gradparent);
- cur->_col = BLACK;
- gradparent->_col = RED;
- }
- break;
-
- }
- }
- else //gradparent->_left==parent,在树的右边插入了节点
- {
-
- Node*uncle = gradparent->_left;
- if (uncle && uncle->_col==RED)//情况1 u存在且为红
- {
- parent->_col = BLACK;
- uncle->_col = BLACK;
- gradparent->_col = RED;
- cur = gradparent;
- parent = cur->_parent;
- }
- else //u不存在或着u为黑
- {
- // g
- //u p
- // cur
- if (parent->_right == cur)//情况2
- {
- //进行左单旋
- RoateL(gradparent);
- parent->_col = BLACK;
- gradparent->_col = RED;
- }
- // g
- //u p
- // c
- else //情况3
- {
- //双旋
- RoateR(parent);
- RoateL(gradparent);
- cur->_col = BLACK;
- gradparent->_col = RED;
- }
- break;
- }
- }
- }
- _root->_col = BLACK;//不管是子树还是根节点都需要是黑色
- return true;
- }
-
- //中序遍历
- void Inoder()
- {
- _Inoder(_root);
- }
- void _Inoder(Node* root)
- {
- if (root == nullptr)
- return;
- _Inoder(root->_left);
- cout << root->_kv.first << ":" << root->_kv.second << endl;
- _Inoder(root->_right);
- }
-
- bool IsBanlance()
- {
- if (_root && _root->_col == RED) //检查根节点是不是黑色的
- {
- cout << "根节点不是黑色" << endl;
- return false;
- }
-
- int benchmark = 0; //取一个路径上黑色节点的数量作为基准值
- Node* cur = _root;
- while (cur)//这里取的最左路径为基准
- {
- if (cur->_col == BLACK)
- {
- benchmark++;
- }
- cur = cur->_left;
- }
- int BlackCount = 0;
-
- return _IsBanlance(_root, benchmark, BlackCount);
- }
- bool _IsBanlance(Node* root, int benchmark, int BlackCount)
- {
- if (root == nullptr)
- {
- //检查每一条黑色节点数量
- if (BlackCount != benchmark)
- {
- cout << "路径黑色节点不一样" << endl;
- return false;
- }
- return true;
- }
- //这里不去检查孩子节点,而是检查父亲节点,检查孩子太麻烦了,情况比较多
- if (root->_col==RED && root->_parent->_col==RED) //root->_col==RED 一定存在父节点,因为根黑色的
- {
- cout << "连续的红节点" << endl;
- return false;
- }
- if (root->_col == BLACK)
- {
- BlackCount++;
- }
-
-
- return _IsBanlance(root->_left, benchmark, BlackCount) && _IsBanlance(root->_right, benchmark, BlackCount);
- }
-
- private:
- Node* _root;
- };
main.cpp
- #include"RBTree.h"
- int main()
- {
- //int a[] = { 5, 4, 3, 2, 1 ,0};
- //int a[] = { 16, 3, 7, 11, 9, 26, 18, 14, 15 };
- int a[] = { 4, 2, 6, 1, 3, 5, 15, 7, 16, 14 };
-
- RBTtree<int, int> t;
- for (auto e : a)
- {
- t.insert(make_pair(e,e));
- cout << "insert" << e << ":"<
IsBanlance() << endl; - }
- t.Inoder();
-
- return 0;
- }
也可以给随机值:
- #include
- #include
- #include"RBTree.h"
- int main()
- {
- //int a[] = { 5, 4, 3, 2, 1 ,0};
- //int a[] = { 16, 3, 7, 11, 9, 26, 18, 14, 15 };
- //int a[] = { 4, 2, 6, 1, 3, 5, 15, 7, 16, 14 };
- vector<int> v;
- srand(time(0));
- int N = 100000;
- for (int i = 0; i < N; i++)
- {
- v.push_back(rand()); //给随机值
- }
- RBTtree<int, int> t;
- for (auto e : v)
- {
- t.insert(make_pair(e,e));
- cout << "insert" << e << ":"<
IsBanlance() << endl; - }
- // cout <
- t.Inoder();
-
- return 0;
- }
6.set和map的模拟实现
1.set和map的模拟实现
观察STL源码:stl_map.h , stl_set.h , stl_tree.h。
set是key ,map是 pair

为了实现红黑树既可以是pair,也可以是key,那就需要改进红黑树。
这里最主要的是实现set和map共用一个红黑树:
RBTree.h
- #pragma once
- #include
- namespace zcf{
-
-
- enum color
- {
- RED,
- BLACK
- };
-
-
- template<class T>//这里只需要一个模板参数,用于接受类型
- struct RBtreeNode
- {
- RBtreeNode
* _left; - RBtreeNode
* _right; - RBtreeNode
* _parent; - T _data; //根据传入的模板参数来确定这里是pair还是key
- color _col;
-
- RBtreeNode(const T& data)
- :_left(nullptr)
- , _right(nullptr)
- , _parent(nullptr)
- , _data(data)
- , _col(RED)
- {}
- };
-
-
- //迭代器
- template<class T, class Ref, class Ptr>
- struct RBTreeIterator
- {
- typedef RBTreeIterator
Self; - typedef RBtreeNode
Node; - Node* _node;
-
- RBTreeIterator(Node* node)
- :_node(node)
- {}
-
- Ref operator*()
- {
- return _node->_data;
- }
-
- Ptr operator->()
- {
- return &(_node->_data);
- }
-
- //前置++
- //当前结点的右子树不为空,那么就去找右子树的最左结点
- //当前结点的右子树为空:因为是中序遍历,++也要遵循中序
- // 1.如果当前结点是parent的左边,++就是parent
- // 2.如果当前结点是parent的右边,++就是当前结点的_parent,直到parent为空,或parent->_left==cur
- Self& operator++()
- {
-
- if(_node->_right) // 右子树不为空,那就是右子树的最左节点
- {
- Node* cur = _node->_right;
- while (cur->_left)
- {
- cur = cur->_left;
- }
- _node = cur;
- }
- else//右子树为空
- {
- Node*cur = _node;
- Node*parent = cur->_parent;
- while (parent && parent->_right==cur)
- {
- cur = cur->_parent;
- parent = parent->_parent;
- }
- //父亲的左节点为cur。
- //parent->_left==cur或parent==nullptr
- _node = parent;
- }
- return *this;
- }
-
- //前置-- ,与++是相反的思想
- Self& operator--()
- {
- if (_node->_left)//左子树不为空,找最右节点
- {
- Node*cur = _node->_left;
- while (cur->_right)
- {
- cur = cur->_right;
- }
- _node = cur;
- }
- else//左子树为空
- {
- Node*cur = _node;
- Node*parent = cur->_parent;
- while (parent && parent->_left==cur)
- {
- cur = cur->_parent;
- parent = parent->_parent;
- }
- _node = parent;
-
-
- }
- return *this;
- }
- bool operator!=(const Self& s)
- {
- return _node != s._node;
- }
-
- bool operator==(const Self& s)
- {
- return _node == s._node;
- }
-
- };
-
-
-
- template<class K,class T,class KeyOfT>//使用T来解决红黑树是key还是pair,
- //KeyOfT 来解决比较的问题,因为红黑树在比较的时候不知道是pair还是key,而map和set类中知道
- class RBTtree
- {
-
- public:
- typedef RBTreeIterator< T, T&, T*> iterator;
- typedef RBTreeIterator< T, const T&,const T*> const_iterator;
- typedef RBtreeNode
Node; //typedef一下RBtreeNode,根据传入的T进行节点的实例化,确定是pair还是key -
- RBTtree()
- :_root(nullptr)
- {}
-
- Node* copy(Node* root)
- {
- if (root == nullptr)
- return nullptr;
- //拷贝结点
- Node* newnode = new Node(root->_data);
- newnode->_col = root->_col;//更新颜色
- //Node*newnode=new Node(*root);//这里会去调用拷贝构造,但是拷贝构造是系统默认的,只更新了颜色和data
-
- //更新左和右
- newnode->_left = copy(root->_left);
- newnode->_right = copy(root->_right);
- //更新parent
- if (newnode->_left)//不为空
- {
- newnode->_left->_parent = newnode;
- }
- if (newnode->_right)
- {
- newnode->_right->_parent = newnode;
- }
- return newnode;
- }
- //拷贝构造,需要深拷贝
- RBTtree(const RBTtree
& rb) - {
- _root=copy(rb._root);
- }
-
- //赋值
- RBTtree
& operator=(RBTtree tmp) - {
- swap(_root, tmp._root);
- return *this;
- }
-
- //迭代器
- iterator begin() //找最小的节点
- {
- Node* min = _root;
- while (min && min->_left)
- {
- min = min->_left;
- }
- return iterator(min);
- }
-
- iterator end()
- {
- return iterator(nullptr);
- }
-
- //查找函数,查找的是key
- iterator find(const K& key)
- {
- KeyOfT kot;
- Node* cur = _root;
- while (cur)
- {
- if (kot(cur->_data) > kot(key))
- {
- cur = cur->_left;
- }
- else if (kot(cur->_data) < kot(key))
- {
- cur = cur->_right;
- }
- else //找到了
- {
- return iterator(cur);
- }
- }
- return end(); //没有找到,返回nullptr;
- }
-
-
- void RoateR(Node*parent)
- {
- Node*subL = parent->_left;
- Node* subLR = subL->_right;
-
- parent->_left = subL->_right;
- if (subLR)
- subLR->_parent = parent;
- subL->_right = parent;
- Node*prevparent = parent->_parent;
- parent->_parent = subL;
- if (parent == _root)
- {
- _root = subL;
- subL->_parent = nullptr;
- }
- else if (prevparent->_left==parent)
- {
- prevparent->_left = subL;
- subL->_parent = prevparent;
- }
- else if (prevparent->_right==parent)
- {
- prevparent->_right = subL;
- subL->_parent = prevparent;
- }
- else
- {
- assert(false);
- }
- }
-
-
- void RoateL(Node* parent)
- {
- Node*subR = parent->_right;
- Node*subRL = subR->_left;
- parent->_right = subRL;
- if (subRL)
- {
- subRL->_parent = parent;
- }
- subR->_left = parent;
- Node*prevparent = parent->_parent;
- parent->_parent = subR;
- if (parent==_root)
- {
- _root = subR;
- subR->_parent = nullptr;
- }
- else if (prevparent->_left == parent)
- {
- prevparent->_left = subR;
- subR->_parent = prevparent;
- }
- else if (prevparent->_right == parent)
- {
- prevparent->_right = subR;
- subR->_parent = prevparent;
- }
- else
- {
- assert(false);
- }
- }
-
- //insert返回pair对象,对象里面一个是迭代器,一个是bool
- pair
bool > insert(const T& data) - {
- if (_root == nullptr)
- {
- _root = new Node(data);
- _root->_col = BLACK;
- return make_pair(iterator(_root),true);
- }
- Node* cur = _root;
- Node* parent = nullptr;
- KeyOfT kot; //创建KeyOfT的对象,相当于仿函数
- while (cur)
- {
- if (kot(cur->_data) > kot(data))//比较去调用KeyOfT对象的重载的()
- {
- parent = cur;
- cur = cur->_left;
- }
- else if (kot(cur->_data) < kot(data))//比较去调用KeyOfT对象的重载的()
- {
- parent = cur;
- cur = cur->_right;
- }
- else
- {
- //返回相同结点的迭代器
- return make_pair(iterator(cur),false);
- }
- }
- cur = new Node(data);
- //记录一下结点,好返回,因为下面会改变cur
- Node* newnode = cur;
-
- cur->_col = RED;//把结点以红色插入
- if (kot(parent->_data) < kot(data))//比较去调用KeyOfT对象的重载的()
- {
- parent->_right = cur;
- cur->_parent = parent;
- }
- else
- {
- parent->_left = cur;
- cur->_parent = parent;
- }
-
- //进行红黑树的控制
- while (parent && parent->_col==RED)//父节点位黑或者不存在就结束了
- {
- Node*gradparent = parent->_parent; //这里的祖父一定是存在的,因为parent->_col==RED,所以parent不可能是根节点
- if (gradparent->_left==parent)//在左边进行插入的
- {
- Node* uncle = gradparent->_right;
- if (uncle && uncle->_col == RED)//u存在且为红色,情况1
- {
- uncle->_col = BLACK;
- parent->_col = BLACK;
- gradparent->_col = RED;
- cur = gradparent;//把祖父当成新增结点
- parent = cur->_parent;
- }
- else //u不存在或为黑
- {
- if (parent->_left==cur) //在parent插入左边插入节点,情况2
- {
- //单旋
- // g
- // p u(存在为黑,或不存在)
- // c
- RoateR(gradparent);
- gradparent->_col = RED;
- parent->_col = BLACK;
- }
- else //在parent的右边插入节点 ,情况3
- {
- //双旋
- // g
- // p u
- // c
- RoateL(parent);
- RoateR(gradparent);
- cur->_col = BLACK;
- gradparent->_col = RED;
- }
- break;
-
- }
- }
- else //gradparent->_left==parent,在树的右边插入了节点
- {
-
- Node*uncle = gradparent->_left;
- if (uncle && uncle->_col==RED)//情况1 u存在且为红
- {
- parent->_col = BLACK;
- uncle->_col = BLACK;
- gradparent->_col = RED;
- cur = gradparent;
- parent = cur->_parent;
- }
- else //u不存在或着u为黑
- {
- // g
- //u p
- // cur
- if (parent->_right == cur)//情况2
- {
- //进行左单旋
- RoateL(gradparent);
- parent->_col = BLACK;
- gradparent->_col = RED;
- }
- // g
- //u p
- // c
- else //情况3
- {
- //双旋
- RoateR(parent);
- RoateL(gradparent);
- cur->_col = BLACK;
- gradparent->_col = RED;
- }
- break;
- }
- }
- }
- _root->_col = BLACK;//不管是子树还是根节点都需要是黑色
- return make_pair(iterator(newnode),true);
- }
-
-
- //析构函数
- ~RBTtree()
- {
- Destory(_root);
- _root = nullptr;
- }
- void Destory(Node*root)
- {
- if (root == nullptr)
- return;
- Destory(root->_left);
- Destory(root->_right);
- delete root;
- }
- private:
- Node* _root;
- };
-
- }
my_set.h
- #pragma once
- #include"RBTree.h"
-
- namespace zcf
- {
- template<class K>
- class set
- {
- public:
- struct SetKeyOfT
- {
- const K& operator()(const K& k)
- {
- return k; //直接返回key
- }
- };
-
- typedef typename RBTtree
::iterator iterator; //迭代器 -
- pair
bool > insert(const K& key) - {
- return _t.insert(key);
- }
- iterator begin()
- {
- return _t.begin();
- }
- iterator end()
- {
- return _t.end();
- }
- iterator find(const K& key)
- {
- return _t.find(key);
- }
-
- protected:
- RBTtree
_t; //关键,set知道自己是key,传的是key - //set知道怎么比较SetKeyOfT
- };
-
- void test_set()
- {
- set<int> s;
- s.insert(1);
- s.insert(2);
- s.insert(3);
- set<int>::iterator it = s.begin();
- while (it!=s.end())
- {
- cout << *it<<" ";
- ++it;
- }
- cout << endl;
-
- set<int> s1(s);//拷贝构造
- set<int>::iterator it1 = s1.begin();
- while (it1 != s1.end())
- {
- cout << *it1 << " ";
- ++it1;
- }
- cout << endl;
-
- set<int> s2;
- s2 = s1; //赋值运算
- set<int>::iterator it2 = s2.begin();
- while (it2 != s2.end())
- {
- cout << *it2 << " ";
- ++it2;
- }
- cout << endl;
- }
- }
-
my_map.h
- #pragma once
- #include"RBTree.h"
-
- namespace zcf
- {
- template<class K,class V>
- class map
- {
- public:
-
- struct MapKeyOfT //提供一个内置类
- {
- const K& operator()(const pair
& kv) - {
- return kv.first; //使用key进行比较,返回pair的第一个值
- }
- };
- //注意这里
- typedef typename RBTtree
, MapKeyOfT>::iterator iterator;//迭代器 -
-
-
- pair
bool > insert(const pair& kv) - {
- return _t.insert(kv);
- }
-
- iterator begin()
- {
- return _t.begin();
- }
-
- iterator end()
- {
- return _t.end();
- }
-
- iterator find(const K& key)
- {
- return _t.find(key);
- }
-
- V& operator[](const K&key)
- {
-
- auto ret=_t.insert(make_pair(key,V()));//给缺省值
- //ret是pair对象
- return ret.first->second;//调用迭代器的operator->()
- }
-
-
- protected:
- RBTtree
, MapKeyOfT> _t; //这里是关键,map知道自己是pair,所以传的pair - //比较也是这样的,map知道怎么比较MapKeyOfT
- };
-
- void test_map()
- {
- map
dict; - dict.insert(make_pair("left","左"));
- dict.insert(make_pair("right", "右"));
- dict.insert(make_pair("map", "地图"));
- dict["map"] = "地图或映射"; //修改second
- dict["state"] = "状态"; //新增
- auto it = dict.begin();
- while (it!=dict.end())
- {
- cout << it->first << ":" << it->second << endl;
- ++it;
- }
- cout << endl;
-
-
- map
dict1(dict) ;//拷贝构造 - auto it1 = dict.begin();
- while (it1 != dict1.end())
- {
- cout << it1->first << ":" << it1->second << endl;
- ++it1;
- }
- cout << endl;
- }
- }
main.cpp
- #include
- #include
- using namespace std;
- #include"my_set.h"
- #include"my_map.h"
-
- int main()
- {
- zcf::test_set();
- zcf::test_map();
- return 0;
- }

对于迭代器相对比较难的是++:


