数据结构与算法(九)STL

理解迭代器是理解 STL 的关键所在。模板使得算法独立于存储的数据类型,而迭代器使得算法独立于使用的容器类型。

1.字符串

1.头文件包含<string>

2.字符串构造函数:

string strs //生成空字符串
string s(str) //生成字符串str的复制品
string s(str, stridx) //将字符串str中始于stridx的部分作为构造函数的初值
string s(str, strbegin, strlen) //将字符串str中始于strbegin、长度为strlen的部分作为字符串初值
string s(cstr) //以C_string类型cstr作为字符串s的初值
string s(cstr,char_len)    //以C_string类型cstr的前char_len个字符串作为字符串s的初值
strings(num, c) //生成一个字符串,包含num个c字符
strings(strs.beg(), strs.end())    //以区间[beg, end]内的字符作为字符串s的初值

~string() //销毁所有内存,释放内存

std::string s('x');    //错误
std::string s(1, 'x');    //正确

std::string s("x");    //正确

目前,在 C++ 中确实存在一个从 const char * 到 string 的隐式型别转换,却不存在从 string 对象到 C_string 的自动型别转换。对于 string 类型的字符串,可以通过 c_str() 函数返回该 string 类对象对应的 C_string。getline(cin,str,c).输入流,字符串,终止符。

3.字符串成员函数

输入输出字符串:cout<<str, cin>>str,可以实现将以空格或回车为 "结束符" 的字符序列读入到对应的字符串中,并且开头和结尾的空白字符不包括进字符串中。遇到文件结束符、分界符、回车符时,将终止读入操作。

赋值=:后面必须有一个string对象。

插入:str.insert(p, string), str中的位置p插入字符或字符串string。

删除:str.erase(p, len), str中的位置p开始删除长度len。

获取子串:substr (size_t pos = 0, size_t len = npos);

字符串替换:replace (size_type p0, size_type n0, const basic_string& str, size_type pos, size_type n); //使用串 str 的子串 str [pos, pos + n-1] 替换源串中的内容,从位置 p0 处开始替换,替换字符 n0 个。

字符串查找:str.find (string, size_type);string可以为字符或字符串,后面一个参数为搜索开始位置。rfind()为反向搜索。

支持迭代器:begin()、end()、rbegin ()、rend()。反向迭代。

2.序列容器

1.五种序列容器:array<T,N>数组容器不能增加或删除元素。vector<T> (向量容器),在末尾可添加元素。deque<T> (双向队列容器) :是一个长度可变的、可以自动增长的序列,在序列的两端都能高效地增加或删除元素。list<T> 双链表。forward list<T> (正向链表容器) 单链表。均使用迭代器来访问元素,进行增删改查。

2.array<T,N>..std::array<double, 100> data {};默认初始化。有size()函数,empty()函数。可以赋值,比较。类似数组。迭代器遍历元素。

3.vector<T>. v, v1(v2),v2=v1, v1(n,val),  v1{}, v1={}.   empty(), size(), v1!=v2, v1<=字典序比较. 插入元素:auto iter = words.emplace(++std::begin(words),5,'A');插入一个字符串“AAAAA”,插入位置为第一个参数。erase(),clear()容量不变。

4.deque 容器中的元素是序列,但是内部的存储方式和 vector 不同。它组织元素的方式导致容器的大小总是和容量相等。但是其它和vector一样。但可以在头部添加或删除元素。push-front,pop_front.

5.list容器的构造函数的用法类似于 vector 或 deque 容器。可以使用 list 容器的成员函数 push_front() 在它的头部添加一个元素。调用 push_back() 可以在 list 容器的末尾添加一个元素对于 list 的成员函数 clear() 和 erase(),它们的工作方式及效果,和前面的序列容器相同。list 容器的成员函数 remove() 则移除和参数匹配的元素。可以在对元素进行排序后,再使用 unique().ist 容器并不提供随机访问迭代器,只提供双向迭代器,因此不能对 list 中的元素使用 sort() 算法,定义自己的无参 sort() 函数将所有元素升序排列定义在头文件 functional 中的模板 greater<T>。这个模板定义了一个用来比较 T 类型对象的函数对象。names.sort(std::greater<std::string>())。list 的成员函数 merge() 以另一个具有相同类型元素的 list 容器作为参数。两个容器中的元素都必须是升序。参数 list 容器中的元素会被合并到当前的 list 容器中。

