1. 程式人生 > >C++學習筆記——C++ Primer Plus中文第六版 第十六章STL程式設計練習解答

C++學習筆記——C++ Primer Plus中文第六版 第十六章STL程式設計練習解答


發現答案資源不全,因此貼出自己的解答,都為STL應用基礎題,如有謬誤,還請不吝賜教。


第一題

要求:迴文字串判斷(假定字串中沒有大小寫、空格、標點符號等問題)

解答:

#include<iostream>
#include<string>
#include<ctype>
#include<algorithm>

int main(void) {
	using namespace std;
	string input;
	bool isPal(string);
	string tmp = "";
	cout << "Enter a string(empty string to quit):";
	while (getline(cin,input) && input.size() > 0) {
		if (isPal(tmp))
			cout << "It's a palindrome." << endl;
		else {
			cout << "It isn't a palindrome" << endl;
		}
		cout << "Enter a string(empty string to quit):";
	}
}

bool isPal(std::string s) {
	std::string r(s.rbegin(), s.rend());
	if (r == s)
		return true;
	else
		return false;
}

 

第二題

要求:第一題拓展,加上輸入字串中有大小寫、空格和標點符號的情況,程式需要將這些忽略掉

解答:

#include<iostream>
#include<string>
#include<ctype>
#include<algorithm>

int main(void) {
	using namespace std;
	string input;
	bool isPal(string);
	string tmp = "";
	cout << "Enter a string(empty string to quit):";
	while (getline(cin,input) && input.size() > 0) {
		for (auto e : input) {
			if (isalpha(e))
				tmp.push_back(tolower(e));
		}
		

		if (isPal(tmp))
			cout << "It's a palindrome." << endl;
		else {
			cout << "It isn't a palindrome" << endl;
		}
		cout << "Enter a string(empty string to quit):";
	}
}

bool isPal(std::string s) {
	std::string r(s.rbegin(), s.rend());
	if (r == s)
		return true;
	else
		return false;
}

 

 

第三題

要求:從檔案中讀取單詞,利用vector儲存並輸出到螢幕上,並列印個數

解答:

1.在.cpp檔案目錄下新建text檔案“data.txt”,開啟編輯並儲存如下內容:

apiary beetle cereal danger ensign florid garage health insult jackal keeper loaner manage nonce onset
plaid quilt remote stolid train useful valid whence xenon yearn zippy

2.

#include<iostream>
#include<iterator>
#include<fstream>
#include<string>
#include<vector>

int main() {
	using namespace std;

	ifstream fin("data.txt");
	string input;
	vector<string>v1;

	while (fin >> input) {
		v1.push_back(input);
	}

	copy(v1.begin(), v1.end(), ostream_iterator<string, char>(cout, " "));//列印單詞
	cout << endl << "共計" << v1.size() << "個單詞";

	fin.close();

	cout << endl;
	system("pause");
	return 0;
}

 

 

第四題

要求:編寫一個具有老式風格的介面函式,原型如下

              int reduce(long ar[], int n);

          實參應是陣列名和陣列元素個數,該函式對陣列進行排序,刪除重複值,返回縮減後陣列中的元素數目。用STL函式編寫。

解答:

#include<iostream>
#include<list>
#include<iterator>

int main() {
	using namespace std;

	int reduce(long[], int);

	long arr[] = { 1,4500,3120, 2791, 3200, 14, 560, 14, 3120, 3200, 3200, 3200 };//測試陣列

	cout << "呼叫reduce前,陣列元素依次如下" << endl;
	for (long e : arr) {
		cout << e << " ";
	}
	int count = sizeof(arr) / sizeof(long);
	cout << endl << "共有" << count << "個元素" << endl;
	
	count = reduce(arr, count);

	cout << "呼叫reduce後,陣列元素依次如下" << endl;
	for (int i = 0; i < count; i++) {
		cout << arr[i] << " ";
	}
	cout << endl << "共有" << count << "個元素" << endl;

	cout << endl;
	system("pause");
	return 0;
}

int reduce(long arr[], int n) {
	std::list<long> myList;
	std::copy(arr, arr + n, std::back_insert_iterator<std::list<long>>(myList));
	myList.sort();
	myList.unique();
	int i = 0;
	for (long e : myList) {
		arr[i++] = e;
	}
	return myList.size();
}

 

第五題

要求:與第四題相同,但要編寫的是模板函式,並用long例項和string例項測試

解答:

#include<iostream>
#include<list>
#include<iterator>
#include<string>

template<typename T>
void out(T arr[],int n);//因為long和string物件均可用cout列印,因此這裡簡化程式碼也用了模板

