1. 程式人生 > >【C++ STL應用與實現】56: 使用std::unique刪除重複元素

【C++ STL應用與實現】56: 使用std::unique刪除重複元素

本系列文章的目錄在這裡:(目錄). 通過目錄裡可以對STL總體有個大概瞭解

前言

本文介紹了STL中的unique演算法的使用,結合一個具體例子講解如何使用它刪除自定義型別結合裡面的重複元素(不僅僅是連續的)。

原型

<algorithm>中的unique函式, 它能刪除連續序列的副本(Remove consecutive duplicates in range).

原型如下:

template <class ForwardIterator>
  ForwardIterator unique ( ForwardIterator first, ForwardIterator last );

template <class ForwardIterator, class BinaryPredicate>
  ForwardIterator unique ( ForwardIterator first, ForwardIterator last,
                           BinaryPredicate pred );

“刪除連續序列”這樣的描述容易造成誤解,知道下面幾點有助於更好理解這個操作:

1. 其實unique僅僅是按照一個規則來修改[first, last)之間的元素,並不會刪除任何東西,它不會改變容器的size。這個規則就是連續一樣的元素會被後面的元素覆蓋。

2. 它通過一個返回迭代器result,並保證在[first, result)之間的元素是不存在連續重複的元素。可以把[result, last)之間的元素移除掉,這才算刪除了連續重複的元素。

3. 預設判斷兩個元素相等是使用==操作符,第二個版本也可自己指定二元函式(函式,函式子,lambda).

看看與它等價的程式碼就知道了:

template <class ForwardIterator>
  ForwardIterator unique (ForwardIterator first, ForwardIterator last)
{
  if (first==last) return last;

  ForwardIterator result = first;
  while (++first != last)
  {
    if (!(*result == *first))  // or: if (!pred(*result,*first)) for version (2)
      *(++result)=*first;
  }
  return ++result;
}

// 程式碼摘自 http://www.cplusplus.com/reference/algorithm/unique/

簡單示例

模擬這個原型程式碼的執行過程:


如果原始序列是: 1,2,2,3,2,1,1
那麼unique之後:1,2,3,2,1,1,1, 容器元素被修改了,但是個數沒變,需要手動再把結尾的1,1刪掉,這個位置由unique的返回值來確定,下面的程式碼演示了這個過程:

#include <iostream>
#include <iterator>     // ostream_iterator
#include <algorithm>    // unique,distance
#include <vector>

int main() {
    using namespace std;
    vector<int> vi{1,2,2,3,2,1,1};
    // 去重
    auto result = unique(vi.begin(), vi.end());
    // 刪除
    vi.resize(distance(vi.begin(), result));
    copy(vi.begin(), vi.end(), ostream_iterator<int>(cout, ","));
    return 0;
}

輸出:

1,2,3,2,1,

實習中對unique運用

下面的例子是在完成實習任務中的應用,在做圖片輪廓提取的時候,得到一些點的座標,我在點與點之間畫線,把輪廓描繪出來,在使用OpenCV提取輪廓點之後,有些資料點重複,它們並不是連續重複的,比如:

(1, 2), (3, 4), (1, 2), (3, 4), (3, 4), (1,2)
我要把所有重複的點刪掉,不能僅僅刪除連續重複的,如果用unique結果會是:
(1, 2), (3, 4), (1, 2), (3, 4), (1, 2)

不能把所有重複的刪掉。所以不能直接使用unique,要先把點排序,這樣重複的點都變成連續的了,接下來就可以用unique一次搞定了。這幾步都能用stl輕鬆實現,下面是示例程式碼:
#include <iostream>
#include <iterator>     // ostream_iterator
#include <algorithm>    // sort, copy, unique
#include <vector>

class Point {
public:
    Point(int x, int y) {
        x_ = x;
        y_ = y;
    }

    // 用於copy()庫函式.
    Point(const Point& other) {
        x_ = other.x_;
        y_ = other.y_;
    }

    // 用於sort庫函式.
    bool operator<(const Point & other) {
        return ((x_ < other.x_) && (y_ < other.y_));
    }

    // 用於erase庫函式.
    bool operator==(const Point & other) const {
        return ((x_ == other.x_) && (y_ == other.y_));
    }

    int x() const { return x_; }
    int y() const { return y_; }
private:
    int x_, y_;
};

