第九章 顺序容器
9.1
(a)std::set是最好的。现在,我们可以选择list,比vector或deque更好,因为我们可能需要经常在中间插入元素来保持按字母顺序排序。
(b)deque,如果程序需要在前面和后面插入或删除元素,而不是在中间插入或删除元素,请使用deque.
(c)vector,不需要在前面或后面插入或删除。如果你的程序有很多小元素和空间开销,不要使用list或forward-list。
9.2
list<deque<int>> ldi;
9.3
-它们指向同一个容器中的元素,或者是容器最后一个元素之后的位置。
-可以通过反复递增begin来到达end.
9.4
bool find(vector<int>::iterator beg, vector<int>::iterator end, int value)
{
for (auto iter = beg; iter != end; ++iter)
if (*iter == value) return true;
return false;
}
9.5
vector<int>::iterator find(vector<int>::iterator beg, vector<int>::iterator end, int value)
{
for (auto iter = beg; iter != end; ++iter)
if (*iter == value) return iter;
return end;
}
9.6
运算符< 不能用于list容器。
while(iter1 != iter2)
9.7
vector<int>:: size_type
9.8
list<string>::iterator || list<string>::const_iterator
list<string>::iterator
9.9
cbegin是一个const成员,返回容器的const_iterator类型。
begin是一个非常量成员,返回容器的iterator类型。
9.10
it1: vector<int>::iterator
it2,3,4 : vector<int>::const_iterator
9.11
vector<int> vec; // 0
vector<int> vec(10); // 10个0
vector<int> vec(10,1); // 10个1
vector<int> vec{1,2,3,4,5}; // 1,2,3,4,5
vector<int> vec(other_vec); // same as other_vec
vector<int> vec(other_vec.begin(), other_vec.end()); // same as other_vec
9.12
将另一个容器作为参数(数组除外)的构造函数必须使得两个容器的容器类型和元素类型相同。它还将把接收到的容器的所有元素复制到新容器中:
list<int> numbers = {1, 2, 3, 4, 5};
list<int> numbers2(numbers); // ok, numbers2 has the same elements as numbers
vector<int> numbers3(numbers); // error: no matching function for call...
list<double> numbers4(numbers); // error: no matching function for call...
采用两个迭代器作为参数的构造函数不要求容器类型相同。此外,只要能够将要复制的元素转换为要初始化的容器的元素类型,新容器和原始容器中的元素类型就可能不同。它还将只复制由接收的迭代器分隔的对象。
list<int> numbers = {1, 2, 3, 4, 5};
list<int> numbers2(numbers.begin(), numbers.end); // ok, numbers2 has the same elements as numbers
vector<int> numbers3(numbers.begin(), --numbers.end()); // ok, numbers3 is {1, 2, 3, 4}
list<double> numbers4(++numbers.beg(), --numbers.end()); // ok, numbers4 is {2, 3, 4}
forward_list<float> numbers5(numbers.begin(), numbers.end()); // ok, number5 is {1, 2, 3, 4, 5}
9.13
#include <iostream>
#include <string>
#include <vector>
#include <list>
using std::list;
using std::vector;
using std::cout;
using std::endl;
int main()
{
list<int> ilst(5, 4);
vector<int> ivc(5, 5);
//! from list<int> to vector<double>
vector<double> dvc(ilst.begin(), ilst.end());
for (auto i : ilst) cout << i;
cout << endl;
for (auto t : dvc) cout << t;
cout << endl;
//! from vector<int> to vector<double>
vector<double> dvc2(ivc.begin(), ivc.end());
for (auto i : ivc) cout << i;
cout << endl;
for (auto t : dvc2) cout << t;
return 0;
}
9.14
#include <iostream>
#include <string>
#include <vector>
#include <list>
int main()
{
std::list<const char*> l{"Mooophy", "pezy", "Queeuqueg"};
std::vector<std::string> v;
v.assign(l.cbegin(), l.cend());
for (const auto& ch : v) std::cout << ch << std::endl;
return 0;
}
9.15
#include <iostream>
#include <vector>
int main()
{
std::vector<int> vec1{1, 2, 3, 4, 5};
std::vector<int> vec2{1, 2, 3, 4, 5};
std::vector<int> vec3{1, 2, 3, 4};
std::cout << std::boolalpha << (vec1 == vec2) << std::endl;
std::cout << std::boolalpha << (vec1 == vec3) << std::endl;
return 0;
}
9.16
#include <iostream>
#include <vector>
#include <list>
int main()
{
std::list<int> list{1, 2, 3, 4, 5};
std::vector<int> vec1{1, 2, 3, 4, 5};
std::vector<int> vec2{1, 2, 3, 4};
std::cout << std::boolalpha
<< (std::vector<int>(list.begin(), list.end()) == vec1)
<< std::endl;
std::cout << std::boolalpha
<< (std::vector<int>(list.begin(), list.end()) == vec2)
<< std::endl;
}
9.17
除了无序关联容器外的所有容器都支持关系运算符,两边的运算对象必须是相同类型的容器,且必须保存相同类型的元素,该元素类型必须支持所需运算符。
9.18
#include <iostream>
#include <string>
#include <deque>
using std::string;
using std::deque;
using std::cout;
using std::cin;
using std::endl;
int main()
{
deque<string> input;
for (string str; cin >> str; input.push_back(str))
;
for (auto iter = input.cbegin(); iter != input.cend(); ++iter)
cout << *iter << endl;
return 0;
}
9.19
#include <iostream>
#include <string>
#include <list>
using std::string;
using std::list;
using std::cout;
using std::cin;
using std::endl;
int main()
{
list<string> input;
for (string str; cin >> str; input.push_back(str))
;
for (auto iter = input.cbegin(); iter != input.cend(); ++iter)
cout << *iter << endl;
return 0;
}
9.20
#include <iostream>
#include <deque>
#include <list>
using std::deque;
using std::list;
using std::cout;
using std::cin;
using std::endl;
int main()
{
list<int> l{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
deque<int> odd, even;
for (auto i : l) (i & 0x1 ? odd : even).push_back(i);
for (auto i : odd) cout << i << " ";
cout << endl;
for (auto i : even) cout << i << " ";
cout << endl;
return 0;
}
9.21
一样。第一次调用insert会将我们刚刚读入的string插入到iter所指的元素之前的位置。insert返回的迭代器恰好指向这个新元素。我们将此迭代器赋予iter并重复循环,读取下一个单词。只要继续有单词读入,每步while循环就会将一个新元素插入到iter之前,并将iter改变为新加入元素的位置。此元素为新的首元素。因此,每步循环将一个新元素插入到vector首元素之前的位置。
9.22
1.是一个死循环
2.向一个vector,string,deque插入元素会使所有指向容器的迭代器,引用和指针失效。
9.23
都是指向同一个元素的相同值。
9.24
#include <iostream>
#include <vector>
int main()
{
std::vector<int> v;
std::cout << v.at(
0); // terminating with uncaught exception of type std::out_of_range
std::cout << v[0]; // Segmentation fault: 11
std::cout << v.front(); // Segmentation fault: 11
std::cout << *v.begin(); // Segmentation fault: 11
return 0;
}
9.25
如果相等,什么也不会发生。
如果elem2是尾后迭代器,从elem1开始到最后都会被删除。
如果都是尾后迭代器,什么也不会发生。
9.26
#include <iostream>
#include <vector>
#include <list>
using std::vector;
using std::list;
using std::cout;
using std::endl;
using std::end;
int main()
{
int ia[] = {0, 1, 1, 2, 3, 5, 8, 13, 21, 55, 89};
//! init
vector<int> vec(ia, end(ia));
list<int> lst(vec.begin(), vec.end());
//! remove odd value
for (auto it = lst.begin(); it != lst.end();)
if (*it & 0x1)
it = lst.erase(it);
else
++it;
//! remove even value
for (auto it = vec.begin(); it != vec.end();)
if (!(*it & 0x1))
it = vec.erase(it);
else
++it;
//! print
cout << "list : ";
for (auto i : lst) cout << i << " ";
cout << "\nvector : ";
for (auto i : vec) cout << i << " ";
cout << std::endl;
return 0;
}
9.27
#include <iostream>
#include <forward_list>
using std::forward_list;
using std::cout;
using std::endl;
int main()
{
forward_list<int> flst = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
for (auto prev = flst.before_begin(), curr = flst.begin();
curr != flst.end();)
if (*curr & 0x1)
curr = flst.erase_after(prev);
else
prev = curr++;
for (auto i : flst) cout << i << " ";
cout << endl;
}
9.28
void find_and_insert(forward_list<string> &list, const string& to_find, const string& to_add)
{
auto prev = list.before_begin();
auto size = std::distance(list.begin(), list.end());
for (auto curr = list.begin(); curr != list.end(); prev = curr++)
if (*curr == to_find) list.insert_after(curr, to_add);
if (size == std::distance(list.begin(), list.end())) list.insert_after(prev, to_add);
}
9.29
添加75个新元素,并进行值初始化。
末尾删除90个元素。
9.30
如果容器保存的是类类型元素,且resize向容器添加新元素,则我们必须提供初始值,或者元素类型必须提供一个默认构造函数。
9.31
可以,注意list,forward_list没有迭代器+-n运算
#include <iostream>
#include <list>
using std::list;
using std::cout;
using std::endl;
using std::advance;
int main()
{
list<int> vi = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
auto iter = vi.begin();
while (iter != vi.end()) {
if (*iter % 2) {
iter = vi.insert(iter, *iter);
advance(iter, 2);
}
else
iter = vi.erase(iter);
}
for (auto i : vi) cout << i << " ";
return 0;
}
#include <iostream>
#include <forward_list>
using std::forward_list;
using std::cout;
using std::endl;
using std::advance;
int main()
{
forward_list<int> vi = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
auto iter = vi.begin(), prev = vi.before_begin();
while (iter != vi.end()) {
if (*iter % 2) {
iter = vi.insert_after(prev, *iter);
advance(iter, 2);
advance(prev, 2);
}
else
iter = vi.erase_after(prev);
}
for (auto i : vi) cout << i << " ";
return 0;
}
9.32
求值顺序不确定。
9.33
会失效,在向容器添加元素后,如果容器是vector或string,且存储空间被重新分配,则指向容器的迭代器,指针和引用都会失效。
9.34
循环范围有错,iter指向也不对。
#include <iostream>
using std::cout;
using std::endl;
#include <vector>
using std::vector;
int main()
{
vector<int> vi = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
auto iter = vi.begin();
while (iter != vi.end()) {
if (*iter % 2) {
iter = vi.insert(iter, *iter);
++iter;
}
++iter;
}
for (auto i : vi) cout << i << " ";
return 0;
}
9.35
size:指它已经保存的元素的数目
capacity:在不分配新的内存空间的前提下它最多可以保存多少元素
9.36
不可能。
9.37
list元素不需要连续存储。array有着固定的大小。
9.38
我的是成倍增长。
#include <iostream>
#include <string>
#include <vector>
int main()
{
std::vector<std::string> v;
std::string word;
while (std::cin >> word) {
v.push_back(word);
std::cout << v.capacity() << "\n";
}
return 0;
}
9.39
调用resize或reserve时给定的大小超过当前的capacity,vector才可能重新分配空间。
9.40
read | size | capacity |
---|---|---|
256 | 384 | 1024 |
512 | 768 | 1024 |
1000 | 1500 | 2000(clang is 2048) |
1048 | 1572 | 2048 |
9.41
#include <iostream>
#include <vector>
#include <string>
using std::vector;
using std::cout;
using std::endl;
using std::string;
int main()
{
vector<char> vec{'p', 'e', 'z', 'y'};
string str(vec.begin(), vec.end());
cout << str << endl;
return 0;
}
9.42
svec.reserve(120)
9.43
#include <string>
using std::string;
#include <iostream>
void Replace(string& s, const string& oldVal, const string& newVal)
{
for (auto beg = s.begin(); std::distance(beg, s.end()) >=
std::distance(oldVal.begin(), oldVal.end());) {
if (string{beg, beg + oldVal.size()} == oldVal) {
beg = s.erase(beg, beg + oldVal.size());
beg = s.insert(beg, newVal.cbegin(), newVal.cend());
std::advance(beg, newVal.size());
}
else
++beg;
}
}
int main()
{
{
string str{"To drive straight thru is a foolish, tho courageous act."};
Replace(str, "thru", "through");
Replace(str, "tho", "though");
std::cout << str << std::endl;
}
{
string str{
"To drive straight thruthru is a foolish, thotho courageous act."};
Replace(str, "thru", "through");
Replace(str, "tho", "though");
std::cout << str << std::endl;
}
{
string str{"To drive straight thru is a foolish, tho courageous act."};
Replace(str, "thru", "thruthru");
Replace(str, "tho", "though");
std::cout << str << std::endl;
}
{
string str{"my world is a big world"};
Replace(str, "world",
"worldddddddddddddddddddddddddddddddddddddddddddddddd");
std::cout << str << std::endl;
}
return 0;
}
9.44
#include <iostream>
#include <string>
using std::cout;
using std::endl;
using std::string;
using std::prev;
void Replace(string& s, string const& oldVal, string const& newVal)
{
for (string::size_type i = 0; i != s.size(); ++i)
if (s.substr(i, oldVal.size()) == oldVal) {
s.replace(i, oldVal.size(), newVal);
i += newVal.size() - 1;
}
}
int main()
{
string str{"To drive straight thru is a foolish, tho courageous act."};
Replace(str, "tho", "though");
Replace(str, "thru", "through");
cout << str << endl;
}
9.45
#include <iostream>
#include <string>
//! Exercise 9.45
std::string pre_suffix(const std::string& name, const std::string& pre,
const std::string& su);
int main()
{
std::string name("alan");
std::cout << pre_suffix(name, "Mr.", ",Jr.") << std::endl;
return 0;
}
inline std::string pre_suffix(const std::string& name, const std::string& pre,
const std::string& su)
{
auto ret = name;
ret.insert(ret.begin(), pre.begin(), pre.end());
ret.append(su);
return ret;
}
9.46
#include <iostream>
#include <string>
std::string pre_suffix(const std::string& name, const std::string& pre,
const std::string& su)
{
std::string ret(name);
ret.insert(0, pre);
ret.insert(ret.size(), su);
return ret;
}
int main()
{
std::string name("alan");
std::cout << pre_suffix(name, "Mr.", ",Jr.");
return 0;
}
9.47
#include <string>
#include <iostream>
using std::string;
using std::cout;
using std::endl;
int main()
{
string numbers{"0123456789"};
string alphabet{"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"};
string str{"ab2c3d7R4E6"};
cout << "numeric characters: ";
for (string::size_type pos = 0;
(pos = str.find_first_of(numbers, pos)) != string::npos; ++pos)
cout << str[pos] << " ";
cout << "\nalphabetic characters: ";
for (string::size_type pos = 0;
(pos = str.find_first_of(alphabet, pos)) != string::npos; ++pos)
cout << str[pos] << " ";
cout << endl;
return 0;
}
#include <string>
#include <iostream>
using std::string;
using std::cout;
using std::endl;
int main()
{
string numbers{"0123456789"};
string alphabet{"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"};
string str{"ab2c3d7R4E6"};
cout << "numeric characters: ";
for (string::size_type pos = 0;
(pos = str.find_first_not_of(alphabet, pos)) != string::npos; ++pos)
cout << str[pos] << " ";
cout << "\nalphabetic characters: ";
for (string::size_type pos = 0;
(pos = str.find_first_not_of(numbers, pos)) != string::npos; ++pos)
cout << str[pos] << " ";
cout << endl;
return 0;
}
9.48
string::npos
9.49
#include <string>
#include <fstream>
#include <iostream>
using std::string;
using std::ifstream;
using std::cout;
using std::endl;
int main()
{
ifstream ifs("../data/letter.txt");
if (!ifs) return -1;
string longest_word;
for (string word; ifs >> word;)
if (word.find_first_not_of("aceimnorsuvwxz") == string::npos &&
word.size() > longest_word.size())
longest_word = word;
cout << longest_word << endl;
}
9.50
#include <iostream>
#include <string>
#include <vector>
int sum_for_int(const std::vector<std::string> &v)
{
int sum = 0;
for (auto const& s : v) sum += std::stoi(s);
return sum;
}
float sum_for_float(const std::vector<std::string> &v)
{
float sum = 0.0;
for (auto const& s : v) sum += std::stof(s);
return sum;
}
int main()
{
std::vector<std::string> v = {"1", "2", "3", "4.5"};
std::cout << sum_for_int(v) << std::endl;
std::cout << sum_for_float(v) << std::endl;
return 0;
}
9.51
#include <array>
#include <iostream>
#include <string>
class Date {
public:
explicit Date(const std::string& str = "");
void Print();
unsigned year = 1970;
unsigned month = 1;
unsigned day = 1;
private:
std::array<std::string, 12> month_names{"Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
unsigned MonthFromName(const std::string& str);
};
Date::Date(const std::string& str)
{
if (str.empty()) return;
std::string delimiters{" ,/"};
auto month_day_delim_pos = str.find_first_of(delimiters);
if (month_day_delim_pos == std::string::npos)
throw std::invalid_argument("This format is not supported now.");
month = MonthFromName(str.substr(0, month_day_delim_pos));
auto day_year_delim_pos = str.find_first_of(delimiters, month_day_delim_pos + 1);
auto day_len = day_year_delim_pos - month_day_delim_pos - 1;
day = std::stoi(str.substr(month_day_delim_pos + 1, day_len));
year = std::stoi(str.substr(day_year_delim_pos + 1));
}
void Date::Print()
{
std::cout << year << "-" << month << "-" << day << "\n";
}
unsigned Date::MonthFromName(const std::string& str)
{
if (str.empty()) return 0;
if (std::isdigit(str[0])) return std::stoi(str);
for (size_t i = 0; i != 12; ++i) {
if (str.find(month_names[i]) != std::string::npos) return i + 1;
}
return 0; // not found
}
int main()
{
{ // default case
auto date = Date();
date.Print();
}
{ // case 0: January 1, 1900
auto date = Date("January 1, 1900");
date.Print();
}
{ // case 1: 1/1/1900
auto date = Date("1/1/1900");
date.Print();
}
{ // case 2: Jan 1, 1900
auto date = Date("Jan 1, 1900");
date.Print();
}
}
9.52
#include <stack>
using std::stack;
#include <string>
using std::string;
#include <iostream>
using std::cout;
using std::endl;
int main()
{
auto& expr = "This is (Mooophy(awesome)((((wooooooooo))))) and (ocxs) over";
auto repl = '#';
auto seen = 0;
stack<char> stk;
for (auto c : expr) {
stk.push(c);
if (c == '(') ++seen; // open
if (seen && c == ')') { // pop elements down to the stack
while (stk.top() != '(') stk.pop();
stk.pop(); // including the open parenthesis
stk.push(repl); // push a value indicate it was replaced
--seen; // close
}
}
// Test
string output;
for (; !stk.empty(); stk.pop()) output.insert(output.begin(), stk.top());
cout << output << endl; // "This is # and # over"
}