template <typename T>
int reduce(T arr[], int n);

using namespace std;

int main() {

	long arr[] = { 1,4500,3120, 2791, 3200, 14, 560, 14, 3120, 3200, 3200, 3200 };//測試陣列
	string arr2[] = { "haha", "heihei", "hehe", "haha", "haha", "heihei" };

	out(arr,sizeof(arr)/sizeof(long));
	out(arr2, sizeof(arr2) / sizeof(string));


	cout << endl;
	system("pause");
	return 0;
}

template<typename T>
void out(T arr[],int n) {
	cout << "呼叫reduce前,陣列元素依次如下" << endl;
	int count = n;
	for (int i = 0; i < count; i++) {
		cout << arr[i] << " ";
	}
	cout << endl << "共有" << count << "個元素" << endl;

	count = reduce(arr, count);


	cout << "呼叫reduce後,陣列元素依次如下" << endl;
	for (int i = 0; i < count; i++) {
		cout << arr[i] << " ";
	}
	cout << endl << "共有" << count << "個元素" << endl;
}

template <typename T>
int reduce(T arr[], int n) {
	std::list<T> myList;
	std::copy(arr, arr + n, std::back_insert_iterator<std::list<T>>(myList));
	myList.sort();
	myList.unique();
	int i = 0;
	for (T e : myList) {
		arr[i++] = e;
	}
	return myList.size();
}

 

第六題

要求:用STL模板類queue實現ATM模擬,允許使用者輸入三個數:佇列的最大長度、程式模擬的持續時間(單位為小時)和平均每小時的客戶數。程式使用迴圈,每次迴圈代表一分鐘。在每分鐘的迴圈中,程式將完成下面的工作:

  1.        判斷是否來了新客戶。如果來了,且此時佇列未滿,則將它新增到佇列中,否則拒絕客戶入隊;
  2.        如果沒有客戶在進行交易,則選取佇列的第一個客戶。確定該客戶的已等候時間,並將waitTime計數器設定為新客戶所需的處理時間。
  3.        如果客戶正在處理中, 則將waitTime計數器減1.
  4.        記錄各種資料,如獲得服務的客戶數目、被拒絕的客戶數目、排隊等候的累積時間以及累積的佇列長度等。

     當模擬迴圈結束時, 程式將報告各種統計結果。

解答:

為了更多地利用STL,並完成一個稍完整的ATM小系統,這裡把本題複雜化,寫得比較多

//---ATM.h
#pragma once
#include<ctime>
#include<queue>
#include<vector>
using namespace std;

//儘管這裡其實沒必要資料封裝以及繼承,但為了迎合OOP思想,還是寫個類吧

class ATM {
private:
	int maxLength;
	time_t startTime;
	double duration;
	int averageClientNum;
	queue<time_t> line;
	vector<time_t> comeTime;
	int currentLeftTime = 0;
	double totalWaitingTime = 0;
	int totalLineLength = 0;
	int currentClientNum = 0;
	int rejectClientNum = 0;

	void circle();

	void createComeTime();
	bool isCome();
	bool isFull();
	void enter();
	void reject();

	void deal();
	int getDealTime();

	time_t getWaitingTime(time_t before);

	int getCurrentClientNum();
	int getRejectClientNum();
	double getTotalWaitingTime();
	int getTotalLineLength();

	void over();
	

public:
	ATM(int maxL = 3, double dur = 24, int aver = 40) :maxLength(maxL), duration(dur), averageClientNum(aver) {};
	void work();
	
};

//--ATP.cpp
#include "ATM.h"
#include<iostream>
#include<ctime>
#include<queue>
#include<vector>
#include<Windows.h>

using namespace std;

#define MAXDEALTIME 5

void ATM::over()
{
	cout << "------------------" << endl;
	cout << "當前統計結果如下" << endl;
	cout << "------------------" << endl;
	cout << "獲得服務的客戶數目:" << getCurrentClientNum() << endl;
	cout << "被拒絕的的客戶數目:" << getRejectClientNum() << endl;
	cout << "排隊等候的累積時間:" << getTotalWaitingTime() << endl;
	cout << "累積的佇列長度:" << getTotalLineLength() << endl;
}

void ATM::work()
{
	//這方便測試,假設一小時是一分鐘;一分鐘是一秒;
	startTime = time(0);
	double minutes = 60 * duration;
	createComeTime();
	cout << "ATM開始執行" << endl;
	while (static_cast<double>(time(0) - startTime) <= minutes) {
		Sleep(1000);
		circle();
	}
	cout << "時間到,ATM執行結束" << endl;
}

