关于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 ...
随机推荐
- 七天.NET 8操作SQLite入门到实战 - 第七天Blazor学生管理页面编写和接口对接(3)
前言 本章节我们的主要内容是完善Blazor学生管理页面的编写和接口对接. 七天.NET 8 操作 SQLite 入门到实战详细教程 第一天 SQLite 简介 第二天 在 Windows 上配置 S ...
- ORA-01658创建表或索引报错分析
一.报错信息 某项目最近在 SQL Loader 导数据时偶尔会报错,类似如下: SQL loader ORA-01658 unable to creale INITIAL extent for se ...
- 【Java】Reflection 反射机制 02获取类的一切
先创建一个可演示的类 注解类 package cn.dai.Reflection.demo; import java.lang.annotation.ElementType; import java. ...
- 【JDBC】Extra01 Oracle-JDBC
关于驱动包依赖: 官网提供的地址: https://www.oracle.com/database/technologies/jdbc-drivers-12c-downloads.html Maven ...
- 最快视频转绘-AnimateDiff-Lightning
最快视频转绘-AnimateDiff-Lightning Video-to-Video Generation AnimateDiff-Lightning 非常适合视频到视频的生成.使用 Control ...
- 不符合自身利益的科学讨论是否应该得到尊重—— 读《自家员工质疑Jeff Dean领衔的Nature论文被解雇,谷歌:我们彻查了,质疑不符合标准》有感
读了一篇博文<自家员工质疑Jeff Dean领衔的Nature论文被解雇,谷歌:我们彻查了,质疑不符合标准>,其问大致是说Google提了一篇使用reinforcement learnin ...
- java多线程之-CAS无锁-unsafe理解
1.背景 这一节我们来学习一下unsafe对象 2.案例 1.自定义一个获取unsafe对象的类 package com.ldp.demo07Unfase; import sun.misc.Unsaf ...
- [天线原理及设计>基本原理] 1. 辐射机制
1. 辐射机制 1.1. Single Wire 单线 如果电荷不移动,则不会产生电流,也不会产生辐射. 如果电荷以匀速移动: a. 如果电线是直的,并且范围是无限的,则没有辐射. b. 如果电线弯曲 ...
- Camera | 8.让rk3568支持前后置摄像头
一.目标 本文主要目标是,支持前置摄像头0v5648.后置摄像头ov13850,以及移植过程遇到的一些小问题的解决. 1. 摄像头连接图 参考上图,摄像头详细信息如下: 2个摄像头均连接在I2C通道4 ...
- AutoMaper使用
使用 AutoMapper 进行赋值 一. 什么是 AutoMapper AutoMapper是对象到对象的映射工具.在完成映射规则之后,AutoMapper可以将源对象转换为目标对象. 二. Aut ...