C++ STL練習(1)
說明:題目節選自《演算法競賽入門經典》(第二版),僅為了練習使用,跟著書中手敲程式碼,並新增自己的理解,僅此記錄,我始終相信,當你開始不懂許多東西的時候,跟著書中弄懂作者的寫的程式碼的思路,照著敲出來,敲多了,明白許多原理了,還有你會為了看懂它,查很多資料,然後當自己面對新的問題,腦子就有的呼叫了
備註:1.個人更喜歡C++,對於作者書中的沿用了C語言的部分,我會改成C++實現,如果涉及到效率問題,請參考原書
2.我會採用自己的命名習慣,以及添加註釋,原書中的程式碼是針對競賽的,我寫程式碼是針對實際工程的,工程中變數最好能表明它的含義,以及必要的註釋,方便其他人閱讀,這也是習慣問題。
題目1:大理石在哪兒
思路:
1.接受使用者多個輸入,儲存下來
2.對儲存的資料排序
3.在已排序的陣列中查詢
程式碼實現:
#include <iostream> #include <algorithm> #include <array> using namespace std; const int arrSize = 10000; int main() { //變數最好在宣告時進行初始化 int marbleNum = 0; //大理石數量,即非負整數的數量 int questionNum = 0; //問題數量 int integerOfQuestion = 0; //問題中所問的整數 int ncase = 0; //第n波輸入 //array物件會進行預設初始化嗎? array<int,arrSize> saveArray; //儲存非負整數的陣列 while((cin>>marbleNum>>questionNum) && marbleNum !=0) //對while迴圈中cin解釋: //要保證有大理石,不然迴圈內的執行沒有意義 { cout<<"CASE# "<<++ncase<<endl; //輸入n個"大理石非負整數“ for(int i=0;i < marbleNum;i++) cin>>saveArray[i]; //對輸入的”大理石整數“進行排序 //sort的用法總結,以及這裡的迭代器解釋見程式碼後解釋 sort(saveArray.begin(),saveArray.begin()+marbleNum); //回答Q個問題 while(questionNum--) { //輸入問題中要問的整數 cin>>integerOfQuestion; //在陣列中查詢等於integerOfQuestion //為什麼最後要減去saveArray.begin()? int intLocation = lower_bound(saveArray.begin(),saveArray.begin()+marbleNum,integerOfQuestion)-saveArray.begin(); //此時我們需要判斷是否找到的是等於integerOfQuestion的數, //因為lower_bound函式返回的是第一個大於或等於integerOfQuestion的位置,詳細見下面解釋 if(saveArray[intLocation] == integerOfQuestion) cout<<integerOfQuestion<<" found at "<< intLocation+1<<endl; //題目中要求返回的是從1開始的位置,所以+1 else cout<<integerOfQuestion<<" not found"<<endl; } } }
eclipse下執行截圖:
知識點詳細用法補充:待補充
1.while下的cin
2.sort()函式和對應迭代器的使用
3.low_bound()函式
題目2:木塊問題
且原題假設輸入中,0<n<25
思路:
實現四個操作即可
1.可以看到該四個操作有公共的部分,我們可以提取出來寫成函式,達到複用的效果
可以看到四個操作有重複的部分:歸位木塊,移動一個木塊到另一個木塊堆的頂部,移動一摞木塊到另一個木塊所在木塊堆的頂部上,雖然歸位本質也是移動,由於移動函式包括移動本身,而歸位函式不包括移動本身,所以需要分開寫
(1)歸位函式:把某個木塊上方的木塊全部歸位,輸入:要歸位的
(2)移動一個木塊:輸入:要移動的木塊 輸出:目標木塊
(3)移動一摞木塊:輸入:要移動的一摞木塊的起始木塊 輸出:目標木塊
2.封裝四個操作
3.在主函式裡獲取使用者輸入,用case選擇相應操作
4.最後列印每個位置的木塊列表
程式碼實現:
#include <iostream>
#include <vector>
#include <string>
using namespace std;
//函式宣告
void back_above(int numOfBlock);
void moveOneBlock(int numMoveBlock,int numTargetBlock);
int findPile(int numOfBlock);
void moveBlocks(int beginMoveBlock,int numTargetBlock);
void print();
void moveOnto(int moveBlock,int targetBlock);
void moveOver(int moveBlock,int targetBlock);
void pileOnto(int moveBlock,int targetBlock);
void pileOver(int moveBlock,int targetBlock);
//因為0<n<25,所以我們讓位置陣列的長度為25
const int arrSize=25;
//使用者輸入的木塊堆的個數
int input;
//定義一個每個元素為vector的陣列,使用陣列來代表每個位置,即pile[i]代表每個木塊堆
//然後每個位置為vector,用於儲存木塊序號,因為移動讓該位置的儲存資料的資料結構不斷變化,所以我們選用vector來儲存
vector<int> pile[arrSize];
//歸位函式:把某個木塊上方的木塊全部歸位,輸入:要歸位的木塊編號
//查詢使用者輸入的木塊編號的時間複雜度為O(n)
void back_above(int numOfBlock)
{
//迴圈遍歷使用者輸入的n個木塊堆
for(int i=0;i < input;i++)
{
//遍歷一個木塊堆中的木塊
for(vector<int>::iterator it = pile[i].begin();it != pile[i].end();it++)
{
//如果找到了使用者輸入的木塊
if(*it == numOfBlock)
{
//從後遍歷木塊,將每個木塊插入到編號為它的木塊堆中,並將它從現在的木塊堆中刪除
for(vector<int>::iterator iter = pile[i].end()-1;iter != it;iter--)
{
pile[*iter].push_back(*iter);
pile[i].pop_back();
//疑問:pop_back()會不會釋放彈出元素原來所佔的記憶體空間
}
break;
}
}
}
}
//移動一個木塊:輸入:要移動的木塊 輸出:目標木塊 且要移動的木塊上面已沒有任何木塊
void moveOneBlock(int numMoveBlock,int numTargetBlock)
{
//定義兩個變數儲存要移動木塊和目標木塊所在堆
int moveAtPile=findPile(numMoveBlock);
int targetAtPile=findPile(numTargetBlock);
//移動木塊
pile[targetAtPile].push_back(numMoveBlock);
pile[moveAtPile].pop_back();
}
//找木塊編號對應的木塊堆的函式
int findPile(int numOfBlock)
{
//定義一個變數儲存木塊所在堆
int atPile=0;
//迴圈遍歷木塊堆找到要移動木塊和目標木塊所在堆
for(int i=0;i < input;i++)
{
//遍歷一個木塊堆中的木塊
for(vector<int>::iterator it = pile[i].begin();it != pile[i].end();it++)
{
//如果找到了要移動的木塊
if(*it == numOfBlock)
{
atPile = i;
return atPile;
}
}
}
return atPile;
}
//移動一摞木塊:輸入:要移動的一摞木塊的起始木塊 輸出:目標木塊
// 找到要移動木塊和目標木塊所在的堆
// 從要移動木塊所在堆找要移動木塊所在的位置
// 從要移動的木塊開始移動到目標木塊所在堆
void moveBlocks(int beginMoveBlock,int numTargetBlock)
{
//找到要移動木塊和目標木塊所在的堆
int moveAtPile=findPile(beginMoveBlock);
int targetAtPile=findPile(numTargetBlock);
for(vector<int>::iterator it = pile[moveAtPile].begin();it != pile[moveAtPile].end();it++)
{
if(*it == beginMoveBlock)
{
for(vector<int>::iterator iter = it;iter != pile[moveAtPile].end();it++)
{
pile[targetAtPile].push_back(*it);
}
for(vector<int>::iterator iter = it;iter != pile[moveAtPile].end();it++)
{
pile[moveAtPile].pop_back();
}
}
}
}
//列印結果列表
void print()
{
for(int i=0; i < input;i++)
{
cout<<i<<":";
for(int j=0;j < pile[i].size();j++)
{
cout<<" "<<pile[i][j]<<endl;
}
}
}
//封裝四個操作
void moveOnto(int moveBlock,int targetBlock)
{
//將a和b上方的木塊全部歸位
back_above(moveBlock);
back_above(targetBlock);
//把a摞到b上
moveOneBlock(moveBlock,targetBlock);
}
void moveOver(int moveBlock,int targetBlock)
{
//將a上方的木塊全部歸位
back_above(moveBlock);
//把a放在b所在木塊堆的頂部
moveOneBlock(moveBlock,targetBlock);
}
void pileOnto(int moveBlock,int targetBlock)
{
//將b上方的木塊全部歸位
back_above(targetBlock);
//把a以及上面的木塊整體摞在b上面
moveBlocks(moveBlock,targetBlock);
}
void pileOver(int moveBlock,int targetBlock)
{
//把a以及上面的木塊整體摞在b所在木塊堆的頂部
moveBlocks(moveBlock,targetBlock);
}
int main()
{
cout<<"請輸入木塊堆的數目:"<<endl;
cin>>input;
}
elipse下執行結果:
未寫完測試函式,待補充
經驗總結:
題目3:安迪的第一個字典
思路:
找出不同的單詞,並且進行排序,這就自然想到要用set
先獲取到使用者輸入的文字串,再進行處理
將非法字元轉化成空格,由於題目要忽略大小寫,我們將所有字元都轉化為小寫
再插入set進行自動排序
將set中儲存的單詞打印出來即可
程式碼實現:
#include <iostream>
#include <string>
#include <set>
#include <sstream>
using namespace std;
//定義一個集合來儲存每個單詞
set<string> saveWord;
int main() {
string inputText;//使用者輸入的文字串
string wordBuffer;//用於sstream流分離單詞時接受每個單詞
//獲取使用者的輸入的文字,直到遇到檔案結束符或非法輸入為止
while(cin >> inputText)
{
//遍歷使用者輸入的文字字串
for(unsigned int i=0;i < inputText.length();i++)
{
//如果是字母,轉換為小寫
if(isalpha(inputText[i]))
inputText[i] = tolower(inputText[i]);
else //不是字母,就是其他字元,轉換成空格
inputText[i] = ' '; //剛開始輸入的是“ ”,報錯,因為inputText本身是字串,對它使用[]運算子返回的是字元,所以只能用‘’
}
//用stringstream將文字串分割成單個單詞
//原理見補充部分
stringstream word(inputText);
while(word >> wordBuffer)
{//將單個單詞插入集合中
saveWord.insert(wordBuffer);
}
}
//列印set集合中順序儲存的字串
for(set<string>::iterator it=saveWord.begin();it != saveWord.end();it++)
cout<<*it<<endl;
}
eclipse下執行效果圖:
知識點詳細用法補充:待補充
1.isalpha()
2.tolower()
3.stringstream()