《C++ Primer》學習筆記(十):泛型算法


泛型算法是一組公共接口,可以用於不同類型的元素和多種容器類型。

泛型算法本身不會執行容器的操作,只會運行於特定迭代器上,執行迭代器的操作,即不會改變底層容器的大小。但是標準庫定義了一類特殊的迭代器:插入器(inserter)。與普通迭代器只能遍歷所綁定的容器相比,插入器能做更多的事情。當給這類迭代器賦值時,它們會在底層的容器上執行插入操作。因此當一個算法操作這樣的迭代器時,迭代器可以完成向容器添加元素的操作,但算法自身則不會做插入操作。

初識泛型算法

只讀算法

對於只讀取而不改變元素的算法,最好使用cbegin()cend()。但是,如果你計劃使用算法返回的迭代器來改變元素的值,就需要使用begin()end()的結果作爲參數。

//對vec中的元素求和,和的初值是0
int sum = accumulate(vec.cbegin(), vec.cend(), 0);//accumulate的第三個參數的類型決定了函數中使用哪個加法運算符以及返回值的類型

string sum = accumulate(v.cbegin(), v.cend(), string("") );//正確
string sum accumulate(v.cbegin(), v.cend(), "" );//錯誤:const char*上沒有定義+運算符

那些只接受一個單一迭代器來表示第二個序列的算法,都假定第二個序列至少與第一個序列一樣長。

//確定兩個序列是否保存相同的值
//roster2中的元素數目應該至少與roster1一樣多
equal(roster1.cbegin(), roster1.cend(), roster2.cbegin());

寫容器元素的算法

向目的位置迭代器寫入數據的算法假定目的位置足夠大,能容納要寫入的元素。要保證這一點,可以使用插入迭代器(insert iterator)。當我們通過一個插入迭代器賦值時,一個與賦值號右側值相等的元素被添加到容器中。

vector<int> vec; //空容器
//災難:修改vec中的10個(不存在)元素
fill_n(vec.bigin(), 10, 0);

auto it = back_inserter(vec);//back_inserter()返回與該容器綁定的插入迭代器
*it = 42; //vec中現在有一個元素42

fill_n(back_inserter(vec), 10, 0);//添加10個元素0到vec

拷貝(copy)算法是另一個向目的位置迭代器指向的輸出序列中的元素寫入數據的算法。傳遞給copy的目的序列至少要包含與輸入序列一樣多的元素。