6.forward_list 容器以单链表的形式存储元素。没有size()。 distance() 函数来得到元素的个数。distance(std::begin(my_words),std::end(my_words))

3.容器适配器

容器适配器是一个封装了序列容器的类模板,它在一般序列容器的基础上提供了一些不同的功能。之所以称作适配器类,是因为它可以通过适配容器现有的接口来提供不同的功能。

1.stack<T>:默认实现的是一个后入先出(Last-In-First-Out,LIFO)的压入栈。stack<T> 模板定义在头文件 stack 中。

2.queue<T>:默认实现的是一个先入先出(First-In-First-Out,LIFO)的队列。可以为它指定一个符合确定条件的基础容器。queue<T> 模板定义在头文件 queue 中。

3.priority_queue<T>:是一个封装了 vector<T> 容器的适配器类模板,默认实现的是一个会对元素排序,从而保证最大元素总在队列最前面的队列。priority_queue<T> 模板定义在头文件 queue 中。

4.queue:

  • front():返回 queue 中第一个元素的引用。如果 queue 是常量,就返回一个常引用;如果 queue 为空,返回值是未定义的。
  • back():返回 queue 中最后一个元素的引用。如果 queue 是常量,就返回一个常引用;如果 queue 为空,返回值是未定义的。
  • push(const T& obj):在 queue 的尾部添加一个元素的副本。这是通过调用底层容器的成员函数 push_back() 来完成的。
  • push(T&& obj):以移动的方式在 queue 的尾部添加元素。这是通过调用底层容器的具有右值引用参数的成员函数 push_back() 来完成的。
  • pop():删除 queue 中的第一个元素。
  • size():返回 queue 中元素的个数。
  • empty():如果 queue 中没有元素的话,返回 true。
  • emplace():用传给 emplace() 的参数调用 T 的构造函数,在 queue 的尾部生成对象。
  • swap(queue<T> &other_q):将当前 queue 中的元素和参数 queue 中的元素交换。它们需要包含相同类型的元素。也可以调用全局函数模板 swap() 来完成同样的操作。

5.priority_queue<T>:priority_queue 容器适配器定义了一个元素有序排列的队列。默认队列头部的元素优先级最高。因为它是一个队列,所以只能访问第一个元素,这也意味着优先级最高的元素总是第一个被处理。但是如何定义“优先级”完全取决于我们自己。priority_queue<std::string> words { std::begin(wrds),std:: end(wrds)}初始化列表中的序列可以来自于任何容器,并且不需要有序。优先级队列会对它们进行排序。

6.堆。堆(heaps)不是容器,而是一种特别的数据组织方式。堆一般用来保存序列容器。用来创建堆的函数定义在头文件 algorithm 中。max_heap() 对随机访问迭代器指定的一段元素重新排列,生成一个堆。默认使用的是 < 运算符,可以生成一个大顶堆。例如:

td::vector<double>numbers{2.5,10.0,3.5,6.5,8.0,12.0,1.5,6.0};

std::make_heap(std::begin(numbers), std:rend(numbers));

numbers.push_back(11); // Result: 12 10 3.5 6.5 8 2.5 1.5 6 11

std::push_heap(std::begin(numbers), std::end(numbers));

删除堆顶:pop_heap(),push_back. is_heap() 判断元素段是否为堆。sort_heap(),它会将元素段作为堆来排序。如果用断言 greater() 来创建堆,会生成一个小顶堆,对它进行排序会生成一个降序序列。排序后的序列不是小顶堆。堆排序。

4.map容器

map 容器是关联容器的一种。在关联容器中,对象的位置取决于和它关联的键的值。键可以是基本类型,也可以是类类型。