// 過載<<操作符,為了使用ostream_iterator<Point>
std::ostream& operator<<(std::ostream& os, const Point & p) {
    os << "(" << p.x() << ", " << p.y() << ")";
    return os;
}

int main() {

    using namespace std;

    // c++11風格容器初始化,使用初始化列表(initializer_list)
    vector<Point> vp { Point(1, 2), Point(3, 4), Point(1, 2), Point(3, 4), Point(3, 4), Point(1, 2) };

    // 列印容器內的Point到標準輸出流cout. 以逗號分隔.
    cout << "origin..." << endl;
    copy(vp.begin(), vp.end(), ostream_iterator<Point>(cout, ","));
    putchar(10);
    putchar(10);

    // 容器排序. 為了後面的unique操作,因為unique是刪除連續相同元素的副本.
    sort(vp.begin(), vp.end());
    cout << "after sorting..." << endl;

    // 列印排序後的容器內容.
    copy(vp.begin(), vp.end(), ostream_iterator<Point>(cout, ","));
    putchar(10);
    putchar(10);

    // 刪除重複元素.
    auto it = unique(vp.begin(), vp.end()); // it 型別為 vector<Point>::iterator
    vp.erase(it, vp.end());

    // 列印處理後的內容.
    cout << "after unique..." << endl;
    copy(vp.begin(), vp.end(), ostream_iterator<Point>(cout, ","));
    putchar(10);

    return 0;
}
執行結果:
origin...
(1, 2),(3, 4),(1, 2),(3, 4),(3, 4),(1, 2),

after sorting...
(1, 2),(1, 2),(1, 2),(3, 4),(3, 4),(3, 4),

after unique...
(1, 2),(3, 4),




作者水平有限,對相關知識的理解和總結難免有錯誤,還望給予指正,非常感謝!



歡迎訪問github部落格,與本站同步更新


相關推薦

C++ STL應用實現56: 使用std::unique刪除重複元素

本系列文章的目錄在這裡:(目錄). 通過目錄裡可以對STL總體有個大概瞭解 前言 本文介紹了STL中的unique演算法的使用,結合一個具體例子講解如何使用它刪除自定義型別結合裡面的重複元素(不僅僅是連續的)。 原型 <algorithm>中的unique函

C++ STL應用實現72: 標準庫裡的堆--如何使用標準庫的heap演算法

本系列文章的目錄在這裡:目錄. 通過目錄裡可以對STL總體有個大概瞭解 前言 本文介紹如何使用STL裡的heap(堆)演算法。第一次接觸heap這種資料結構是在大學的資料結構教材上,它是一棵完全二叉樹。在STL中,heap是演算法的形式提供給我們使用的。

C++ STL應用實現5: 如何使用std::array (since C++11)

本系列文章的目錄在這裡:目錄. 通過目錄裡可以對STL總體有個大概瞭解 前言 本文總結了STL中的序列式容器array的用法及注意事項。array的出現代表著C++的程式碼更進一步“現代化”,就像std::string的出現代替了c風格字串並且能和STL

C++ STL應用實現0: 感恩STL——STL, ACM和年輕的我們

本系列文章的目錄在這裡:目錄. 通過目錄裡可以對STL總體有個大概瞭解 前言 本文是一篇洗腦文,鼓吹STL的好處, 回憶作者第一次認識STL,並給出STL的學習路線。 初見STL 第一次接觸STL還是在我的大學時代,大一下學期剛剛學完了C++

小程式之計算器 C++ STL實現 + C 陣列模擬棧實現 適用VC, DEV, codeblack

自己寫的小程式,記錄一下提醒這兩個只能在DEV或者codeblack 執行。 VC能執行的在最後面(哎,畢竟課程設計是在VC裡面測試)。  C++版本的,用STL棧實現的: #include <cstdio> #include <cstring>

C++STL/紅黑樹POJ 3481 DoubleQueue

POJ 3481 Double Queue 描述: 新成立的BIG-Bank在不切雷斯特開了一間新辦公室,使用了由IBM羅馬尼亞的現代計算機辦公環境,運用了現代資訊科技.一般來說,銀行的每個顧客都有一個識別碼K,並且每一個來銀行的顧客都會被給予一個優先順序P.銀行主管的一個大膽想法震驚了公司的軟體工程師.

