1. 程式人生 > >c++中set用法

c++中set用法

1.關於set
C++ STL 之所以得到廣泛的讚譽,也被很多人使用,不只是提供了像vector, string, list等方便的容器,更重要的是STL封裝了許多複雜的資料結構演算法和大量常用資料結構操作。vector封裝陣列,list封裝了連結串列,map和set封裝了二叉樹等,在封裝這些資料結構的時候,STL按照程式設計師的使用習慣,以成員函式方式提供的常用操作,如:插入、排序、刪除、查詢等。讓使用者在STL使用過程中,並不會感到陌生。
關於set,必須說明的是set關聯式容器。set作為一個容器也是用來儲存同一資料型別的資料型別,並且能從一個數據集合中取出資料,在set中每個元素的值都唯一,而且系統能根據元素的值自動進行排序。應該注意的是set中數元素的值不能直接被改變。C++ STL中標準關聯容器set, multiset, map, multimap內部採用的就是一種非常高效的平衡檢索二叉樹:紅黑樹,也成為RB樹(Red-Black Tree)。RB樹的統計效能要好於一般平衡二叉樹,所以被STL選擇作為了關聯容器的內部結構。

關於set有下面幾個問題:
(1)為何map和set的插入刪除效率比用其他序列容器高?
大部分人說,很簡單,因為對於關聯容器來說,不需要做記憶體拷貝和記憶體移動。說對了,確實如此。set容器內所有元素都是以節點的方式來儲存,其節點結構和連結串列差不多,指向父節點和子節點。結構圖可能如下:

  A
  / \
  B C
 / \ / \
D E F G
因此插入的時候只需要稍做變換,把節點的指標指向新的節點就可以了。刪除的時候類似,稍做變換後把指向刪除節點的指標指向其他節點也OK了。這裡的一切操作就是指標換來換去,和記憶體移動沒有關係。
(2)為何每次insert之後,以前儲存的iterator不會失效?


iterator這裡就相當於指向節點的指標,記憶體沒有變,指向記憶體的指標怎麼會失效呢(當然被刪除的那個元素本身已經失效了)。相對於vector來說,每一次刪除和插入,指標都有可能失效,呼叫push_back在尾部插入也是如此。因為為了保證內部資料的連續存放,iterator指向的那塊記憶體在刪除和插入過程中可能已經被其他記憶體覆蓋或者記憶體已經被釋放了。即使時push_back的時候,容器內部空間可能不夠,需要一塊新的更大的記憶體,只有把以前的記憶體釋放,申請新的更大的記憶體,複製已有的資料元素到新的記憶體,最後把需要插入的元素放到最後,那麼以前的記憶體指標自然就不可用了。特別時在和find等演算法在一起使用的時候,牢記這個原則:不要使用過期的iterator。
(3)當資料元素增多時,set的插入和搜尋速度變化如何?

如果你知道log2的關係你應該就徹底瞭解這個答案。在set中查詢是使用二分查詢,也就是說,如果有16個元素,最多需要比較4次就能找到結果,有32個元素,最多比較5次。那麼有10000個呢?最多比較的次數為log10000,最多為14次,如果是20000個元素呢?最多不過15次。看見了吧,當資料量增大一倍的時候,搜尋次數只不過多了1次,多了1/14的搜尋時間而已。你明白這個道理後,就可以安心往裡面放入元素了。
2.set中常用的方法

begin()    ,返回set容器的第一個元素
end()      ,返回set容器的最後一個元素
clear()    ,刪除set容器中的所有的元素
empty()    ,判斷set容器是否為空
max_size()   ,返回set容器可能包含的元素最大個數
size()      ,返回當前set容器中的元素個數

#include <iostream>
#include <set>
using namespace std;
int main()
{
    set<int>a;
    a.insert(1);
    a.insert(2);
    a.insert(6);
    a.insert(100);
    a.insert(54);
    set<int>::iterator i=a.begin();
    set<int>::iterator x;
    set<int>::iterator y;

    x=--a.end();
    cout<<"最後一個元素是"<<*x<<endl;

    for(;i!=a.end();i++)
    {
        cout<<*i<<endl;
    }
    cout<<endl;

    int m=a.count(100);
    cout<<m<<endl;

    a.erase(1);
    cout<<"now there are "<<a.size()<<" in this set"<<endl;

    x=a.find(2);
    cout<<*x<<endl;
    if(x!=a.end())
        cout<<"2 found"<<endl;
    y=a.find(3);
    cout<<*y<<endl;
    cout<<*a.end()<<endl;
    if(y!=a.end())
        cout<<"3 not found"<<endl;
    return 0;
}

這裡寫圖片描述
一些函式看輸出結果就可知用處,
需要說一下的就是,a.find()這個函式,返回給定值,若沒有找到就返回a.end()

erase()函式:

#include<iostream>
#include<set>
using namespace std;
int main()
{
    set<int>a;
    for(int i=1;i<=10;i++)
        a.insert(i);
    set<int>::iterator first;
    set<int>::iterator second;
    set<int>::iterator x;
    a.erase(8);
    //a.erase(a.begin());
    first=a.begin();
    second=a.begin();
    second++;
    second++;

    a.erase(first,second);
    for(x=a.begin();x!=a.end();x++)
        cout<<*x<<endl;
    return 0;
}

這裡寫圖片描述

lower_bound(key_value) ,返回第一個大於等於key_value的定位器
upper_bound(key_value),返回最後一個大於等於key_value的定位器

#include<iostream>
#include<set>
using namespace std;
int main()
{
    set<int> s;
    s.insert(1);
    s.insert(3);
    s.insert(4);
    cout<<*s.lower_bound(2)<<endl;
    cout<<*s.lower_bound(3)<<endl;
    cout<<*s.upper_bound(3)<<endl;

    return 0;
}

這裡寫圖片描述

自定義比較函式
(1)元素不是結構體:

struct ty
{
    int data;
};
struct mycomp
{
    bool operator()(const ty&a,const ty&b)
    {
        return a.data>b.data;
    }
};
set<int,mycomp>s;
set<int,mycomp>::iterator it;

(2)如果元素是結構體,可以直接將比較函式寫在結構體內。

        struct Info
        {
            string name;
            float score;
            //過載“<”操作符,自定義排序規則
            bool operator < (const Info &a) const
            {
                //按score從大到小排列
                return a.score<score;
            }
        }
        set<Info> s;
        ......
        set<Info>::iterator it;