map 容器有 4 种,每一种都是由类模板定义的。所有类型的 map 容器保存的都是键值对类型的元素。map 容器的元素是 pair<const K,T> 类型的对象,这种对象封装了一个 T 类型的对象和一个与其关联的 K 类型的键。pair 元素中的键是 const,因为修改键会扰乱容器中元素的顺序。

  1. map<K,T>容器,保存的是 pair<const K,T> 类型的元素。pair<const K,T> 封装了一对键对象,键的类型是 K,对象的类型是 T。每个键都是唯一的,所以不允许有重复的键;但可以保存重复的对象,只要它们的键不同。map 容器中的元素都是有序的,元素在容器内的顺序是通过比较键确定的。默认使用 less<K> 对象比较。
  2. multimap<K,T> 容器和 map<K,T> 容器类似,也会对元素排序。它的键必须是可比较的,元素的顺序是通过比较键确定的。和 map<K,T> 不同的是,multimap<K,T> 允许使用重复的键。因此,一个 multimap 容器可以保存多个具有相同键值的 <const K,T> 元素。
  3. unordered_map<K,T> 中 pair< const K,T>元素的顺序并不是直接由键值确定的,而是由键值的哈希值决定的。哈希值是由一个叫作哈希的过程生成的整数,本章后面会解释这一点。unordered_map<K,T>不允许有重复的键。
  4. unordered_multimap<K,T> 也可以通过键值生成的哈希值来确定对象的位置,但它允许有重复的键

map 和 mutilmap 容器的模板定义在 map 头文件中,unordered_map 和 unordered_multimap 容器的模板定义在 unordered_map 头文件中。STL map 容器对元素的组织方式并没有具体要求,但元素一般都会保存在一个平衡二叉树中。对于每个父节点来说,它的键值大于它的左子节点,但小于它的右子节点。

初始化列表来指定 map 的初始值:map<std::string, size_t> people{{"Ann", 25}, {"Bill", 46},{"Jack", 32},{"Jill", 32}};

移动和复制构造函数

可以用另一个容器的一段元素来创建一个 map,用开始和结束迭代器以通常的方式指定元素。personnel {std::begin(people),std::end(people)};

map 容器提供了双向迭代器,这样就可以通过自增或自减访问元素。map 容器还提供了反向迭代器,所以可以从最后一个元素遍历到第一个元素。

map<K,T> 容器的成员函数 insert() 有多个版本,它们可以在 map 中插入一个或多个 pair<const K,T> 对象。

  1. auto pr = std::make_pair("Fred",22); //Create a pair element and insert it
  2. auto ret_pr = people.insert(pr);
  3. std::cout << ret_pr.first->first << " "<< ret_pr.first->second

成员函数 insert() 会返回一个 pair<iterator,bool> 对象。对象的成员 first 是一个迭代器,它要么指向插入元素,要么指向阻止插入的元素。如果 map 中已经保存了一个和这个键相同的对象,就会出现后面这种情况。这个对象的成员变量 second (布尔型)是返回对象,如果插入成功,返回值为 true,否则为 false。

同理,以上可用来修改元素中的值。当插入返回对象的second为false,可修改。

map 容器的成员函数 find() 可以返回一个元素的迭代器,这个元素的键值和参数匹配。

map 的成员函数 erase() 可以移除键和参数匹配的元素,然后返回所移除元素的个数。也可以用指向删除元素的迭代器作为 erase() 的参数。这种情况下,返回的迭代器指向被删除元素的下一个位置。这个参数必须是容器中的有效迭代器,不能是结束迭代器。pair<T1,T2> 模板定义在 utility 头文件中,如果不想使用 map 而只想使用 pair 对象,可以包含这个头文件。

map[key]可以获取对应键的值,若该键不存在则怎么办呢?map[key]可以插入该键值对。

5.mutimap

multimap 容器保存的是有序的键/值对,但它可以保存重复的元素。multimap 中会出现具有相同键的元素序列,multimap 的成员函数 find() 可以返回一个键和参数匹配的元素的迭代器如果没有找到键,会返回一个结束迭代器,所以我们应该总是对返回值进行检查。