void ATM::circle()
{
	cout << "---------------------------------------------------------------------" << endl;
	if (isCome()) {
		if (isFull()) {
			cout << "當前佇列已滿" << endl;
			reject();
		}
		else {
			enter();
			if (currentLeftTime == 0) {
				deal();
				currentLeftTime = getDealTime();
			}
		}
	}
	else {
		cout << "沒有客戶前來" << endl;
	}
	if (currentLeftTime > 0) {
		currentLeftTime--;
		cout << "當前客戶交易剩餘時間為" << currentLeftTime << endl;
		if(currentLeftTime == 0)
			cout << "當前客戶交易結束" << endl;
	}
	over();
	cout << "本輪迴圈結束" << endl;
	cout << "---------------------------------------------------------------------" << endl;
}

void ATM::createComeTime()
{
	srand(time(NULL));
	int totalClientNum = duration * averageClientNum;
	time_t cometime;
	for (int i = 0; i < totalClientNum; i++) {
		cometime = (rand() % static_cast<long>(60 * duration-1)) + startTime+1;
		comeTime.push_back(cometime);
	}
	sort(comeTime.begin(), comeTime.end());
}

bool ATM::isCome()
{
	time_t currentTime = time(NULL);
	if (comeTime.size() > 0 && comeTime.front() <= currentTime)
		return true;
	return false;
}

bool ATM::isFull()
{
	if (line.size() >= maxLength)
		return true;
	return false;
}

void ATM::enter()
{

		line.push(comeTime.front());
		double waitingTime =static_cast<double>(getWaitingTime(comeTime.front()));
		totalWaitingTime += waitingTime;
		cout << "新客戶已入隊,他已等候時間為" << waitingTime << endl;
		totalLineLength++;

}

void ATM::reject()
{
	cout << "入隊請求被拒" << endl;
	comeTime.erase(comeTime.begin());
	rejectClientNum++;
}

void ATM::deal()
{
	totalWaitingTime += static_cast<double>(time(NULL) - line.front());
	line.pop();
	cout << "當前客戶交易開始" << endl;
	comeTime.erase(comeTime.begin());
	currentClientNum++;
}

int ATM::getDealTime()
{
	return rand() % MAXDEALTIME+1;
}

time_t ATM::getWaitingTime(time_t before)
{
	return time(NULL) - before;
}

int ATM::getCurrentClientNum()
{
	return currentClientNum;
}

int ATM::getRejectClientNum()
{
	return rejectClientNum;
}

double ATM::getTotalWaitingTime()
{
	return totalWaitingTime;
}

int ATM::getTotalLineLength()
{
	return totalLineLength;
}

 

//--main.cpp
#include"ATM.h"


int main(void) {
	using namespace std;

	int maxLength, averageClientNum;
	double duration;

	cout << "請輸入佇列最大長度:";
	cin >> maxLength;
	cout << "請輸入程式模擬的持續時間(單位為小時):";
	cin >> duration;
	cout << "請輸入平均每小時的客戶數:";
	cin >> averageClientNum;
	ATM atm1(maxLength, duration, averageClientNum);
	atm1.work();

	cout << endl;
	system("pause");
	return 0;
}

 

 

第七題

   使用了random_shuffle,用法陳舊因此棄掉

 

第八題

題目:

     Mat和Pat希望邀請他們的朋友來參加派對。他們分別輸入自己的朋友姓名列表,然後將兩人的朋友姓名列表合併,得到不重複的被邀請者名單。

解答:

分析後可知,採用STLset容器極其相關演算法最為簡單,也就是求並集的過程,但注意set_union同copy一樣,最後的一個引數要求輸出迭代器且被輸出容器容量足夠,不能用C.begin(),因此採用insert_iterator構造輸出迭代器

#include<iostream>
#include<iterator>
#include<set>
#include<vector>
#include<algorithm>
#include<string>

int main(void) {
	using namespace std;

	void input(string, set<string>&);
	
	set<string>nameList1, nameList2, nameList3;
	
	input("Mat", nameList1);
	input("Pat", nameList2);
	set_union(nameList1.begin(), nameList1.end(), nameList2.begin(), nameList2.end(), insert_iterator<set<string>>(nameList3, nameList3.begin()));
	cout << "最終得出的邀請名單如下:" << endl;
	for (string e : nameList3) {
		cout << e << endl;
	}

	cout << endl;
	system("pause");
	return 0;
}

