1. 程式人生 > >C++ STL練習(1)

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()