用自定义的函数对象来比较元素

  1. std::map<std::unique_ptr<string>,std::string,Key_compare> phonebook;
  2. class Key_compare
  3. {
  4. public:
  5. bool operator () (const std::unique_ptr<string>& p1, const std::unique_ptr <string>& p2) const
  6. {
  7. return *p1 < *p2;
  8. }
  9. };

6.unorded_map

unordered_map 包含的是有唯一键的键/值对元素。容器中的元素不是有序的。元素的位置由键的哈希值确定,因而必须有一个适用于键类型的哈希函数。如果用类对象作为键,需要为它定义一个实现了哈希函数的函数对象。如果键是 STL 提供的类型,通过特例化 hash<T>,容器可以生成这种键对应的哈希函数。

一般情况下,元素被保存在哈希表中,这个表中的条目被称为格子,每个格子可以包含几个元素。一个给定的哈希值会选择特定的格子,因为哈希值可能的个数几乎可以肯定会大于格子的个数,两个不同的哈希值可能会映射到同一个格子上。因此,不同键会产生相同的哈希值,会产生碰撞,而且两个不同的哈希值选择相同的格子也会导致碰撞的产生。

  • 载入因子是每个格子平均保存的元素的个数。这个值等于容器中元素个数除以格子的个数。当容器达到最大载入因子时,容器会为格子分配更多的空间,这通常也会对容器中的元素重新进行哈希。默认为1.0。组织格子的方式有很多。一种方式是将格子看作类似 vector 的序列,然后在哈希表中保存序列的地址。另一种方式是将格子定义为链表,在哈希表中保存根节点。具体使用哪种方法取决于我们的实现。

1.unordered_map初始化详解。

unordered_map<std::string, size_t> people {{"Jan",44}, {"Jim", 33}, {"Joe", 99}}; 它会用定义在 string 头文件中的 hash<string> 来对 string 进行哈希。如果没有提供初始值,默认的构造函数会生成一个空容器,它有默认个数的格子。当我们知道要在容器中保存多少个元素时,可以在构造函数中指定应该分配的格子的个数:后面加一个参数表示格子个数。people.bucket_count()返回格子个数。people.rehash(15);改变格子个数。

对于 unordered_map,可以在下标运算符中使用键来获取它所对应对象的引用。unordered_map 的迭代器是可以使用的,因此可以用基于范围的 for 循环来访问它的元 素。 unordered_map 的成员函数 bucket_count() 返回的格子个数。bucket_size() 可以返回参数指定的格子中的元素个数。bucket() 返回的是格子的索引值,包含和传入的参数键匹配的元素。

people.bucket_size(people.bucket(key))。每个格子中的元素个数记录在一个 vector 容器中。所以格子个数的序列是 8、64、512、1024、2048、4096、8192。可以调用 unordered_map 的成员函数 erase() 来移除元素。参数可以是标识元素的一个键或是指向它的一个迭代器。当参数是键时,erase() 会返回一个整数,它是移除元素的个数,所以 0 表示没有找到匹配的元素。当参数是迭代器时,返回的迭代器指向被移除元素后的元素。成员函数 clear() 会移除所有的元素。当容器中没有元素时,成员函数 empty() 返回 true。

7.unordered_multimap

当容器中只有一个 key 时,可以用 find() 来访问这个元素。如果超过一个,可以用 equal_range() 来访问这段元素。当然,在这两种情况下都可以使用 equal_range()。find() 总会返回它所找到的第一个元素的迭代器,如果找不到这个键,会返回一个结束迭代器。

8.C++排序算法

1.函数模板 sort<Iter>() 的类型参数 Iter 是元素段元素对应的迭代器类型,而且它们必须支持随机访问迭代器。这表明 sort() 算法只能对提供随机访问迭代器的容器中的元素进行排序,也说明 sort() 只能接受 array、vector、deque 或标准数组中的元素。

  1. std::sort(std::begin(names), std::end(names), [](const Name& name1, const Name& name2) {return name1.get_second() < name2.get_second(); });