void input(std::string name, std::set<std::string>& s) {
	std::cout << "請輸入" << name << "的朋友名單(以空格分隔,回車結束):";
	std::string tmp;
	std::vector<std::string> v;
	while (std::cin >> tmp) {
		v.push_back(tmp);
		if (std::cin.get() == '\n')
			break;
	}
	std::copy(v.begin(), v.end(), std::insert_iterator<std::set<std::string>>(s,s.begin()));
}

 

 

第九題

要求:利用ctime中的clock()和STL完成下列目的

  1.  建立大型vector<int>物件vi0, 並使用rand()給它賦初值;
  2.  建立vector<int>物件vi和list<int>物件li, 它們的長度和初始值與vi0相同;
  3.  計算使用STL演算法sort()對vi進行排序所需的時間,再計算使用list的方法sor()對li進行排序所需的時間。
  4. 將li重置為排序的vi0內容,並計算執行如下操作所需的時間:將li的內容複製到vi中,對vi進行排序,並將結果複製到li中。

解答:

#include<iostream>
#include<vector>
#include<algorithm>
#include<ctime>
#include<random>
#include<list>
#include<iterator>

using namespace std;

clock_t getTime(list<int>&li);
clock_t getTime(vector<int>&vi);
clock_t getTime(list<int>&li, vector<int>&vi);
void copy2(list<int>& li, vector<int>&vi);

int main(void) {

	int getRandom(int e);
	vector<int>vi0(1000);
	vector<int>vi;
	list<int>li;
	random_device dev;
	uniform_int_distribution<int> dist(-100, 100);

	for (auto &e : vi0) {
		e = dist(dev);
	}
	copy(vi0.begin(), vi0.end(), insert_iterator<vector<int>>(vi, vi.begin()));
	copy(vi0.begin(), vi0.end(), insert_iterator<list<int>>(li, li.begin()));
	cout << "初始化完成" << endl;

	cout << "使用sort()對vi進行排序所需的時間為:" << getTime(vi) << endl;
	cout << "使用li.sort()對li進行排序所需的時間為:" << getTime(li) << endl;
	cout << "先將li拷貝到vi在對vi使用STL sort()排序後再拷貝會li所需的時間為:" << getTime(li,vi) << endl;

	cout << endl;
	system("pause");
	return 0;
}


clock_t getTime(list<int>&li) {
	clock_t start = clock();
	li.sort();
	return clock() - start;
}

clock_t getTime(vector<int>&vi) {
	clock_t start = clock();
	sort(vi.begin(),vi.end());
	return clock() - start;
}


clock_t getTime(list<int>&li, vector<int>&vi){
		clock_t start = clock();
		copy2(li, vi);
		return clock() - start;
}



void copy2(list<int> &li, vector<int> &vi) {
	vi.clear();
	for (int e : li) {
		vi.push_back(e);
	}
	sort(vi.begin(), vi.end());
	li.clear();
	for (int e : vi) {
		li.push_back(e);
	}
}

 

 

第十題

原題沒意思,這裡我加以改編題目拓展了一下。

要求:實現一個書單相關功能,使其能夠實現如下功能(使用vector<shared_ptr<Review>>儲存書籍資訊)

  1. 按原始順序顯示
  2. 按字母表順序顯示
  3. 按評級升序顯示
  4. 按評級降序顯示
  5. 按價格升序顯示
  6. 按價格降序顯示
  7. 退出

假設書單內容為如下:

  1.   Harry Potter and the Goblet of Fire    9     ¥68.5
  2.   To Kill a Mockingbird      7     ¥78
  3.   Pride and Prejudice        8     ¥70.50
  4.   Fifty Shades of Grey     5  ¥71
  5.   Great Expectations     7   ¥ 45

解答:

#include<iostream>
#include<vector>
#include<memory>
#include<string>
#include<algorithm>
#include<iterator>

using namespace std;
typedef struct Review {
	string name;
	int rate;
	double price;
}Review;

class BookList {
private:
	vector<shared_ptr<Review>> books;

	static bool cmpByAlpha(shared_ptr<Review> first, shared_ptr<Review> second);
	static bool cmpByRate(shared_ptr<Review> first, shared_ptr<Review> second);
	static bool cmpByPrice(shared_ptr<Review> first, shared_ptr<Review> second);
	void out(vector<shared_ptr<Review>> b = {});
public:
	BookList(vector<shared_ptr<Review>> &arr);
	void outByInit();
	void outByAlpha(bool sequence = true);
	void outByRate(bool sequence = true);
	void outByPrice(bool sequence = true);
};

bool BookList::cmpByAlpha(shared_ptr<Review> first, shared_ptr<Review> second)
{
	if(first->name >= second->name)
		return false;
	return true;
}

