与 set 集合容器一样, multiset 多重容器也使用红黑树组织元素数据,只是 multiset 容器允许将重复的元素键值插入,而 set 容器则不允许。multiset 容器实现了 Sorted Associativate Container 、Simple Associative Container 和 Multiple Associative Container 概念的接口规范
关于怎么使用set和multiset,我是通过hdu4268这个题才开始关注的。
集合
Set、multiset都是集合类,差别在与set中不允许有重复元素,multiset中允许有重复元素。sets和multiset内部以平衡二叉树实现。
【平衡二叉树的定义 (AVL—— 发明者为Adel'son-Vel'skii 和 Landis)
平衡二叉查找树,又称 AVL树。 它除了具备二叉查找树的基本特征之外,还具有一个非常重要的特点:它 的左子树和右子树都是平衡二叉树,且左子树和右子树的深度之差的绝对值(平衡因子 ) 不超过1。 也就是说AVL树每个节点的平衡因子只可能是-1、0和1(左子树高度减去右子树高度)。
那么如何是二叉查找树在添加数据的同时保持平衡呢?基本思想就是:当在二叉排序树中插入一个节点时,首先检查是否因插入而破坏了平衡,若 破坏,则找出其中的最小不平衡二叉树,在保持二叉排序树特性的情况下,调整最小不平衡子树中节点之间的关系,以达 到新的平衡。所谓最小不平衡子树 指离插入节点最近且以平衡因子的绝对值大于1的节点作为根的子树。
在此对平衡二叉树有很好的解释:http://hxraid.iteye.com/blog/609949
http://www.cnblogs.com/fornever/archive/2011/11/15/2249492.html】
容器和算法:
容器:能容纳两个或多个值的数据结构。
数组是c++唯一直接支持的容器,但是数组并不适合来解决所有问题。为不同类别的问题寻找最适合的数据结构。eg:对于拼写检查程序,最适合的是散列表和二叉树。
在c++标准库里有许多现成的容器,可以直接拿来使用。
解决一个问题,找到最适合的容器只是编程工作的一部分,还需要一些适当的函数(算法)来处理这个容器里的数据才能实现最优效率。
向量容器:
由于数组受限于一个固定的长度,c++标准库提供的向量(Vector)类型解决了这个问题。就像可以创建不同类型的数组一样,也可以创建不同类型的向量. std::vector<type>vectorName
(1)向量可以动态地随着往里面添加元素而无限增大(前提是有足够可用的内存)。
(2)还可以用它的size方法查知某给定向量的当前长度(它当前包含的元素个数)。
(3)定义一个向量后,可以用push_back()的方法往它里面添加东西。
(4)还可用访问数组元素的语法来访问某给定向量里的各个元素。
eg:
#include<iostream>
#include<string>
#include<vector>
int main()
{
std::vector<std::string> names;
names.push_back("小甲鱼");
names.push_back("小金鱼");
for(int i=0;i<names.size();i++)
{
std::cout<<names[i]<<"\n";
}
return 0;
}
迭代器:
(1)一种功能非常有限但非常实用的函数,提供一些基本操作符:*,++,==,!=,=。
(2)是个所谓的智能指针,具有遍历复杂数据结构的能力。
(3)因为迭代器的功能很基本,所以标准库里的每一种容器都支持。
(4)通过使用迭代器,当在程序里改用另一种容器的时候就用不着修改那么多的代码了。
(5)每一种容器都必须提供自己的迭代器,事实上每一种容器都将其迭代器以嵌套的方式定义于内部。
(6)因此各种迭代器的借口相同,型号却不同,这就是所谓的泛型程序设计的概念:所有操作行为都使用相同接口,虽然它们的具体实现不同。
在上面这个例子中在遍历各个数组元素时,仍然把它视为一个c++数组来对待(这是因为向量容器允许使用下标操作符来来访问它的各个元素:name[x]),若想改用另一种不提供此方法访问的容器(比如栈),就不得不对程序做很多修改才得以实现。
#include<iostream>
#include<string>
#include<vector>
int main()
{
std::vector<std::string> names;
names.push_back("小甲鱼");
names.push_back("小金鱼");
std::vector<std::string>::iterator iter =names.begin();//begin方法是返回最初的值(指向的是起始位置)
while(iter !=names.end())//取得向量的最后一个元素(指向最后一个元素的下一个位置,即容器的底部)
{
std::cout<<*iter<<"\n";//指向最下面的一个元素的底部(用*取它的值)
++iter;
}
return 0;
}
算法:
迭代器的真正价值体现在它们可以和所有的容器配合使用,而使用迭代器去访问容器元素的算法可以和任何一种容器配合使用。
排序算法:冒泡排序,堆排序,快速排序等等
c++标准库包含着一个全面优化的排序算法,处理速度也非常理想。#include<algorithm>
再像下面这样调用sort()方法就可以了 std::sort(beginIterator,endIterator);
#include<iostream>
#include<string>
#include<vector>
#include<algorithm>
int main()
{
std::vector<std::string> names;
names.push_back("Larry");
names.push_back("Rola");
names.push_back("DingDing");
names.push_back("Joyjoy");
names.push_back("Michael");
names.push_back("Lucy");
names.push_back("Lilei");
std::sort(names.begin(),names.end());
std::vector<std::string>::iterator iter =names.begin();//begin方法是返回最初的值(指向的是起始位置)
while(iter !=names.end())//取得向量的最后一个元素(指向最后一个元素的下一个位置,即容器的底部)
{
std::cout<<*iter<<"\n";//指向最下面的一个元素的底部(用*取它的值)
++iter;
}
return 0;
}