2.sort() 算法可能会改变相等元素的顺序,有时候这不是我们想要的。stable_sort()。

3.对于部分排序,有一个特殊的算法 partial_sort(),它需要 3 个随机访问迭代器作为参数。如果这个函数的参数是 first、second 和 last,那么这个算法会被应用到 [first,last) 这个范围内的元素上。执行这个算法后,[first,second) 会包含降序序列 [first,last) 中最小的 second-first 个元素。

4.如果两个迭代器参数所指定范围内的元素是升序排列的,函数模板 is_sorted() 就会返回 true。

5.merge() 算法返回的迭代器指向合并序列末尾的后一个位置,所以可以通过这个函数调用使用的第 5 个参数加上这个函数返回的迭代器来确定合并序列的范围。

  1. std::vector<int> result(these.size() + those.size() + 10);//Plenty of room for results
  2. auto end_iter = std::merge(std::begin(these), std::end(these),std::begin(those), std::end(those),std::begin (result), std::greater<>());// Merge 1st range and 2nd range into result

6.find() 为在输入迭代器所定义的范围内查找单个对象的算法,它可以在前两个参数指定的范围内查找和第三个参数相等的第一个对象。find 算法会返回一个指向被找到对象的迭代器,如果没有找到对象,会返回这个序列的结束迭代器。find_if() 同 find() 一样, find_if() 来查找 numbers 中第一个大于 value 的元素::find_if(std::begin(numbers), std::end(numbers),[value](int n) { return n > value; });

7.search() 算法返回的迭代器指向找到的子序列的第一个元素,因此为了搜索 phrase 的第二个实例,iter 必须增加 phrase 中元素的个数,使它指向下一个找到的序列的第一个元素。子序列查找。

  1. while((iter = std::search(iter, end_iter, std::begin(phrase), std::end (phrase) , [](char ch1, char ch2) { return std::toupper (ch1) == std:: toupper (ch2); })) != end_iter)
  2. {
  3. ++count;
  4. std::advance(iter, phrase.size()); // Move to beyond end of subsequence found
  5. }

8.binary_search() 实现了一个二分查找算法。它会在前两个参数指定范围内搜索等同于第三个参数的元素。如果找到第三个参数,这个算法会返回布尔值 true,否则返回 false。binary_search() 能告诉我们元素是否在这个序列中,但当它在序列中时,却不能告诉我们它的位置。

9.equal()。判断两个序列是否相等。用 == 运算符的第二个版本期望 4 个参数:第一个序列的开始和结束迭代器,第二个序列的开始和结束迭代器,如果两个序列的长度不同,那么结果总是为 false。

10.lexicographical_compare()算法可以比较由开始和结束迭代器定义的两个序列。它的前两个参数定义了第一个序列,第 3 和第 4 个参数分别是第二个序列的开始和结束迭代器。如果第一个序列的字典序小于第二个,这个算法会返回 true,否则返回 false

11.next_permutation() 会生成一个序列的重排列,它是所有可能的字典序中的下一个排列,默认使用 < 运算符来做这些事情。它的参数为定义序列的迭代器和一个返回布尔值的函数,这个函数在下一个排列大于上一个排列时返回 true,如果上一个排列是序列中最大的,它返回 false,所以会生成字典序最小的排列。当我们想以降序的方式生成排列时,可以使用 prev_permutation()。is_permutation() 算法可以用来检查一个序列是不是另一个序列的排列,如果是,会返回 true。

12.unique() 算法可以在序列中原地移除重复的元素。在移除重复元素后,它会返回一个正向迭代器作为新序列的结束迭代器。

13.replace() 算法会用新的值来替换和给定值相匹配的元素。它的前两个参数是被处理序列的正向迭代器,第 3 个参数是被替换的值,第 4 个参数是新的值。下面展示了它的用法:

  1. std::deque<int> data {10, -5, 12, -6, 10, 8, -7, 10, 11};
  2. std::replace(std::begin(data), std::end(data), 10, 99);
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值