bool BookList::cmpByRate(shared_ptr<Review> first, shared_ptr<Review> second)
{
	if (first->rate >= second->rate)
		return false;
	else
		return true;
}

bool BookList::cmpByPrice(shared_ptr<Review> first, shared_ptr<Review> second)
{
	if (first->price >= second->price)
		return false;
	else
		return true;
}

void BookList::out(vector<shared_ptr<Review>> b)
{
	if (b.size() == 0)
		b = books;
	for (auto e : b) {
		cout << "-----------------------------" << endl;
		cout << "name: " << e->name << endl;
		cout << "rate: " << e->rate << endl;
		cout << "price: ¥" << e->price << endl;
		cout << "-----------------------------" << endl << endl;
	}
}

BookList::BookList(vector<shared_ptr<Review>> &arr)
{
	for (auto e : arr) {
		books.push_back(e);
	}
}

void BookList::outByInit()
{
	cout << "按原序輸出如下" << endl;
	out();
}

void BookList::outByAlpha(bool sequence)
{
	
	vector<shared_ptr<Review>> books2;
	copy(books.begin(), books.end(), insert_iterator<vector<shared_ptr<Review>>>(books2, books2.begin()));
	if (sequence) {
		cout << "按字母升序輸出如下" << endl;
		sort(books2.begin(), books2.end(), BookList::cmpByAlpha);
	}
	else {
		cout << "按字母降序輸出如下" << endl;
		sort(books2.rbegin(), books2.rend(), BookList::cmpByAlpha);
	}
	out(books2);
}

void BookList::outByRate(bool sequence)
{
	vector<shared_ptr<Review>> books2;
	copy(books.begin(), books.end(), insert_iterator<vector<shared_ptr<Review>>>(books2, books2.begin()));
	if (sequence) {
		cout << "按評級升序輸出如下" << endl;
		sort(books2.begin(), books2.end(), BookList::cmpByRate);
	}
	else {
		cout << "按評級降序輸出如下" << endl;
		sort(books2.rbegin(), books2.rend(), BookList::cmpByRate);
	}
	out(books2);
}

void BookList::outByPrice(bool sequence)
{
	vector<shared_ptr<Review>> books2;
	copy(books.begin(), books.end(), insert_iterator<vector<shared_ptr<Review>>>(books2, books2.begin()));
	if (sequence) {
		cout << "按價格升序輸出如下" << endl;
		sort(books2.begin(), books2.end(), cmpByPrice);
	}
	else {
		cout << "按價格降序輸出如下" << endl;
		sort(books2.rbegin(), books2.rend(), cmpByPrice);
	}
	out(books2);
}


int main(void) {

	int showMenu();

	vector<shared_ptr<Review>> arr = { 
		shared_ptr<Review>(new Review{"Harry Potter and the Goblet of Fire",9,68.5}),
		shared_ptr<Review>(new Review{"To Kill a Mockingbird", 7, 70.50}),
		shared_ptr<Review>(new Review{"Pride and Prejudice", 8, 70.50}),
		shared_ptr<Review>(new Review{" Fifty Shades of Grey", 5, 71.0}),
		shared_ptr<Review>(new Review{"Great Expectations", 7, 45.0}),
	};
	BookList list1(arr);

	int option;
	while ((option = showMenu()) != 0) {
		switch (option)
		{
		default:
			continue; break;
		case 1:
			list1.outByInit(); break;
		case 2:
			list1.outByAlpha(); break;
		case 3:
			list1.outByAlpha(false); break;
		case 4:
			list1.outByRate(); break;
		case 5:
			list1.outByRate(false); break;
		case 6:
			list1.outByPrice(); break;
		case 7:
			list1.outByPrice(false); break;
		}
		system("pause");
		system("cls");
	}

	cout << endl;
	system("pause");
	return 0;
}

int showMenu() {
	cout << "本程式是一個書單列印程式,請輸入整數0-7選擇列印方式" << endl;
	cout << "1.按原序列印;" << endl;
	cout << "2.按字母升序列印;" << endl;
	cout << "3.按字母降序列印;" << endl;
	cout << "4.按評級升序列印;" << endl;
	cout << "5.按評級降序列印;" << endl;
	cout << "6.按價格升序列印;" << endl;
	cout << "7.按價格降序列印;" << endl;
	cout << "0.退出;" << endl;
	int input;
	cin >> input;
	return input;
}

這裡尤其注意一個點,自定義的謂詞函式不能是穩定的,因為sort()非穩定演算法。所以應是當">="時返回順序錯誤(false),而寫“>”會報錯。