C++ Primer 中文版(第 5 版)练习解答合集
自己写的解答,如有错误之处,烦请在评论区指正!
1
(a)如果插入过程不重要:先用 vector 接收所有数据,然后用 sort 排序。如果插入过程重要:用 list,在中间插入的时间复杂度是 O(1)
(b)deque,在两端插入、删除都是 O(1)
(c)先用 vector 接收所有数据,然后用 sort 排序
2
list<deque<int>> testList;
3
- 它们指向同一个容器中的元素,或者是容器最后一个元素之后的位置
- end 不在 begin 之前
4
bool findBetween(vector<int>::const_iterator begin, vector<int>::const_iterator end, int target) {
for (auto p = begin; p != end; ++p) {
if (*p == target) {
return true;
}
}
return false;
}
5
// 返回end迭代器表示没有找到
vector<int>::iterator findBetween(vector<int>::iterator begin, vector<int>::iterator end, int target) {
for (auto p = begin; p != end; ++p) {
if (*p == target) {
return p;
}
}
return end;
}
6
while
循环的条件应该是 iter1 != iter2
某些容器,如 list 不支持 <
运算符,因为数据在内存中的排列不是连续的。
7
注意是索引。
vector<int>::size_type i;
8
list<string>::const_iterator it;
list<string>::iterator cit;
9
begin 返回的迭代器类型是 Typename::iterator
cbegin 返回的迭代器类型是 Typename::const_iterator,不能通过这种迭代器来修改容器内元素
10
变量名 | 类型 |
---|---|
it1 | vector<int>::iterator |
it2 | vector<int>::const_iterator |
it3 | vector<int>::const_iterator |
it4 | vector<int>::const_iterator |
11
#include <iostream>
#include <vector>
using namespace std;
void display(const vector<int>& vec) {
for (auto num : vec) {
cout << num << " ";
}
cout << endl;
}
int main() {
// empty
vector<int> vec1;
// {0, 1, 2, 3, 4}
vector<int> vec2{0, 1, 2, 3, 4};
// {0, 1, 2, 3, 4}
vector<int> vec3(vec2);
// {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
vector<int> vec4(10);
// {1, 1, 1, 1, 1, 1, 1, 1, 1, 1}
vector<int> vec5(10, 1);
// {2, 3, 4}
vector<int> vec6(vec2.begin() + 2, vec2.end());
return 0;
}
12
对于接受一个容器创建拷贝的构造函数,容器类型必须完全相同。但是对于接受两个迭代器创建拷贝的构造函数,容器类型不一定完全相同,只要元素能转换为要初始化的容器的类型就可以。
13
#include <iostream>
#include <vector>
#include <list>
using namespace std;
int main() {
list<int> lst;
for (int i = 0; i < 10; ++i)
lst.emplace_back(i);
vector<int> vec{1, 2, 3};
vector<double> vec1(lst.begin(), lst.end());
vector<double> vec2(vec.begin(), vec.end());
return 0;
}
14
#include <iostream>
#include <string>
#include <vector>
#include <list>
using namespace std;
int main() {
list<char*> lst;
char *p = "12345";
char *q = "23456";
lst.emplace_back(p);
lst.emplace_back(q);
vector<string> vec;
vec.assign(lst.begin(), lst.end());
for (const auto& str : vec)
cout << str << endl;
return 0;
}
15
template<class T>
bool operator == (const vector<T>& vec1, const vector<T>& vec2) {
int size1 = vec1.size(), size2 = vec2.size();
if (size1 != size2)
return false;
for (int i = 0; i != size1; ++i) {
if (vec1[i] != vec2[i])
return false;
}
return true;
}
16
template<class T>
bool operator == (const vector<T>& vec, const list<T>& lst) {
int size1 = vec.size(), size2 = lst.size();
if (size1 != size2)
return false;
auto itv = vec.begin();
auto itl = lst.begin();
for (; itv != vec.end(); ++itv, ++itl) {
if (*itv != *itl)
return false;
}
return true;
}
template<class T>
bool operator == (const list<T>& lst, const vector<T>& vec) {
return vec == lst;
}
17
容器内元素的类型要支持 <
运算符。
18
#include <iostream>
#include <string>
#include <deque>
using namespace std;
int main() {
string buffer;
deque<string> words;
while (cin >> buffer) {
words.emplace_back(buffer);
}
for (deque<string>::const_iterator it = words.begin();
it != words.end(); ++it)
cout << *it << endl;
return 0;
}
19
#include <iostream>
#include <string>
#include <list>
using namespace std;
int main() {
string buffer;
// 修改类型
list<string> words;
while (cin >> buffer) {
words.emplace_back(buffer);
}
// 修改类型
for (list<string>::const_iterator it = words.begin();
it != words.end(); ++it)
cout << *it << endl;
return 0;
}
20
#include <iostream>
#include <deque>
#include <list>
using namespace std;
template<class T>
void displayDeque(const deque<T>& deq) {
for (const auto& num : deq) {
cout << num << " ";
}
cout << endl;
}
int main() {
list<int> source{1, -99, 0, 8, 678, 99999, 3, 0};
deque<int> even, odd;
for (const auto& num : source) {
if (num % 2) {
odd.emplace_back(num);
} else {
even.emplace_back(num);
}
}
displayDeque(even);
displayDeque(odd);
return 0;
}
21
循环可以正常运作,但是效率会很低。每次在 vector 头部插入的时间复杂度是 O(n),插入 n 个元素的时间复杂度是 O(n^2)
22
while 循环中 iter 的位置永远不变,可能会死循环。代码修改如下:
#include <iostream>
#include <vector>
using namespace std;
template<class T>
void displayVec(const vector<T>& deq) {
for (const auto& num : deq) {
cout << num << " ";
}
cout << endl;
}
int main() {
int some_val = -99;
vector<int> iv{1, -99, 0, 8, 678, 99999, 3, 0};
vector<int>::iterator iter = iv.begin(),
mid = iv.begin() + iv.size()/2;
while (iter != mid) {
if (*iter == some_val)
iter = iv.insert(iter, 2 * some_val) + 1;
++iter;
}
displayVec(iv);
return 0;
}
23
四个值相等,全部都是 c 中唯一元素的值。
24
#include <iostream>
#include <vector>
using namespace std;
int main() {
vector<int> evec;
// 报错std::out_of_range
cout << evec.at(0) << endl;
// 以下都会导致程序非正常退出
cout << evec[0] << endl;
cout << evec.front() << endl;
cout << *evec.begin() << endl;
return 0;
}
25
如果 elem1 和 elem2 相同,且 elem1 不是尾后迭代器,那么就只删除 elem1 指向的元素。
如果 elem2 是尾后迭代器,就删除 elem1 开始,包括 elem1 在内的所有元素。
如果 elem1 和 elem2 都是尾后迭代器,则行为未定义。
26
#include <iostream>
#include <vector>
#include <list>
using namespace std;
void displayVec(const vector<int>& vec) {
for (auto num : vec)
cout << num << " ";
cout << endl;
}
void displayLst(const list<int>& lst) {
for (auto num : lst)
cout << num << " ";
cout << endl;
}
int main() {
int ia[] = {0, 1, 1, 2, 3, 5, 8, 13, 21, 55, 89};
int size = sizeof(ia) / sizeof(ia[0]);
vector<int> vec(ia, ia + size);
list<int> lst(ia, ia + size);
vector<int>::iterator vit = vec.begin();
while (vit != vec.end()) {
if (!((*vit) % 2)) {
vit = vec.erase(vit);
} else {
++vit;
}
}
displayVec(vec);
list<int>::iterator lit = lst.begin();
while (lit != lst.end()) {
if ((*lit) % 2) {
lit = lst.erase(lit);
} else {
++lit;
}
}
displayLst(lst);
return 0;
}
27
#include <iostream>
#include <forward_list>
using namespace std;
void deleteOdd(forward_list<int>& lst) {
forward_list<int>::iterator prev = lst.before_begin();
forward_list<int>::iterator curr = lst.begin();
while (*curr) {
if ((*curr) % 2) {
curr = lst.erase_after(prev);
} else {
prev = curr;
++curr;
}
}
}
void displayFlist(const forward_list<int>& lst) {
for (auto num : lst) {
cout << num << " ";
}
cout << endl;
}
int main() {
forward_list<int> lst{1, -197, -2, 0, 3, 3357, 888, 89};
deleteOdd(lst);
displayFlist(lst);
return 0;
}
28
#include <iostream>
#include <string>
#include <forward_list>
using namespace std;
template<class T>
void insertAfter(forward_list<T>& lst, const T& target, const T& newStr) {
typename forward_list<T>::iterator prev = lst.before_begin();
typename forward_list<T>::iterator curr = lst.begin();
while (curr != lst.end()) {
if(*curr == target) {
lst.insert_after(curr, newStr);
return;
}
prev = curr;
++curr;
}
lst.insert_after(prev, newStr);
}
template<class T>
void displayFlist(const forward_list<T>& lst) {
for (auto ele : lst) {
cout << ele << " ";
}
cout << endl;
}
int main() {
forward_list<string> lst{"Apple", "Bee", "Dog"};
insertAfter(lst, string("Bee"), string("Cat"));
displayFlist(lst);
return 0;
}
29
将 vec 的大小变成 100,并自动在原来的 vec 后面添加 75 个值为 0 的元素。
将 vec 的大小变成 10,并删除后面的 90 个元素。
30
需要元素有默认构造函数,可能会在自动添加元素的时候被调用。
31
首先,list 和 forward_list 的迭代器不支持和整数类型相加,因为这两种容器的元素在内存中不是连续排列的;
其次。forward_list 容器对元素的插入和删除和一般的顺序容器不同,有自己的接口。
#include <iostream>
#include <vector>
#include <list>
#include <forward_list>
using namespace std;
void displayVec(const vector<int>& vec) {
for (auto num : vec)
cout << num << " ";
cout << endl;
}
void displayLst(const list<int>& lst) {
for (auto num : lst)
cout << num << " ";
cout << endl;
}
void displayFlst(const forward_list<int>& flst) {
for (auto num : flst)
cout << num << " ";
cout << endl;
}
int main() {
vector<int> vec{0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
auto itv = vec.begin();
while (itv != vec.end()) {
if (*itv % 2) {
itv = vec.insert(itv, *itv);
itv += 2;
} else {
itv = vec.erase(itv);
}
}
displayVec(vec);
list<int> lst{0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
auto itl = lst.begin();
while (itl != lst.end()) {
if (*itl % 2) {
itl = lst.insert(itl, *itl);
++itl;
++itl;
} else {
itl = lst.erase(itl);
}
}
displayLst(lst);
forward_list<int> flst{0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
auto prev = flst.before_begin();
auto curr = flst.begin();
while (curr != flst.end()) {
if (*curr % 2) {
flst.insert_after(curr, *curr);
++curr;
prev = curr;
++curr;
} else {
++curr;
flst.erase_after(prev);
}
}
displayFlst(flst);
return 0;
}
32
不合法。因为 insert 成员函数的参数中包含了 *iter++
,如果是从右往左计算实参并传参的话就会出错。
33
程序崩溃。begin 迭代器在容器插入元素之后就失效了。
34
#include <iostream>
#include <vector>
#include <list>
#include <forward_list>
using namespace std;
void displayVec(const vector<int>& vec) {
for (auto num : vec)
cout << num << " ";
cout << endl;
}
int main() {
vector<int> vec{0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
auto iter = vec.begin();
while (iter != vec.end()) {
if (*iter % 2) {
iter = vec.insert(iter, *iter);
iter += 2;
} else {
++iter;
}
}
displayVec(vec);
return 0;
}
35
capacity 是指 vector 目前在内存中最多可以使用的大小,一旦 vector 的实际大小大于这个值,就需要重新申请一块更大的连续内存。
size 就是目前 vector 实际的大小。
36
不可能。因为 vector 中所有元素在内存中连续排列,size 大于 capacity 的话放不下(此时 vector 就会申请更大的空间)。
37
因为 list 和 array 中的元素可以在内存中不连续地分布,理论上可以占满所有可用的内存。
38
#include <iostream>
#include <vector>
using namespace std;
void display(const vector<int>& vec) {
cout << "vec.size(): " << vec.size() << endl;
cout << "vec.capacity(): " << vec.capacity() << endl;
}
int main() {
vector<int> vec;
display(vec);
vec.push_back(1);
display(vec);
vec.push_back(1);
display(vec);
vec.push_back(1);
display(vec);
vec.insert(vec.end(), 10, 10);
display(vec);
vec.insert(vec.end(), 100, 10);
display(vec);
vec.insert(vec.end(), 911, 10);
display(vec);
vec.push_back(1);
display(vec);
return 0;
}
输出如下:
vec.size(): 0 vec.capacity(): 0
vec.size(): 1 vec.capacity(): 1
vec.size(): 2 vec.capacity(): 2
vec.size(): 3 vec.capacity(): 4
vec.size(): 13 vec.capacity(): 13
vec.size(): 113 vec.capacity(): 113
vec.size(): 1024 vec.capacity(): 1024
vec.size(): 1025 vec.capacity(): 2048
39
构造了一个空的 string 的 vector,svec
将 svec 的 capacity 变成 1024
不断从标准输入中读入 string,添加到 svec 的尾部
将 svec 的大小变为原来的 1.5 倍,多出来的位置用空 string 填充
40
读入 256:resize 之后的 capacity 是 1024
读入 512:resize 之后的 capacity 是 1024
读入 1000:resize 之后的 capacity 大于等于 1500,可能是 1500 或者 2048
读入 1048:resize 之后的 capacity 大于等于 1572,可能是 1572 或者 2048
41
#include <iostream>
#include <vector>
#include <string>
using namespace std;
int main() {
vector<char> vec{'h', 'e', 'l', 'l', 'o'};
string s(vec.data(), vec.size());
cout << s << endl;
return 0;
}
42
string s;
s.reserve(100);
43
TDM-GCC 4.9.2 的 string::insert 函数不返回迭代器,于是只好暂存 p 的位置,插入结束后再自行复位。
搜了一下,StackOverflow 上的解释:GCC 4.9 中关于 string 的实现不遵循 C++11,这只是其中一例。
#include <iostream>
#include <string>
using namespace std;
void replaceStr(string& s, const string& oldVal, const string& newVal) {
int sizeO = oldVal.size(), sizeN = newVal.size();
string::iterator p = s.begin();
while (p != s.end()) {
if (s.substr(p - s.begin(), sizeO) == oldVal) {
string::difference_type diff = distance(s.begin(), p);
p = s.erase(p, p + sizeO);
s.insert(p, newVal.cbegin(), newVal.cend());
p = s.begin() + diff + sizeN;
} else {
++p;
}
}
}
int main() {
string str("sdfsdfthoksdh tho skdthththhftho");
replaceStr(str, "tho", "though");
cout << str << endl;
return 0;
}
44
#include <iostream>
#include <string>
using namespace std;
void replaceStr(string& s, const string& oldVal, const string& newVal) {
int sizeO = oldVal.size(), sizeN = newVal.size();
auto p = s.begin();
while (p != s.end()) {
if (s.substr(p - s.begin(), sizeO) == oldVal) {
string::size_type tempPos = p - s.begin();
s.replace(p, p + sizeO, newVal.begin(), newVal.end());
p = s.begin() + tempPos + sizeN;
} else {
++p;
}
}
}
int main() {
string str("kasdhfj tho th sdfadftho");
replaceStr(str, "tho", "though");
cout << str << endl;
return 0;
}
45
#include <iostream>
#include <string>
using namespace std;
string addPrefixSuffix(const string& name, const string& prefix, const string& suffix) {
string ret = name;
ret.insert(ret.begin(), prefix.begin(), prefix.end());
ret.append(suffix);
return ret;
}
int main() {
string name = addPrefixSuffix("Alex", "Mr.", "III");
cout << name << endl;
return 0;
}
46
#include <iostream>
#include <string>
using namespace std;
string addPrefixSuffix(const string& name, const string& prefix, const string& suffix) {
string ret = name;
ret.insert(0, prefix);
ret.insert(ret.size(), suffix);
return ret;
}
int main() {
string name = addPrefixSuffix("Alex", "Mr.", "III");
cout << name << endl;
return 0;
}
47
#include <iostream>
#include <string>
using namespace std;
void displayNumber(const string& str) {
const string number("0123456789");
string::size_type pos = 0;
while ((pos = str.find_first_of(number, pos)) != string::npos) {
cout << "found number at index: " << pos << endl
<< "element is: " << str[pos] << endl;
++pos;
}
}
void displayAlpha(const string& str) {
const string number("0123456789");
string::size_type pos = 0;
while ((pos = str.find_first_not_of(number, pos)) != string::npos) {
cout << "found alphabet at index: " << pos << endl
<< "element is: " << str[pos] << endl;
++pos;
}
}
int main() {
const string str("ab2c3d7R4E6");
displayNumber(str);
displayAlpha(str);
return 0;
}
48
string::npos
49
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
int main() {
const string ascAndDesc("qtyipdfghjklb");
ifstream input("words.d");
string buffer, ans;
while (input >> buffer) {
if (buffer.find_first_of(ascAndDesc) == string::npos) {
if (buffer.size() > ans.size()) {
ans = buffer;
}
}
}
cout << ans;
return 0;
}
50
#include <iostream>
#include <vector>
#include <string>
using namespace std;
int sumVecInt(const vector<string>& vecInt) {
int sum = 0;
for (const auto& str : vecInt) {
sum += stoi(str);
}
return sum;
}
double sumVecDbl(const vector<string>& vecDbl) {
double sum = 0;
for (const auto& str : vecDbl) {
sum += stod(str);
}
return sum;
}
int main() {
vector<string> vecInt{"1", "2", "4", "8", "16"};
cout << sumVecInt(vecInt) << endl;
vector<string> vecDbl{"0.01", "0.02", "0.04", "0.08", "0.16"};
cout << sumVecDbl(vecDbl) << endl;
return 0;
}
51
#include <iostream>
#include <sstream>
#include <string>
#include <cctype>
#include <vector>
#include <unordered_map>
using namespace std;
class Date {
public:
Date() = default;
Date(const string& date) {
if (date.empty())
return;
string::const_iterator p = date.begin();
while (delimiters.find(*p) == string::npos)
++p;
string monthStr(date.substr(0, p - date.begin()));
// cout << monthStr << endl;
if (monthStr.empty()) {
cerr << "invalid input format." << endl;
return;
} else if (isdigit(monthStr[0])) {
month = stoi(monthStr);
} else {
for (auto& ch : monthStr)
ch = tolower(ch);
if (stringToMonth.find(monthStr) != stringToMonth.end()){
month = stringToMonth[monthStr];
} else {
cerr << "invalid input format." << endl;
return;
}
}
while (delimiters.find(*p) != string::npos)
++p;
string::size_type dayBegin = distance(date.begin(), p);
while (delimiters.find(*p) == string::npos)
++p;
day = stoi(date.substr(dayBegin, distance(date.begin(), p)));
while (delimiters.find(*p) != string::npos)
++p;
string::size_type yearBegin = distance(date.begin(), p);
while (delimiters.find(*p) == string::npos)
++p;
year = stoi(date.substr(yearBegin, distance(date.begin(), p)));
}
void display() {
cout << "year: " << year << " "
<< "month: " << month << " "
<< "day: " << day << endl;
}
private:
unsigned year;
unsigned month;
unsigned day;
// delimiters of the input string
const string delimiters = " ,/";
unordered_map<string, int> stringToMonth{
{"january", 1},
{"february", 2},
{"march", 3},
{"april", 4},
{"may", 5},
{"june", 6},
{"july", 7},
{"august", 8},
{"september", 9},
{"october", 10},
{"november", 11},
{"december", 12},
{"jan", 1},
{"feb", 2},
{"mar", 3},
{"apr", 4},
// {"may", 5}, // same as above
{"jun", 6},
{"jul", 7},
{"aug", 8},
{"sep", 9},
{"oct", 10},
{"nov", 11},
{"dec", 12},
};
};
int main() {
Date date1("January 1,1900");
date1.display();
Date date2("1/1/1900");
date2.display();
Date date3("Jan 1 1900");
date3.display();
return 0;
}
52
#include <iostream>
#include <sstream>
#include <string>
#include <cctype>
#include <vector>
#include <stack>
#include <unordered_map>
using namespace std;
unordered_map<string, int> stringToPriority{
{"(", -1},
{"+", 0}, {"-", 0},
{"*", 1}, {"/", 1},
};
string infixToPostfix(const string& infix) {
string postfix, buffer;
istringstream input(infix);
stack<string, vector<string> > ops;
while (input >> buffer) {
if (isdigit(buffer[0]) || (buffer[0] == '-' && buffer.size() > 1)) {
postfix.append(buffer + " ");
} else if (buffer == "(") {
ops.push(buffer);
} else if (buffer == ")") {
while (!ops.empty() && ops.top() != "(") {
postfix.append(ops.top() + " ");
ops.pop();
}
ops.pop();
} else {
while (!ops.empty() && stringToPriority[ops.top()]
>= stringToPriority[buffer]) {
postfix.append(ops.top() + " ");
ops.pop();
}
ops.push(buffer);
}
}
while (!ops.empty()) {
postfix.append(ops.top() + " ");
ops.pop();
}
return postfix;
}
int calculatePostfix(const string& postfix) {
stack<int, vector<int> > nums;
istringstream input(postfix);
string buffer;
while (input >> buffer) {
if (isdigit(buffer[0]) || (buffer[0] == '-' && buffer.size() > 1)) {
nums.push(stoi(buffer));
} else {
int b = nums.top();
nums.pop();
int a = nums.top();
nums.pop();
switch(buffer[0]) {
case '+':
nums.push(a + b);
break;
case '-':
nums.push(a - b);
break;
case '*':
nums.push(a * b);
break;
case '/':
nums.push(a / b);
break;
}
}
}
return nums.top();
}
int main() {
// suppose that the numbers and symbols
// in the input string are separated by space
string infix("( ( 10 * ( 6 / ( ( 9 + 3 ) * -11 ) ) ) + 17 ) + 5");
string postfix = infixToPostfix(infix);
cout << postfix << endl;
cout << calculatePostfix(postfix) << endl;
return 0;
}