STL set

set集合容器使用一种称为红黑树(Red-Black Tree)的平衡二叉检索树的数据结构,来组织泛化的元素结构。每个节点包含一个取值红色或黑色的颜色域,以利于进行树的平衡处理。作为节点键值的元素的插入,必须确保每个子树根节点的键值大于左子树所有节点的键值,而小于右子树所有节点的键值。不会将重复的键值插入容器,也不需要指定具体的插入位置,而按元素在树中的关联关系,进行位置检索和插入,元素的删除依然。

      set是一种随机存储的关联式容器,其关键词(key)也是元素(value)。set之中所有元素互不相同。set是通过二叉查找树来实现的。

关于set,必须说明的是set关联式容器。set作为一个容器也是用来存储同一数据类型的数据类型,并且能从一个数据集合中取出数据,在set中每个元素的值都唯一,而且系统能根据元素的值自动进行排序。应该注意的是set中数元素的值不能直接被改变。C++ STL中标准关联容器set, multiset, map, multimap内部采用的就是一种非常高效的平衡检索二叉树:红黑树,也成为RB树(Red-Black Tree)。RB树的统计性能要好于一般平衡二叉树,所以被STL选择作为了关联容器的内部结构。

一、为何map和set的插入删除效率比用其他序列容器高?

大部分人说,很简单,因为对于关联容器来说,不需要做内存拷贝和内存移动。说对了,确实如此。set容器内所有元素都是以节点的方式来存储,其节点结构和链表差不多,指向父节点和子节点。结构图可能如下:

 

  A
   / \
  B C
 / \ / \
  D E F G

因此插入的时候只需要稍做变换,把节点的指针指向新的节点就可以了。删除的时候类似,稍做变换后把指向删除节点的指针指向其他节点也OK了。这里的一切操作就是指针换来换去,和内存移动没有关系。

二、 Set是一个有序的集合,所以有3点要注意:

 1 唯一的元素值。如果需要有多个相同的元素,请用multiset

 2 元素值本身就是它的键。如果需要键值对,请用map

 3 元素时刻遵循有序的规则,非排序的请用unordered_set

set与multiset:

set/multiset会根据待定的排序准则,自动将元素排序。两者不同在于前者不允许元素重复,而后者允许。
1) 不能直接改变元素值,因为那样会打乱原本正确的顺序,要改变元素值必须先删除旧元素,则插入新元素
2) 不提供直接存取元素的任何操作函数,只能通过迭代器进行间接存取,而且从迭代器角度来看,元素值是常数
3) 元素比较动作只能用于型别相同的容器(即元素和排序准则必须相同)

模板原型:

set模板原型://Key为元素(键值)类型
template <class Key, class Compare=less<Key>, class Alloc=STL_DEFAULT_ALLOCATOR(Key) >
从原型可以看出,可以看出比较函数对象及内存分配器采用的是默认参数,因此如果未指定,它们将采用系统默认方式

三、创建set对象

创建set对象,共6种方式,提示如果比较函数对象及内存分配器未出现,即表示采用的是系统默认方式。

1.创建一个空的set

   set<int> s0 ;

2.创建一个带大于比较器的set, 默认是小于比较器less<int>

  set<int, greater<int>> s1 ;

3.用数组初始化一个set

   int a[3] = {1, 2, 3} ;
   set<int> s2(a, a + 3) ;

4.用拷贝构造函数初始化set

  set<int> s1 ;
  set<int> s2(s1) ;

5.区间初始化

  set<int> s1 ;
  set<int> s2(s1.begin(), s1.end()) ;
6.创建空的set对象,元素类型是char*, 比较函数对象(即排序准则)为自定义的stress

struct strLess
{
     bool operator() (const char *s1, const char *s2) const
    {
     return strcmp(s1, s2) < 0;
   }

}
;

set<const char*,strLess> s2(strLess());

7.用迭代区间[&first, &last)所指的元素,及比较函数对象strLess,创建一个set对象


const char* szArray[] = {"hello", "dog", "bird" };


set<const char*, strLess> s5(szArray, szArray + 3, strLess() );

8.创建自定义类型的set:

怎样在set中放入自定义类型?两种方法:1、定义一个函数对象并在定义set的时候将其作为第二个模板参数。2、为自定义类型定义<运算符。如:

class Edge  
{  
public:  
Edge(int u, int v): u(u), v(v){}      
bool operator < (const Edge& edge) const     
{  
    return this->u < edge.u;      
}    //为了方便起见设为public    int u;      int v;  
};  
class EdgeComp  
{  
public:  
bool operator()(const Edge& left, const Edge& right)  
{          
    return left.u < right.u;      
}  
};  
std::set<Edge> edge_set;  
Edge edge_a(0, 1);  
Edge edge_b(0, 2);      
edge_set.insert(edge_a);  
edge_set.insert(edge_b);  
std::set<Edge, EdgeComp> edge_set2;  
edge_set2.insert(edge_a);  
edge_set2.insert(edge_b);  

