关于set实现结构体自动去重原理的推论
转自本人博客,原文链接
先说结论
在每个操作均为log复杂度的前提下,set无法在判断顺序和重复关键字不同时完成对结构体元素的去重。
首先我们先看这段结构体定义,目的是先按num相等进行去重,再按key降序排列。
struct node{
int num;
int key;
bool operator < (const node &b) const{
if(num == b.num)
return false;
else{
if(key != b.key)
return key > b.key;
return num > b.num;
}
}
};
然后我们测试以下四组输入数据




从第一组数据来看,我们的去重要求确实得到了满足,第二个插入的(3, 7)并没有进入set。但我们看到第二组数据,我们后插入(3, 7)set却没有正确的执行我们想象中的去重操作。首先我们要知道,set容器在判定已有元素a和新插入元素b是否相等时,是这么做的:
1)将a作为左操作数,b作为有操作数,调用比较函数,并返回比较值
2)将b作为左操作数,a作为有操作数,再调用一次比较函数,并返回比较值。
如果1、2两步的返回值都是false,则认为a、b是相等的,则b不会被插入set容器中
如果1、2两步的返回值都是true,则可能发生未知行为
按照我们结构体定义的优先级,(3, 7)没有被正确去重,只能说明在插入时它并没有与(3, 1)进行比较,而是与其他元素比较后直接确定了大小顺序就插入了。结合第三组数据,难道set插入时是按顺序从头比较吗?显然不是的,因为第四组数据我们把(2, 4)换成(3, 4)就能成功去重了。所以(2, 4)的插入应当影响了set中数据存储的结构。
而我们又知道set底层使用红黑树实现的。所以我们做出如下猜想验证三,四组数据的结果。
对于第三组数据,首先插入(3, 1)为根节点,后插入(3, 7)与根节点比较发现重复,不插入。再插入(1, 2)由于我们按key的大小排序(1, 2) > (3, 1)因此放到右子节点。(2, 4)同理,放到(1, 2)的右子节点。此时,树的平衡性丧失,所以要进行左旋使树重新平衡。左旋后根节点为(1, 2),左子节点(3, 1), 右子节点(2, 4)。再插入(3, 7)时,先与根节点比较,发现大于,后(3, 7)进入右子树,与(2, 4)比较后就直接确定顺序了,不会与(3, 1)比较,这就导致了无法正确去重。
而对于第四组数据,我们可以发现,在插入(1, 2)后,树的平衡性并没有丧失,所以根节点还是(3, 1)所以后插入的元素还是会和根节点进行比较,从而能够正确的去重。
为了进一步验证,我们可以将后插入的(3, 7)换为(3, 0)这样它在与左旋后的根节点(1, 2)比较后就会进入左子树与(3, 1)比较,从而能够被正确去重。实验数据如下:

发现(3, 0)确实没有被插入,因此我们的猜想的到了验证。
其实出现这种情况的本质原因是我们判断重复和排序的关键字不同。导致num相同的元素可能因为与根节点key的大小关系不同而被分到两个完全不同的子树中去。而如果我们如果将重复和排序的关键字换成如下相同的样子:
struct node{
int num;
int key;
bool operator < (const node &b) const{
if(num == b.num)
return false;
return num > a.num;
return key > a.key;
}
};
则不会出现无法去重的情况。因为在后者的情况下,只要num相同,与其他元素的大小关系就会确定,就会被存储在红黑树中的相同位置。
从另一个角度说,我们选择set,是因为它有每个操作log级别的优秀复杂度。而log级别的插入操作不可能做到和所有元素都进行比较。
关于set实现结构体自动去重原理的推论的更多相关文章
- 由结构体成员地址计算结构体地址——list_entry()原理详解
#define list_entry(ptr, type, member) container_of(ptr, type, member) 在进行编程的时候,我们经常在知道结构体地址的情况下,寻找其中 ...
- 10 结构体和类 - —— 《Swift3.0 从入门到出家》
Swift中的面向对象5个要素:枚举.结构体.类.协议.扩展 面向对象研究的是对象,完成一件事情需要多个对象参与,是生活的映射 Swift中结构体和类非常相似,也就是结构体能完成类的所有功能.结构体是 ...
- 结构体(struct)
结构体 结构体是将不同类型的数据按照一定的功能需求进行整体封装,封装的数据类型与大小均可以由用户指定. 1 结构体的声明.定义及初始化 1.1 声明结构体类型 struct 结构体名 { 成员列表: ...
- 【C/C++编程入门学习】C语言结构体硬核玩法分享,一切皆是数据!
前言 对于结构体的应用太多了,今天这篇文章我主要为大家总结平时关于结构体的一些独特硬核小技巧,对于结构体更多优秀的编程表现,如果你对结构体的基础知识还不具备的话得回头看一下专栏教程或者自己找本书籍学习 ...
- Delphi结构体的扩展,可以自动初始化,反初始化,自定义拷贝函数.
转载:http://www.raysoftware.cn/?p=518&utm_source=tuicool 恭贺Delphi XE7诞生,Delphi XE7在编译器内部集成了我之前所实现的 ...
- 【原】结构体包含CString类型成员变量出错的原理
问题如下:我定义了如下的一个结构体: typedef struct{ CString csText;}MyStruct; 并有如下的程序段1:MyStruct * p=NULL;p=(MyStru ...
- C语言中处理结构体的原理
汇编中有几种寻址方式,分别是直接寻址:(ds:[idata]).寄存器间接寻址(ds:[bx]).寄存器相对寻址(ds:[bx + idata].ds:[bx + si])基址变址寻址(ds:[bx ...
- C结构体中数据的内存对齐问题
转自:http://www.cnblogs.com/qwcbeyond/archive/2012/05/08/2490897.html 32位机一般默认4字节对齐(32位机机器字长4字节),64位机一 ...
- C#中的结构体与类的区别
经常听到有朋友在讨论C#中的结构与类有什么区别.正好这几日闲来无事,自己总结一下,希望大家指点. 1. 首先是语法定义上的区别啦,这个就不用多说了.定义类使用关键字class 定义结构使用关键字str ...
- 字符设备驱动1:新的方式添加cdev + 在open函数中将文件私有数据指向设备结构体
本例中,驱动入口处,使用cdev_add添加驱动,这点也可与字符设备驱动0:一个简单但完整的字符设备驱动程序对比一下. 另外主要讲xx_open实现文件私有数据指向设备结构体. 引子: 偶然看到,在j ...
随机推荐
- Python编写html文件
背景:部门需要发送周报.月报,每次都需要去数据库导出数据整理统计发送给领导,人工操作显得繁琐且费时间. 1.可以定时用python将数据库查询数据结果写成html文件,达到浏览器访问的效果,定时发送给 ...
- 【Vue2】Direct 指令
1.内容渲染指令 1.插值表达式 2.V - TEXT 3.V - HTML <!DOCTYPE html> <html lang="en"> <he ...
- 【Uni-App】UniApp转微信小程序发布应用
参考地址: https://www.jianshu.com/p/a77b73f329e4 第一步,把原始Uni-App项目,转成微信小程序项目 点[发行]-- [小程序-微信(仅适用uni-app)] ...
- 【SpringBoot】13 数据访问P1 整合Jdbc
SpringBoot与数据访问概述: 对于数据访问层,无论是SQL还是NOSQL,Spring Boot默认采用整合Spring Data的方式进行统一处理, 添加大量自动配置,屏蔽了很多设置.引入各 ...
- 数字人 —— 虚拟人 —— Inworld AI用生成式AI——生成式游戏NPC
相关: https://www.ithome.com/0/756/603.htm https://baijiahao.baidu.com/s?id=1774732295233220838 https: ...
- 【转载】 Docker-关于docker cpu的限制后,实际效果的研究
原文地址: https://zhuanlan.zhihu.com/p/46275332 ================================================== 思考:我们 ...
- ubuntu:通过缺失的系统lib库文件查找所需要安装的package——根据lib文件查找所属的package包——命令:sudo apt-file search
参考: 使用apt-file,根据文件查找所需安装的软件包 ======================================= 使用 apt-file 命令可以通过lib文件名查找其所属的 ...
- volatile重要特性-可见性,避免指令重排序-案例讲解
1.背景 volatile 修饰的作用???? 什么是可见性?? 什么是指令重排序?? 2.可见性-案例 package com.my.aqs; /** * @Copyright (C) XXXXX技 ...
- maven项目打包时排除依赖包
1.背景 为了快速上传jar包到服务器,很多时候我们需要把依赖包单独独立出来,避免每次修改都传依赖包 2.实现方式 maven的pom文件,没有独立依赖包时配置如下: <build> &l ...
- MD5签名生成,字典排序,实际生产
1.背景 作用:防止请求参数篡改,限制请求时效性: 常用方式:md5签名 关键:签名Key 常用签名原串排列:字母顺序.key1=value1&key2=value2....key (注意:签 ...