int a1[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
int a2[sizeof(a1)/sizeof(*a1)];//a2與a1大小一樣
//ret指向拷貝到a2的尾元素之後的位置
auto ret = copy(begin(a1), end(a1), a2);//把a1的內容拷貝給a2

//將所有值爲0的元素改爲42
replace(ilst.begin(), ilst.end(), 0, 42);

//使用back_inserter按需要增長目標序列
replace_copy(ilst.cbegin(), ilst.cend(), back_inserter(ivec), 0, 42);
//此調用後,ilst並未改變,ivec包含ilst的一份拷貝,不過原來在ilst中值爲0的元素在ivec中都變爲42

重排容器元素的算法

void elimDups(vector<string> &words)
{
	//按字典序排序words,以便查找重複單詞
	sort(words.begin(), words.end());

	//unique重排輸入範圍,使得每個單詞只出現一次
	//排列在範圍的前部,返回指向不重複區域之後一個位置的迭代器
	auto end_unique = unique(words.begin(), words.end());

	//使用容器操作erase刪除重複單詞
	words.erase(end_unique, words.end());
}

在這裏插入圖片描述
完成sort操作後:
在這裏插入圖片描述
使用unique消除相鄰重複項後:
在這裏插入圖片描述

定製操作

標準庫允許我們爲某些算法提供自己定義的操作來代替默認運算符。

向算法傳遞函數

加入我們希望對一個單詞序列進行重排,首先按長度排序,大小相同的再按字典序排序。那麼默認的sort就無法完成這個操作,需要我們使用重載的sort版本,它接受一個謂詞作爲第三個參數。

謂詞是一個可調用的表達式,其返回結果是一個能用作條件的值。一元謂詞只接受單一參數,二元謂詞接受兩個參數。接受謂詞作爲參數的算法會對輸入序列中的元素調用謂詞。因此元素類型必須能夠轉換爲謂詞的參數類型。

bool isShorter(const string &s1, const string &s2)
{
	return s1.size() < s2.size(); //長度較短的單詞排在更前面
}

//按長度由短至長排序words
sort(words.begin(), words.end(), isShorter);
elimDups(words); //將words按字典序重排,並消除重複單詞
//按長度重新排序,長度相同的單詞維持字典序
stable_sort(words.begin(), words.end(), isShorter);

lambda表達式

標準庫find_if算法接受一對錶示範圍的迭代器和一個謂詞,對範圍內的每個元素調用給定的這個謂詞,函數返回第一個使謂詞返回非0值的元素。如果不存在這樣的元素則返回尾迭代器。

一個lambda表達式 表示一個可調用的代碼單元,具有如下形式:

[capture list](parameter list) -> return type {function body}

可以忽略參數列表和返回類型,但是必須包含捕獲列表和函數體:

//定義一個可調用對象f, 它不接受參數,返回42
auto f = [] {return 42;} ;

cout << f() << endl; //輸出42

lambda表達式不能有默認參數。

//按長度排序,長度相同的單詞維持字典序
stable_sort(words.begin(), words.end(), [](const string &a, const string &b) {return a.size() < b.size();} );

一個lambda只有在其捕獲列表中捕獲一個它所在函數中的局部非static變量,才能在函數體中使用該變量。但是對於局部static變量和它所在函數之外聲明的名字,它可以直接使用。

//獲取一個迭代器,指向第一個滿足size()>=sz的元素
auto wc = find_if(words.begin(), words.end(), 
			[sz](const string &a){return a.size() >= sz;} );

lambda的變量捕獲形式可以是值或引用。採用值捕獲的前提是變量可以拷貝。與參數不同,被捕獲的變量的值是在lambda創建時拷貝,而不是調用時拷貝:

void fcn1()
{
	size_t v1 = 42;//局部變量
	//將v1拷貝到名爲f的可調用對象
	auto f = [v1] {return v1;} ;
	v1 = 0;
	auto j = f(); //j爲42;f保存了我們創建它時v1的拷貝
}

void fcn2()
{
	size_t v1 = 42;//局部變量
	//對象f2包含v1的引用
	auto f = [&v1] {return v1;} ;
	v1 = 0;
	auto j = f(); //j爲0;f2保存了v1的引用,而非拷貝
}

在引用捕獲或者返回引用的時候需要注意,必須確保被引用的對象在lambda執行的時候時存在的。

當定義一個lambda時,編譯器生成一個與lambda對應的新的(未命名的)類類型。因此函數可以返回一個lambda,但是要注意與不能返回一個局部變量的引用類似,這個返回的lambda不能包含引用捕獲。

除了顯示列出我們需要捕獲的變量外,還可以採用隱式捕獲形式,在捕獲列表中寫一個&(表示採用引用捕獲方式)或者=(表示採用值捕獲方式)。讓編譯器來推斷需要捕獲哪些變量。也可以混合使用隱式捕獲和顯式捕獲,但是當混合使用時,捕獲列表的第一個元素必須是&=,它指定了默認捕獲方式。

//os隱式捕獲,引用捕獲方式;c顯式捕獲,值捕獲方式
for_each(words.begin(), words.end(), [&,c](const string &s) {os << s << c;});
//os顯式捕獲,引用捕獲方式;c隱式捕獲,值捕獲方式
for_each(words.begin(), words.end(), [=, &os](const string &s){os << s << c;});

我們應該儘可能減少捕獲的數據量,來避免潛在的捕獲導致的問題。如果可能,避免捕獲指針或引用。
在這裏插入圖片描述

參數綁定

通過標準庫bind函數,可以爲一個已有的函數綁定一個參數列表。bind定義在頭文件functional中。

auto newCallable = bind(callable, arg_list);

arg_list是一個逗號分隔的參數列表,當我們調用newCallable時 ,newCallable會調用callable,並傳遞給它arg_list中的參數。

arg_list中的參數可能包含形如_n的名字,其中n是一個整數。這些參數是“佔位符”,表示newCallable的參數,它們佔據了傳遞給newCallable的參數的“位置”。_1newCallable的第一個參數,_2爲第二個參數,依此類推。

//check6是一個可調用對象,接受一個string類型的參數
//並用此string和值6來調用check_size
auto check6 = bind(check_size, _1, 6);
//此bind調用只有一個佔位符,表示check6只接受單一參數。佔位符出現在arg_list的第一個位置,表明check6的此參數對應check_size的第一個參數,此參數是一個const string&

string s = "hello";
bool b1 = check6(s); //check6(s)會調用check_size(s, 6)

//g是一個有兩個參數的可調用對象
auto g = bind(f, a, b, _2, c, _1);g(X, Y)等效於調用f(a, b, Y, c, X)

名字_n都定義在一個名爲placeholders的命名空間中,而這個命名空間本身定義在std命名中間中,因此在使用佔位符名字前,我們需要做一個using聲明:

using namespace std::placeholders;

還可以使用bind重排參數順序

//按單詞長度由短至長排序
sort(words.begin(), words.end(), isShorter);
//按單詞長度由長至短排序
sort(words.begin(), words.end(), bind(isShorter, _2, _1));

由於默認情況下,bind的那些不是佔位符的參數被拷貝到bind返回的可調用對象中。但是有些情況我們需要綁定引用參數,那麼這時就必須使用標準庫ref函數:

//os是一個局部變量,引用一個輸出流
//c是一個局部變量,類型爲char
for_each(words.begin(), words.end(), [&os, c](const string &s) {os << s << c;});


ostream &print(ostream &os, const string &s, char c)
{
	return os << s << c;
}

//錯誤:不能拷貝os
for_each(words.begin(), words.end(), bind(print, os, _1, ' '));

for_each(words.begin(), words.end(), bind(print, ref(os), _1, ' '));

ref返回一個對象,包含給定的引用,此對象是可拷貝的。類似的,cref生成一個保存const引用的類。

再探迭代器

除了爲每個容器定義的迭代器外,標準庫在頭文件iterator中還定義了額外的幾種迭代器:

  • 插入迭代器:插入迭代器被綁定到一個容器上,可用於向容器中插入元素。
  • 流迭代器 :流迭代器被綁定到輸入或輸出流上,可用來遍歷所關聯的IO流。
  • 反向迭代器 :反向迭代器向後而不是向前移動,除了forward_list之外的標準庫容器都有反向迭代器。
  • 移動迭代器 :這些專用的迭代器不是拷貝其中的元素,而是移動它們。

插入迭代器

根據插入元素的位置不同,分爲三種:

  • back_inserter: 創建一個使用push_back的迭代器。
  • front_inserter :創建一個使用push_front的迭代器。
  • inserter :創建一個inserter的迭代器。此函數接受第二個參數,這個參數必須是一個指向給定容器的迭代器。元素將被插入到給定迭代器所表示的元素之前。

在這裏插入圖片描述

list<int> lst = {1,2,3,4};
list<int> lst2, lst3;//空list

//拷貝完成後,lst2包含4 3 2 1
copy(lst.cbegin(), lst.cend(), front_inserter(lst2) );

//拷貝完成後,lst3包含1 2 3 4
copy(lst.cbegin(), lst.cend(), inserter(lst3, lst3.begin()) );

iostream迭代器

istream_iterator讀取輸入流,ostream_iterator向一個輸出流寫數據。通過使用流迭代器,我們可以用泛型算法從流對象讀數據以及向其寫入數據。

我們可以爲任何定義了輸入運算符(>>)的類型創建istream_iterator對象。類似的,可以爲任何定義了輸出運算符(<<)的類型創建ostream_iterator對象。

istream_iterator操作

istream_iterator<int> int_iter(cin); //從cin讀取int
istream_iterator<int> int_eof; //默認初始化的迭代器可以當作尾後迭代器來使用

while(int_iter != int_eof) //當有數據可供讀取時
    vec.push_back(*int_iter++);

ifstream in("afile");
istream_iterator<string> str_in(in);//從"afile"讀取字符串 

istream_iterator更有用的地方如下:

istream_iterator<int> in_iter(cin), eof;
vector<int> vec(in_iter, eof);//從迭代器範圍構造vec

在這裏插入圖片描述

//使用算法操作流迭代器
istream_iterator<int> in(cin), eof;
//計算從標準輸入讀取的值的和
cout << accumulate(in, eof, 0) << endl;

ostream_iterator操作

在這裏插入圖片描述

ostream_iterator<int> out_iter(cout, " ");
for(auto e : vec)
	*out_iter++ = e; //賦值語句實際上將元素寫到cout
cout << endl;

//通過調用copy來打印vec中的元素,相比於編寫循環更簡單
copy(vec.begin(), vec.end(), out_iter);
cout << endl;

反向迭代器

對於反向迭代器,遞增和遞減的操作含義會反過來。

除了forward_list外,其他容器都支持反向迭代器。可通過rbeginrendcrbegincrend來獲取。

在這裏插入圖片描述
可以通過向sort傳遞一對反向迭代器來實現遞減排序:

sort(vec.begin(), vec.end());//遞增排序

sort(vec.rbegin(), vec.rend());//遞減排序

反向迭代器與普通迭代器的轉換

在這裏插入圖片描述

//在一個逗號分隔的列表中查找第一個元素
auto comma = find(line.cbegin(), line.cend(), ',');
cout << string(line.cbegin(), comma) << endl; //打印第一個單詞

//如果希望打印最後一個單詞,可以改用反向迭代器
//在一個逗號分隔的列表中查找最後一個元素
auto rcomma = find(line.crbegin(), line.crend(), ',');
//錯誤:將輸出逆序單詞。例如如果我們輸入的是first,mid,last  則輸出是tsal
cout << string(line.crbegin(), rcomma) << endl;
//我們需要調用base成員函數將rcomma轉換爲一個普通迭代器(即正向迭代器)
//正確:得到一個正向迭代器,則輸出是last
cout << string(rcomma.base(), line.cend()) << endl;

反向迭代器的目的是表示元素範圍,而這些範圍是不對稱的,這導致一個重要的結果:當我們從一個普通迭代器初始化一個反向迭代器,或是給一個反向迭代器賦值時,結果迭代器與原迭代器指向的並不是相同的元素。

泛型算法結構

每個算法都會對它的每個迭代器參數指明必須提供哪類迭代器。

一個高層類別的迭代器支持低層類別的迭代器的所有操作。
在這裏插入圖片描述
C++標準指明瞭泛型和數值算法的每個迭代器參數的最小類別。

迭代器類別

輸入迭代器必須支持:

  • 用於比較兩個迭代器的相等和不相等運算符(==!=
  • 用於推進迭代器的前置和後置遞增運算(++
  • 用於讀取元素的解引用運算符(*);只會出現在賦值運算符的右側
  • 箭頭運算符(->),等價於(*it).member,即解引用迭代器並提取對象的成員

輸出迭代器必須支持:

  • 用於推進迭代器的前置和後置遞增運算(++)
  • 解引用運算符(*),只能出現在賦值運算符左側(向一個已經解引用的輸出迭代器賦值,就是將值寫入它所指向的元素)

前向迭代器支持所有輸入和輸出迭代器的操作,而且可以多次讀寫同一個元素(即可以對序列進行多遍掃描)。

雙向迭代器除了支持所有前向迭代器的操作外,還支持前置和後置遞減運算符(--)。

隨機訪問迭代器支持雙向迭代器的所有操作,並且還支持下列操作:

  • 用於比較兩個迭代器相對位置的關係運算符(<<=>>=)
  • 迭代器和一個整數值的加減運算(++=--=),計算結果是迭代器在序列中前進(或後退)給定整數個元素後的位置。
  • 用於兩個迭代器上的減法運算符(-),得到兩個迭代器間的距離。
  • 下標運算符(iter[n]),與*(iter[n])等價。

算法參數模式

alg(beg, end, other args);
alg(beg, end, dest, other args); //假定目標空間足夠容納寫入的數據
alg(beg, end, beg2, other args); //假定從beg2開始的範圍與[beg end)表示的範圍至少一樣大
alg(beg, end, beg2, end2, other args);

if版本的算法

接受謂詞算法的參數附加_if後綴。

find(beg, end, val); //查找輸入範圍中val第一次出現的位置
find_if(beg, end, pred); //查找第一個令pred爲真的元素

區分拷貝元素的版本和不拷貝元素的版本

寫到額外目的空間的算法在名字後面附加一個_copy

reverse(beg, end); //反轉輸入範圍中元素的順序
reverse_copy(beg, end, dest); //將元素按逆序拷貝到dest

一些算法同時提供_copy_if版本。

//從v1中刪除奇數元素
remove_if(v1.begin(), v1.end(), [](int i) {return i%2;});

//將偶數元素從v1拷貝到v2;v1不變
remove_copy_if(v1.begin(), v1.end(), back_inserter(v2), 
				[](int i) {return i%2;});

特定容器算法

對於listforward_list,應該優先使用成員函數版本的算法而不是通用算法。

在這裏插入圖片描述
在這裏插入圖片描述
splice算法是鏈表數據結構所特有的。
在這裏插入圖片描述
多數鏈表特有的版本與通用版本間一個至關重要的區別是鏈表版本會改變底層的容器

練習

  1. 頭文件algorithm中定義了一個名爲count的函數,類似於find,接受一對迭代器參數和一個值作爲參數。count返回給定值在序列中出現的次數。編寫程序,讀取int序列存入vector中,打印有多少個元素的值等於給定值。
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

int main()
{
	vector<int> vi;
	int num;

	while(cin>>num){
		vi.push_back(num);
	}

	int value = 3;
	cout<< value <<" appears "<< count(vi.begin(), vi.end(), value) << " times" << endl;
	
	system("pause");
	return 0;
}

在這裏插入圖片描述

  1. accumulate求一個vector<int>中的元素之和
#include <iostream>
#include <vector>
#include<numeric>

using namespace std;


int main()
{
	vector<int> vi{1, 2, 3, 4, 5};

	cout <<" The sum is "<< accumulate(vi.begin(), vi.end(), 0) << endl;
	
	system("pause");
	return 0;
}

在這裏插入圖片描述

  1. 假定v是一個vector<double>,那麼調用accumulate(v.cbegin(), v.cend(), 0)有何錯誤?

因爲accumulate的第三個參數指定了求和的初值,若將初值設定爲0,表明返回值爲int類型,那麼在實際求和時會將double轉換爲int,會引起精度損失。

  1. 編寫程序,使用fill_n將一個序列中的int值都設置爲0
#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;


int main()
{
	vector<int> vi{1, 2, 3, 4, 5};

	cout << "Before change: " << endl;
	for(auto a : vi){
		cout << a << " ";
	}
	cout << endl;

	fill_n(vi.begin(), vi.size(), 0);

	cout << "After change: " << endl;
	for(auto a : vi){
		cout << a << " ";
	}
	cout << endl;

	system("pause");
	return 0;
}

在這裏插入圖片描述

  1. 下面的程序是否有錯誤?如果有,請改正
    (a)
vector<int> vec;
list<int> lst;
int i;

while(cin >> i)
	lst.push_back(i);

copy(lst.cbegin(), lst.cend(), vec.begin());

錯誤,這裏使用copy需要保證vec的大小不能小於lst,因此需要在copy前添加vec.resize(lst.size)

	vector<int> vec;
	list<int> lst;
	int i;

	while(cin >> i)
		lst.push_back(i);
	
	vec.resize(lst.size());
	copy(lst.cbegin(), lst.cend(), vec.begin());

(b)

vector<int> vec;
vec.reserve(10);
fill_n(vec.begin(), 10, 0);

錯誤,這裏不應該用reserve,而是應該使用resize,因爲reserve只是分配空間,實際元素個數還是0個。

vector<int> vec;
vec.resize(10);
fill_n(vec.begin(), 10, 0);
  1. 實現你自己的elimDups,測試在讀取輸入後、調用unique後,調用erasevector中的內容分別是什麼?
#include<iostream>
#include<string>
#include<vector>
#include<algorithm>

using namespace std;
 
void elimDups(vector<string> &s)
{
	cout<<"Before sort:";
	for (int i = 0; i<s.size(); ++i)
	{
		cout<<s[i]<<" ";
	}
	cout<<endl<<"After sort():";
	sort(s.begin(),s.end());//sort排序
	for (int i = 0; i<s.size(); ++i)
	{
		cout<<s[i]<<" ";
	}

	cout<<endl<<"After unique():";
	vector<string>::iterator ite = unique(s.begin(),s.end());//unique排序
	for (int i = 0; i<s.size(); ++i)
	{
		cout<<s[i]<<" ";
	}

	cout<<endl<<"After erase():";
	s.erase(ite,s.end());//erase()操作
	for (int i = 0; i<s.size(); ++i)
	{
		cout<<s[i]<<" ";
	}
 
}
int main(int argc, char**argv)
{
	string a[10] = {"because","I","Like","Like","C++","very","very","much","that's","why"};
	vector<string> s(a,a+10);
	elimDups(s);
 
	system("pause");
	return 0;
}

在這裏插入圖片描述

  1. 編寫程序,使用stable_sortisShorter將傳遞給你的elimDups版本的vector排序。打印vector的內容,驗證你的程序的正確性。
#include<iostream>
#include<string>
#include<vector>
#include<algorithm>

using namespace std;
 
void elimDups(vector<string> &s)
{
	cout<<"Before sort:";
	for (int i = 0; i<s.size(); ++i)
	{
		cout<<s[i]<<" ";
	}
	cout<<endl<<"After sort():";
	sort(s.begin(),s.end());//sort排序
	for (int i = 0; i<s.size(); ++i)
	{
		cout<<s[i]<<" ";
	}

	cout<<endl<<"After unique():";
	vector<string>::iterator ite = unique(s.begin(),s.end());//unique排序
	for (int i = 0; i<s.size(); ++i)
	{
		cout<<s[i]<<" ";
	}

	cout<<endl<<"After erase():";
	s.erase(ite,s.end());//erase()操作
	for (int i = 0; i<s.size(); ++i)
	{
		cout<<s[i]<<" ";
	}
 
}

bool isShorter(const string &s1, const string &s2)
{
	return s1.size() < s2.size();
}

int main(int argc, char**argv)
{
	string a[10] = {"because","I","Like","Like","C++","very","very","much","that's","why"};
	vector<string> s(a,a+10);
	elimDups(s);
	cout << endl;

	stable_sort(s.begin(), s.end(), isShorter);//stable_sort保持等值元素間的相對順序。
	cout << "After stable_sort():";
	for(auto a:s){
		cout << a << " ";
	}
	cout << endl;
 
	system("pause");
	return 0;
}

在這裏插入圖片描述

  1. 標準庫定義了一個名爲count_if的算法,接受一對錶示輸入範圍的迭代器,還接受一個謂詞,會對輸入範圍中每個元素執行。count_if返回一個計數值,表示謂詞有多少次爲真。使用count_if統計有多少個單詞長度超過6.
#include<iostream>
#include<string>
#include<vector>
#include<algorithm>

using namespace std;
 

int main(int argc, char**argv)
{
	
	vector<string> vec1{"sfsaferqwr","aaa","bbbbbbbbbbb","s","ddd","w"};
	cout << count_if(vec1.begin(), vec1.end(), [](const string&s){return s.size() > 6;}) << endl;
 
 	system("pause");
	return 0;
}

在這裏插入圖片描述

  1. 分別使用inserterback_inserterfront_inserter向容器添加元素,看輸出序列有何不同
#include<iostream>  
#include<list>  
#include<algorithm>  
#include<iterator>

using namespace std;

 
int main(int argc, char**argv)  
{  
	list<int> lst1{1,2,3,4,5,6,7,8,9};
	list<int> lst2;
	list<int> lst3;
	list<int> lst4;
 
	//實現包含頭文件iterator
	copy(lst1.cbegin(),lst1.cend(),back_inserter(lst2));
	copy(lst1.cbegin(),lst1.cend(),front_inserter(lst3));
	copy(lst1.cbegin(),lst1.cend(),inserter(lst4,lst4.begin()));
 
	cout<<"lst2:";
	for(auto item: lst2){
		cout << item << " ";
	}
	cout << endl;

	cout<<endl<<"lst3:";
	for(auto item: lst3){
		cout << item << " ";
	}
	cout << endl;

	cout<<endl<<"lst4:";
	for(auto item: lst4){
		cout << item << " ";
	}
	cout << endl;

	system("pause");
	return 0;  
}  

在這裏插入圖片描述
對於inserterback_inserter是按原順序插入,而對於front_inserter因爲每個元素插入到容器中時,會變成新的首元素,因爲插入的元素序列順序會顛倒過來。

  1. **使用流迭代器、sortcopy從標準輸入讀取一個整數序列,將其排序後輸出,並使用unique_sort保證不輸出重複元素。
#include<iostream>  
#include<fstream>
#include<vector>  
#include<algorithm>  
#include<iterator>
using namespace std;
 
int main(int argc, char**argv)  
{  
	vector<int> vec1;
	//創建流迭代器、尾後迭代器
	istream_iterator<int> str(cin), end;
 
// 	while (str != end)
// 	{
// 		vec1.push_back(*str++);
// 		
// 	}//此操作和copy一樣的效果,只不過要記得++.
	unique_copy(str,end,back_inserter(vec1));//存入vec1,unique_copy只會留下不重複元素
 
	sort(vec1.begin(),vec1.end());
 
	for (auto item: vec1)
	{
		cout<<item<<endl;
	}
 
	system("pause");
	return 0;  
} 

在這裏插入圖片描述

  1. 編寫程序,使用istream_iterator讀取一個保存着許多整數的輸入文件,使用ostream_iterator將奇數寫入第一個輸出文件,每個值後面都跟一個空格;將偶數寫入第二個輸出文件,每個值都獨佔一行。
#include<iostream>  
#include<fstream>
#include<vector>  
#include<algorithm>  
#include<iterator>

using namespace std;

int main(int argc, char**argv)  
{  
	ifstream in("input.txt");
	istream_iterator<int> iter1(in), end;

	vector<int> vi;
	while(iter1 != end){
		vi.push_back(*iter1);
		++iter1;
	}

	ofstream out1("even.txt");
	ofstream out2("odd.txt");

	ostream_iterator<int> iter2(out1, " ");//奇數以空格分割
	ostream_iterator<int> iter3(out2, "\n");//奇數以換行分割

	for(auto item: vi){
		if(item % 2 != 0)//奇數
		{
			iter2++ = item;
		}else{
			iter3++ = item;
		}
	}
 
	system("pause");
	return 0;  
} 
  1. 使用find在一個intlist中查找最後一個值爲0的元素
#include<iostream>  
#include<list>  
#include<algorithm>  
#include<iterator>

using namespace std;

 
int main(int argc, char**argv)  
{  
	list<int> li{1,2,0,3,4,0,5,6};

	auto pos = find(li.crbegin(), li.crend(), 0);

	system("pause");
	return 0;  
} 
  1. 列出幾種常用容器的迭代器屬於哪類
  • vector: random access iterator
  • list: bidirectional iterator
  • deque: random access iterator
  1. 僅根據算法和參數的名字,描述下面每個標準庫算法執行什麼操作?
replace(beg, end, old_val, new_val);
replace_if(beg, end, pred, new_val);
replace_copy(beg, end, dest, old_val, new_val);
replace_copy_if(beg, end, dest, pred, new_val);

(1):遍歷begend,找到oldVal就用newVal替換

(2):遍歷begend,找到滿足pred條件的就用newVal替換

(3):遍歷begend,找到oldVal就用newVal替換,並將其拷貝至dest

(4):遍歷begend,找到滿足pred條件的就用newVal替換,並將其拷貝至dest

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章