关于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 ...
随机推荐
- 【Eclipse】下载安装(Windows)
Eclipse的下载和安装 官网地址:https://www.eclipse.org/downloads/ 刚用的时候选了安装版,然后安装到一半就失败了 建议点下面的解压包下载,解压即用 注意看框线的 ...
- 【DataBase】局域网访问Windows系统下的MySQL8
Windows服务主机已经安装好MySQL8并且配置了用户密码 MySQL8更改用户密码: ALTER USER 'root'@'localhost' IDENTIFIED BY 'password' ...
- 【MySQL】Windows-5.7.30 解压版 下载安装
1.Download 下载 mysql官网: https://dev.mysql.com/ 找到download点击进入下载页面: https://dev.mysql.com/downloads/ 找 ...
- 强化学习入门书籍《DeepReinforcementLearningHands-On-SecondEdition》
前段时间在网上买了本强化学习入门的书籍,即<Deep-Reinforcement-Learning-Hands-On>,虽然是影印版的,但是感觉还是可以看看的,说的也蛮易懂的,感觉比现在市 ...
- maven项目打包时排除依赖包
1.背景 为了快速上传jar包到服务器,很多时候我们需要把依赖包单独独立出来,避免每次修改都传依赖包 2.实现方式 maven的pom文件,没有独立依赖包时配置如下: <build> &l ...
- EF Core连接PostgreSQL数据库
PostgreSQL数据库介绍 PostgreSQL是一个功能强大的开源对象关系型数据库管理系统(RDBMS).最初于1986年在加州大学伯克利分校的POSTGRES项目中诞生,PostgreSQL以 ...
- 告别卡顿,畅享GitHub:国内开发者必看的五大加速访问与下载技巧
告别卡顿,畅享GitHub:国内开发者必看的五大加速访问与下载技巧 本文介绍了五种加速在国内访问和下载 GitHub 的方法,包括:使用 Gitee 平台加速克隆代码.修改 hosts 文件.使用油猴 ...
- mybatis打印sql
转
我们在使用mybatis开发过程中,经常需要打印sql以及输入输出,下面说一下mybatis结合log4j打印sql的. 1.添加mybatis配置 mybatis的日志打印方式比较多,SLF4J | ...
- 使用 preloadComponents 进行组件预加载
title: 使用 preloadComponents 进行组件预加载 date: 2024/8/18 updated: 2024/8/18 author: cmdragon excerpt: 摘要: ...
- 如何诱导AI犯罪-提示词注入
我们用到的大模型基本把政治类信息.犯罪相关信息都已屏蔽.但是,黑客依旧可以使用提示词诱导和提示词注入的方式对大模型进行攻击. 1.提示词诱导 如果直接让AI提供犯罪过程,AI会直接拒绝.虽然AI对于大 ...