四、set里元素遍历

正向遍历:

使用while

   int a[3] = {1, 2, 3} ;
   set<int> s(a, a + 3) ;
   
  set<int>::const_iterator itor ;
  itor = s.begin() ;
  
  while (itor != s.end())
   {
     cout << *itor << endl ;
    ++itor ;
  }

使用for

  int a[3] = {1, 2, 3} ;
  set<int> s(a, a + 3) ;
  
  set<int>::const_iterator itor ;
  for (itor = s.begin(); itor != s.end(); ++itor)
  {
    cout << *itor << endl ;
  }
反向遍历:

使用while

  int a[3] = {1, 2, 3} ;
  set<int> s(a, a + 3) ;
   
  set<int>::const_reverse_iterator ritor ;
  ritor = s.rbegin() ;
  
  while (ritor != s.rend())
   {
    cout << *ritor << endl ;
    ++ritor ;
  }

使用for

   int a[3] = {1, 2, 3} ;
  set<int> s(a, a + 3) ;
  
  set<int>::const_reverse_iterator ritor ;
  for (ritor = s.rbegin(); ritor != s.rend(); ++ritor)
   {
    cout << *ritor << endl ;
   }
五、元素插入
1,插入value,返回pair配对对象,可以根据.second判断是否插入成功。(提示:value不能与set容器内元素重复)

pair<iterator, bool> insert(value)

如:
  s1.insert(20);
  cout<<"s1.insert(20).second = "<<endl;;
  if (s1.insert(20).second)
      cout<<"Insert OK!"<<endl;
  else
     cout<<"Insert Failed!"<<endl;

2,在pos位置之前插入value,返回新元素位置,但不一定能插入成功

iterator insert(&pos, value)

3,将迭代区间[&first, &last)内所有的元素,插入到set容器

void insert[&first, &last)

如:批量插入实例

插入整个数组

  int a[3] = {1, 2, 3} ;
  set<int> s ;
  s.insert(a, a + 3) ;
插入其他set值
  int a[3]={1,2,3};
  set<int> s(a,a+3);
  set<int> s1;
  s1.insert(s.begin(),s.end());
六、元素删除
1. size_type erase(value) 
删除set容器内元素值为value的所有元素,返回移除的元素个数.对于set容器来说,此函数总是返回1,因为set容器不会出现重复的元素值(键值)
2.void erase(&pos) 
移除pos位置上的元素,无返回值
3.void erase(&first, &last) 
移除迭代区间[&first, &last)内的元素,无返回值

4,void clear(), 移除set容器内所有元素

例子:

   set<int> s ;
   for (int i = 1; i <= 5; ++i)
   s.insert(i) ;
 
  set<int>::const_iterator citor ;
  citor = s.begin() ;
  ++citor ; // citor now point to 2
   
  // 删除单个元素
 s.erase(citor) ; // erase 2 ; 
 
 //批量删除
 citor = s.find(3) ; // itor now point to 3
 s.erase(citor, s.end()) ; // erase 3, 4, 5
  
 //删除所有元素
 s.erase(s.begin(), s.end()) ;// erase all elements, same as s.clear()

七、元素查找

1.count(value) 返回set对象内元素值为value的元素个数

 set<int> s ;
 for (int i = 1; i <= 5; ++i)
     s.insert(i) ;
  
 set<int>::iterator itor ;
 if(s.count(4) == 1) // return 1 if s contains 4, else 0
     cout << "s contains 4" ;
 else
    cout << "s does not contains 4" ;
 
2.iterator find(value) 返回value所在位置,找不到value将返回end() 

 set<int> s ;
 for (int i = 1; i <= 5; ++i)
     s.insert(i) ;
 
  set<int>::iterator itor ;
  itor = s.find(4) ;
  if(itor != s.end()) // itor point to s.end() if not found
     cout << "found" ;
   else
    cout << "not found" ;
 
3.lower_bound(value),upper_bound(value), equal_range(value) 略,参见map里的用法 

八、其他常用函数

元素判空:s1.empty()

元素个数:s1.size()

交换:s1.swap(s2)

九、小结

set 集合容器是一个有序关联容器,所包含的元素必须是唯一的,不能插入重复的元素(如果重复插入了,程序会自动舍弃重复的元素)。由于使用了红黑树这种特殊的平衡二叉检索树管理元素数据,具有高效的元素插入、删除和检索。元素检索的算法时间复杂度 【log2 N】,也即 2 的 N 次方个元素的检索,只需要 N 此比较操作。

pair ,Functor 是比较高级的用法,必须掌握。
    set 缺点:不能重复插入相同元素,删除、插入 操作频繁的时候,set 就不适合。
    set 优点:对于元素的检索,非常快







  
 
 




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值