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 zippy2.
#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模擬,允許使用者輸入三個數:佇列的最大長度、程式模擬的持續時間(單位為小時)和平均每小時的客戶數。程式使用迴圈,每次迴圈代表一分鐘。在每分鐘的迴圈中,程式將完成下面的工作:
- 判斷是否來了新客戶。如果來了,且此時佇列未滿,則將它新增到佇列中,否則拒絕客戶入隊;
- 如果沒有客戶在進行交易,則選取佇列的第一個客戶。確定該客戶的已等候時間,並將waitTime計數器設定為新客戶所需的處理時間。
- 如果客戶正在處理中, 則將waitTime計數器減1.
- 記錄各種資料,如獲得服務的客戶數目、被拒絕的客戶數目、排隊等候的累積時間以及累積的佇列長度等。
當模擬迴圈結束時, 程式將報告各種統計結果。
解答:
為了更多地利用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完成下列目的
- 建立大型vector<int>物件vi0, 並使用rand()給它賦初值;
- 建立vector<int>物件vi和list<int>物件li, 它們的長度和初始值與vi0相同;
- 計算使用STL演算法sort()對vi進行排序所需的時間,再計算使用list的方法sor()對li進行排序所需的時間。
- 將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>>儲存書籍資訊)
- 按原始順序顯示
- 按字母表順序顯示
- 按評級升序顯示
- 按評級降序顯示
- 按價格升序顯示
- 按價格降序顯示
- 退出
假設書單內容為如下:
- Harry Potter and the Goblet of Fire 9 ¥68.5
- To Kill a Mockingbird 7 ¥78
- Pride and Prejudice 8 ¥70.50
- Fifty Shades of Grey 5 ¥71
- 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),而寫“>”會報錯。