STL區間成員函式及區間演算法總結

大CC發表於2019-05-10

在這裡總結下可替代迴圈的區間成員函式和區間演算法;

相比單元素遍歷操作,使用區間成員函式的優勢在於:
1)更少的函式呼叫
2)更少的元素移動
3)更少的記憶體分配

在區間成員函式不適用的情況下也應該使用區間演算法,至少,相比手寫迴圈而言,它更加簡單,有效,並且不容易出錯;

區間成員函式

區間構造

標準容器都支援區間建構函式:

container::container(InputIterator begin, // 區間的起點
                  InputIterator end); // 區間的終點

例如:

int myints[] = { 10, 20, 30, 30, 20, 10, 10, 20 };
std::vector<int> myvector (myints, myints+8); 

以上是C++98中常用法,在C++11中,vector可以直接初始化了:

std::vector<int> second ={10, 20, 30, 30, 20, 10, 10, 20};  
或者:
std::vector<int> second ({10, 20, 30, 30, 20, 10, 10, 20});   

區間插入

標準序列容器提供這種形式的insert:

void container::insert(iterator position, // 區間插入的位置
                    InputIterator begin, // 插入區間的起點
                    InputIterator end); // 插入區間的終點

例如:

int myints[] = { 10, 20, 30, 30, 20, 10, 10, 20 };
std::vector<int> myvector;
myvector.push_back(100);
myvector.insert(myvector.begin(),myints,myints+8); //10 20 30 30 20 10 10 20 100

關聯容器也支援區間插入,但由於其插入後的位置由其比較函式來決定,所以沒有區間插入的位置這個引數;

區間刪除

標準序列容器提供的erase:

iterator container::erase(iterator begin, iterator end); 

c++98的標準關聯容器提供的erase為:

void container::erase(iterator begin, iterator end);

序列容器呼叫erase之後,返回一個迭代器(被刪除的那個元素的下一個),
而關聯容器的erase刪除之後並不返回迭代器.【官方解釋說如果實現成序列容器那樣返回指向下一個迭代器,會導致無法接收的效能下降】;

這一區別在c++11中終於統一了;c++11中,對關聯容器呼叫erase之後會返回一個迭代器(指向被刪除元素的下一個);

iterator container::erase(const_iterator first, const_iterator last);

區間賦值

所有標準容器提供了區間賦值的成員函式:

void container::assign(InputIterator begin, InputIterator end);

這個函式用於給容器賦值,會替代現有值,並根據需要分配空間;
與copy()演算法的區別在於它不需要預先分配空間,並有更高的效能;

int myints[]={10,20,30,40,50,60,70};
std::vector<int> myvector;
myvector.assign(myints,myints+7);

通用區間演算法

for_each 區間迭代

for_each:遍歷,對每個元素都執行一個動作;
C++98只支援最原始的for迴圈,很多語言(java、python等)都實現了foreach區間迭代語法,這讓C++程式設計師眼饞了很久;
在沒有foreach區間迭代的時代,我們可以用for_each()演算法來代替:

例:對每個元素都加5:

void myfunction (int& i) {
    i += 5;
}

std::vector<int> myvector;
myvector.push_back(10);
myvector.push_back(20);
myvector.push_back(30);
for_each(myvector.begin(),myvector.end(),myfunction); //15 25 35

c++11中新增了區間迭代,使得我們對for_each的依賴降低了,使用也更加方便:

for(auto &i : myvector )
{
    i+=5;
}

transform() 區間迭代後新值另存為其它地方

對區間中每個元素執行操作後,將修改後的值寫入到新區間中;
可以認為這個是for_each()演算法不修改原區間的版本;
還是for_each中的例子:

int addfunction(int i ){
    return i+5;
}

void output (int i) {  // output function
    std::cout << ` ` << i;
}

std::vector<int> myvector;
myvector.push_back(10);
myvector.push_back(20);
myvector.push_back(30);
std::vector<int> bvector;
bvector.resize(myvector.size());
transform(myvector.begin(),myvector.end(),bvector.begin(),addfunction);
//輸出
for_each(bvector.begin(),bvector.end(),output); //bvector: 15 25 35

copy() 區間複製

區間複製,一般用於多個容器間的資料傳值;
這個演算法被用的很普遍,其實,很多使用copy的場景,都可以使用區間成員函式來替代(也建議這麼做);

例:複製陣列到vector:

int myints[]={10,20,30,40,50,60,70};
std::vector<int> myvector (7);
std::copy ( myints, myints+7, myvector.begin() );

fill() 區間填充

用一個元素來重複填充區間;
這個演算法使用頻率較低;
例:用5填充vector前4個元素:

std::vector<int> myvector (8);                       // myvector: 0 0 0 0 0 0 0 0
std::fill (myvector.begin(),myvector.begin()+4,5);   // myvector: 5 5 5 5 0 0 0 0

replace() 區間替換

遍歷區間,進行值替換:
例:將以下區間中所有20替換為99:

int myints[] = { 10, 20, 30, 30, 20, 10, 10, 20 };
std::vector<int> myvector (myints, myints+8);            // 10 20 30 30 20 10 10 20
std::replace (myvector.begin(), myvector.end(), 20, 99); // 10 99 30 30 99 10 10 99

更復雜的版本(使用仿函式)replace_if
例:將以下區間中所有大於20替換為99:

bool bigerThen20 (int i) { return i > 20; }

int myints[] = { 10, 20, 30, 30, 20, 10, 10, 20 };
std::vector<int> myvector (myints, myints+8);            // 10 20 30 30 20 10 10 20
std::replace_if (myvector.begin(), myvector.end(), bigerThen20, 99); //10 20 99 99 20 10 10 20

由於用到了仿函式,通過replace_if實現的,用for_each()也很容易實現;

remove() 區間刪除

從區間中刪除指定元素;

int myints[] = { 10, 20, 30, 30, 20, 10, 10, 20 };
std::vector<int> myvector (myints, myints+8);            // 10 20 30 30 20 10 10 20
std::remove(myvector.begin(), myvector.end(), 20); // 10 30 30 10 10 ? ? ? 

注意,remove並不會真正刪除元素,而只是將需要刪除的元素放到到最後,同時返回一個新的尾部迭代器,
比如,上述例子中,呼叫完remove後,vector中的值一般為 //10 30 30 10 10 10 10 20
而如果希望真的刪除元素,需要加上成員函式erase()來實現刪除 【remove-erase慣用法】:

myvector.erase(std::remove(myvector.begin(), myvector.end(), 20),myvector.end()); // 10 30 30 10 10

unique() 區間去重

從區間中刪除相鄰相同的元素,同樣,這個演算法也不會真正的刪除元素,而是將待刪除的元素移到區間尾部;
使用【unique-erase慣用法】:

int myints[] = {10,20,20,20,30,30,20,20,10};           // 10 20 20 20 30 30 20 20 10
std::vector<int> myvector (myints,myints+9);
std::vector<int>::iterator it;
it = std::unique (myvector.begin(), myvector.end());   // 10 20 30 20 10 ?  ?  ?  ?
myvector.erase(it,myvector.end());

推薦支援

如果你覺得本文對你有所幫助,請點選以下【推薦】按鈕, 讓更多人閱讀;

Posted by: 大CC | 28JUN,2015
部落格:blog.me115.com [訂閱]
Github:大CC

相關文章