Linxu核心設計實現-第7章 中斷和中斷處理

第7章 中斷和中斷處理 作業系統的核心任務之一-對連線上的硬體進行管理(硬碟、鍵盤、滑鼠等)。要想管理這些硬體,就需要可以和他們進行通訊。硬體的反應要遠遠慢於CPU,輪詢會耗費大量CPU資源,顯然不

C++ STL學習之五容器set和multiset

一、set和multiset基礎 set和multiset會根據特定的排序準則,自動將元素進行排序。不同的是後者允許元素重複而前者不允許。 需要包含標頭檔案: #include <set> set和multiset都是定義在std空間裡的類模板: templ

C++ STLDeques

容器 ever pty ngs 速度 pos algo dom 器) 1、結構   容器deque和vector非常相似,也是采用動態數組來管理元素,提供隨機存取,有著和vector幾乎一樣的接口,不同的是deque的動態數組頭尾都開放,因此可以在頭尾都可以進行快速的安插和

C++ STL容器的選擇

但是 函數 pair list 成員 cto 允許 數據 結構 c++提供了各具特長的容器,那麽我們該如何選擇最佳的容器? 缺省狀態下應該選擇vector,因為vector內部結構最簡單,並允許隨機存取,所以數據的存取十分方便,數據的處理也快。 如果經常要在頭部和尾部安插

C++ STLQueue

stack push com col 第一個 順序 size deque lis 1、定義   class queue<>實作為一個queue(也成為FIFO,先進先出)。可以使用push()將任意數量的元素置入queue中,也可以使用pop()將元素以其插入順

C++ Primer 第16章《模板泛型編程》目錄

cnblogs OS pan c++ get In lan microsoft .cn 模板與泛型編程 • 定義模板(16.1) 類模板(16.1.2) 類前置聲明範例 •【C

10、C++ STL容器適配器(stack queue priority_queue)

pub function 適配 pty str 成員 cto ali The 容器適配器   stack、queue、priority_queue 都不支持任一種叠代器,它們都是容器適配器類型,stack是用vector/deque/list對象創建了一個先進後出容器;qu

C++深度剖析教程39實現C++陣列類模板

上一篇文章在那個學習了多引數類模板與特化的分析:點選連結檢視上一篇文章:類模板深度剖析 本篇文章學習記錄: 數值型模板引數 實現C++陣列類模板 1、模板中的數值型引數 模板引數可以是數值型引數。也就是非型別引數。如下圖所示: 我們可以像上面定義

5、C++ STL仿函式(函式物件)

仿函式(函式物件)     仿函式又稱函式物件,函式物件首先是一個物件,即某個類的例項。其次,函式物件的行為和函式一致,即是說可以像呼叫函式一樣來使用函式物件,如引數傳遞、返回值等。這種行為是通過過載類的()操作符來實現的。 【示例】 class Print {

8、C++ STL容器介面卡(stack/queue/priority_queue)

容器介面卡     stack、queue、priority_queue 都不支援任一種迭代器,它們都是容器介面卡型別,stack是用vector/deque/list物件建立了一個先進後出容器;queue是用deque或list物件建立了一個先進先出容器;pr

Android UI設計開發第06期:底部選單欄(一)使用TabActivity實現底部選單欄

轉載請註明出處:http://blog.csdn.net/yangyu20121224/article/details/8989063               從這一篇文章開始,我們將進入到一個應用程式主介面UI的開發和設計中了,底部選單欄在Android的應用開發當

C++ STL細數C++ STL 的那些事---vector (動態陣列)

一,vector概述         vector是一個順序容器,可以存放各種型別的物件,簡單地說,vector是一個能夠存放任意型別的動態陣列,能夠增加和壓縮資料。         vector是動態空間,隨著元素的增加內部機制可以自行擴充空間,而array則是固定大小空

C++ STL模板之queue佇列的用法

#include <iostream> #include <queue> #include <assert.h> /* 呼叫的時候要有標頭檔案: #incl

資料結構C++STL棧和FIFO佇列

其實我就是水一發部落格 STL中自帶的棧和佇列 庫分別是stack和queue 支援的最主要的三個操作就是push pop 和top(front) push是在棧或者佇列的頂端放入一組資料 pop在棧中是取出頂端的一組資料 而在佇列中是取出最底端的元素