在实际搜索中有两个搜索模型:Key的搜索模型和Key/Value的搜索模型
Key的搜索模型:
简单来说就是在一个搜索树,搜索树中的内容是一个一个的关键值,而通过一定的查找手段可以确定一个值在不在这个树内,在实际应用情境中可以在闸机,门禁等地方遇到这个搜索模型,通过给定的信息在已有的信息库中进行搜索,如果有就同一进入,如果没有就禁止进入
Key/Value的搜索模型:
这种搜索模型有两个功能,一个功能是进行搜索Key在不在这个搜索树内,第二个功能是用来通过Key可以查找Value的值,在实际应用情景中可以在字典,统计单词出现的次数中见到,在后面也会进行一些举例来更好的理解这种场景的意义所在
在前面的学习中有学到过不同的容器,例如有vector list deque等,这些容器被叫做是序列式容器,原因是因为它们底层的数据结构都是线性序列的数据结构,存储的是元素的本身
关联式容器也是用来存储数据的,但是和序列式不同的是,它里面存储的是
键值对:
用来表示具有一一对应关系的一种结构,该结构中一般只包含两个成员变量key和value,key代
表键值,value表示与key对应的信息
对于set的定义解释:
Sets are containers that store unique elements following a specific order.
In a set, the value of an element also identifies it (the value is itself the key, of type T), and each value must be unique. The value of the elements in a set cannot be modified once in the container (the elements are always const), but they can be inserted or removed from the container.
Internally, the elements in a set are always sorted following a specific strict weak ordering criterion indicated by its internal comparison object (of type Compare).
set containers are generally slower than unordered_set containers to access individual elements by their key, but they allow the direct iteration on subsets based on their order.
Sets are typically implemented as binary search trees.
set是按照一定次序存储元素的容器,它的基本用法如下所展示:
实现也是用模板来实现的,第一个模板参数是存储的元素类型,第二个是仿函数,第三个是空间配置器
set的基本使用
void testset1()
{
int arr[] = { 1,2,3,4,5,6,1,2,3,4 };
set<int> st(arr, arr + sizeof(arr) / sizeof(arr[0]));
for (auto e : st)
{
cout << e << " ";
}
cout << endl;
}
上面的样例构造了一个set,并将其中的内容进行了输出,输出结果是1 2 3 4 5 6,说明它包含了去重和排序的功能
insert的使用
在cplusplus中,对于insert的定义如下:
insert有三种用法,一种是直接插入一个值,在前文定义中提到value_type实际上是The first template parameter (T),也就是第一个模板参数,那么就默认可以把这个当成是set中存储的元素类型,也就是直接向set中插入一个元素,而函数的返回值是一个键值对,键值对中第一个数据是一个迭代器,第二个数据是布尔值,在文档中对于其定义如下:
The single element versions (1) return a pair, with its member pair::first set to an iterator pointing to either the newly inserted element or to the equivalent element already in the set. The pair::second element in the pair is set to true if a new element was inserted or false if an equivalent element already existed.
The versions with a hint (2) return an iterator pointing to either the newly inserted element or to the element that already had its same value in the set.
Member type iterator is a bidirectional iterator type that points to elements.
pair is a class template declared in (see pair).
简单来说就是,第一种版本的函数,其成员pair::first被设置为一个迭代器,该迭代器指向新插入的元素或集合中已经存在的等效元素。如果插入了新元素,则pair::中的第二个元素设为true;如果已经存在等效元素,则设为false
第二个版本的函数返回一个迭代器,该迭代器指向新插入的元素或在集合中已经具有相同值的元素
第三个版本的函数是通过迭代器来进行插入的
从测试的函数结果可以看出,和预期的结果是一样的,使用版本1的函数如果插入成功则pair的返回值是true,如果插入失败返回的是false,迭代器指向的位置就是插入元素的位置
find的使用和前面的STL容器基本相同,如果找到了返回该位置的迭代器,如果没找到返回end(),下面利用find函数来进行特定位置的插入
// 在迭代器所指示的位置插入一个元素
set<int>::iterator it3 = st.find(1);
if (it3 != st.end())
{
st.insert(it3, 10);
}
erase的用法:
erase函数版本1的参数时一个迭代器,返回值为空,版本2的参数是一个值,返回的值是删除元素的数量
void testset3()
{
int arr[] = { 1,2,3,4,5,6 };
set<int> st(arr, arr + sizeof(arr) / sizeof(arr[0]));
for (auto e : st)
{
cout << e << " ";
}
cout << endl;
set<int>::iterator it = st.find(2);
if (it != st.end())
{
st.erase(it);
}
cout << st.erase(3) << endl;
}
前面的使用在vector和list等序列式容器中其实也有这样的操作,总体来说大差不差,但是set是一个关联式容器,因此它是有一些前面没有见过的内容和操作的
lower_bound和upper_bound:
简单来说,这两个函数可以构造出一个迭代器区间,lower_bound返回的是第一个大于等于val值的位置,而upper_bound返回的是第一个大于val值的位置,因此可以用这个构造出一个迭代器区间,可以供给erase来使用
void testset4()
{
int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
set<int> st(arr, arr + sizeof(arr) / sizeof(arr[0]));
for (auto e : st)
{
cout << e << " ";
}
cout << endl;
set<int>::iterator lowit = st.lower_bound(2);
set<int>::iterator upit = st.upper_bound(5);
// 构造出要删除的区间是[2,5]
st.erase(lowit, upit);
for (auto e : st)
{
cout << e << " ";
}
cout << endl;
}
因此输出结果为
1 2 3 4 5 6 7 8 9 10
1 6 7 8 9 10
equal_range的使用:
获取相等元素的范围
返回一个范围的边界,该范围包括容器中与val等效的所有元素。
因为set容器中的所有元素都是唯一的,所以返回的范围最多只包含一个元素。
如果没有找到匹配项,则返回的范围长度为0,两个迭代器都指向容器内部比较对象(key_comp)认为在val之后的第一个元素。
如果容器的比较对象自反性地返回false(即,无论元素作为参数传递的顺序如何),则认为集合中的两个元素相等。
该函数返回一个pair,其成员pair::first是范围的下界(与lower_bound相同),pair::second是上界(与upper_bound相同)。
成员类型iterator和const_iterator是指向元素的双向迭代器类型。
// set::equal_elements
void testset5()
{
std::set<int> myset;
for (int i = 1; i <= 5; i++) myset.insert(i * 10); // myset: 10 20 30 40 50
std::pair<std::set<int>::const_iterator, std::set<int>::const_iterator> ret;
ret = myset.equal_range(30);
std::cout << "the lower bound points to: " << *ret.first << '\n';
std::cout << "the upper bound points to: " << *ret.second << '\n';
}
下面介绍的是multiset
对于这个容器来说,它是一个简单的排序的作用,并不会有去重的效果,如果有多个值,find返回中序第一个val,multiset在底层实际存储的是
从中看出,map中使用的就是Key_Value搜索模型,在传参的时候也需要传递两个参数
insert的使用
结合上面的定义表可以看到,在map中的value_type实际上是一个键值对类型,也就是说在插入元素的时候每次要插入的都是一个键值对类型的元素,下面演示两种insert的插入方法
void testmap1()
{
map<string, string> mp;
// 1. 构造对象
mp.insert(pair<string, string>("sort", "排序"));
mp.insert(pair<string, string>("insert", "插入"));
// 2. 函数调用
mp.insert(make_pair("left", "左边"));
}
通常来说使用函数调用来构造一个pair对象更好一些
在序列式容器中,[]通常是用作运算符重载来看的,可以用于数据的随机存取,但是在map中并非如此,map中的[]的功能扩展很多:
A call to this function is equivalent to:
(*((this->insert(make_pair(k,mapped_type()))).first)).second
那么对这句进行解析:(*((this->insert(make_pair(k,mapped_type()))).first)).second
在理解这句前,要首先知道insert调用的结果是pair
因此通过[]得到的返回值其实是通过Key来访问到Value值,如果没有Key会优先创建出Key的值,再对Value值进行一些访问修改等操作…
Multimaps
是关联式容器,它按照特定的顺序,存储由key
和value
映射成的键值对
,其中多个键值对之间的key
是可以重复的multimap
中,通常按照key
排序和惟一地标识元素,而映射的value
存储与key
关联的内容。key
和value
的类型可能不同,通过multimap
内部的成员类型value_type
组合在一起,value_type
是组合key
和value
的键值对:typedef pairvalue_type
multimap
中的元素总是通过其内部比较对象,按照指定的特定严格弱排序标准对key
进行排序的multimap
通过key
访问单个元素的速度通常比unordered_multimap
容器慢,但是使用迭代器直接遍历multimap
中的元素可以得到关于key有序的序列multimap
在底层用二叉搜索树(红黑树)来实现。注意:multimap
和map
的唯一不同就是:map
中的key
是唯一的,而multimap
中key
是可以