STL set深入分析

来源:互联网 发布:淘宝刷销量兼职可信吗 编辑:程序博客网 时间:2024/06/05 10:02

其实写出一个set的程序很简单。但是这里面的东西可把我搞糊涂了一天,现在总算有点眉目了。写篇博客,希望后来者可以轻松的解决烦恼。

首先我们从set的构造函数开始,set<key,Compare&,Alloc>,第一个参数为元素类型,第二个为比较大小的,第三个为分配内存的。

元素类型我们可以是我们自己定义的类型,比较大小的函数也是可以我们自己定义的。至于内存分配,我们不用管,stl中有默认内存分配器。

为了从本质来分析set,我打算自己用自己定义的类型(一个结构体),这样才有价值嘛。

struct Node{Node(int v=0,string s=" "):num(v),name(s){}int num;string name;};


这就是我们的自定义类型。

但是set不知道怎么给我们的类型比较大小啊,不像int那些可以直接用。所以我们还欠缺一步就是写出比较元素大小的函数。

这里有两种方法:

方法一:(在自定义的结构体内定义)

struct Node{Node(int v=0,string s=" "):num(v),name(s){}int num;string name;bool operator<(const Node& r) const {return strcmp(name.data(),r.name.data())<0;}};
这里是重载<,注意后面的const,至于函数体细节目前可以暂时不管。
方法二:(在别的结构体内定义)

struct NodeLess{bool operator()(const Node& a,const Node& b)  const{return strcmp(a.name.data(),b.name.data())<0;}};
这里是重载(),注意后面的const。

接下来来关注一下函数体的细节处理:

用于对关联容器排序的比较函数必须为它们所比较的对象定义一个‘严格的弱序化’(strick weak ordering)”。什么是严格的弱序化?让我们看看wiki上的定义:

严格弱序化拥有如下属性。对于集合S中所有的x,y,z,
对于所有的x,不存在x < x (非自反性 - 21条标题说的就是这个)
对于所有x不等于y,如果x < y那么不存在y < x (不对称性)
对于所有的x,y和z,如果x < y并且y < z,那么x < z(传递性)
如果x < y,那么对于所有的z,要么x < z要么z < y(或者两者都成立)

effective stl中有一条原则是:永远让比较函数对相等值的比较返回false。其实这个比较函数就是为了把元素放置到正确的位置上,相等元素(严格来说是相似,具体请看effective stl)。在执行插入操作的时候,将该插入元素和红黑树的元素比较,找到正确的位置,如果出现相等元素,那么插入失败,它每次比较都会调用比较函数两次:

!cmp(key1,key2)&&!cmp(key2,key1),如果key1==key2那么返回true,插入失败。

所以上面是return strcmp(name.data(),r.name.data())<0;而不仅仅是return strcmp(name.data(),r.name.data());因为并不能判定大小,而只是能判断是否是相同。让我们来分析一下吧。我们依次插入:Node(1,"wu0"),Node(4,"wu3"),Node(3,"wu2"),Node(2,"wu1"),Node(5,"wu4"),Node(5,"wu5")。

下面都是基于我用return strcmp(name.data(),r.name.data())来插入的。只是为了理解插入元素的思路。

 ====================》===================================================================》============================================》



这是根据红黑树来分析的,应该没错吧。。我也是临时看的红黑树。

通过分析,我们知道每次都是在节点的左边插入,因为每次返回的都是true,-1也是true啊,只有0才是false,而0是相等的元素的情况。所以我们应该要用return strcmp(name.data(),r.name.data())<0;以满足不对称性原则。

提供一个用法的例子吧。。

#include<iostream>#include<string>#include<set>using namespace std;struct Node{Node(int v=0,string s=" "):num(v),name(s){}int num;string name;bool operator<(const Node& r) const {return strcmp(name.data(),r.name.data())<0;}};void main(){set<Node>mySet;set<Node>::iterator iter;pair<set<Node>::iterator,bool> pairs;pairs=mySet.insert(Node(1,"wu0"));cout<<pairs.second<<endl;pairs=mySet.insert(Node(4,"wu3"));cout<<pairs.second<<endl;pairs=mySet.insert(Node(3,"wu2"));cout<<pairs.second<<endl;pairs=mySet.insert(Node(2,"wu1"));cout<<pairs.second<<endl;pairs=mySet.insert(Node(5,"wu4"));cout<<pairs.second<<endl;pairs=mySet.insert(Node(5,"wu5"));cout<<pairs.second<<endl;pairs=mySet.insert(Node(4,"wu1"));cout<<pairs.second<<endl;for (iter = mySet.begin(); iter != mySet.end(); iter++)cout<<(*iter).num<<" "<<(*iter).name<<endl;iter=mySet.find(Node(3,"wu0"));if(iter!=mySet.end())cout<<((*iter).num)<<" "<<((*iter).name)<<endl;elsecout<<"没找到"<<endl;cout<<endl;}


可以发现最后一个插入元素操作没有成功,因为set中已经有了一个wu1。

如果你还是用return strcmp(name.data(),r.name.data())来慢慢调试这个程序的话,可以得到很多东西,有兴趣的可以试试。

原创粉丝点击