首页 > 其他分享 >关于set实现结构体自动去重原理的推论

关于set实现结构体自动去重原理的推论

时间:2024-10-10 11:45:40浏览次数:8  
标签:推论 set return 插入 num key 原理 节点

转自本人博客,原文链接

先说结论

在每个操作均为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,return,插入,num,key,原理,节点
From: https://www.cnblogs.com/Starry---sky/p/18456002

相关文章

  • Linux网络(二)——socket、BIO、epoll原理
    二、内核如何与用户进程协作//创建Socket的c语言程序...intmain(){ intsk=socket(PF_INET,SOCK_STREAM,0); //忽略bind和accept ... } 2.1读取视角:Linuxsocket结构2.1.1socket源码//代码:/include/linux/net.hstructsocket{ socket_state state; shor......
  • 链表Set_LinkList(建立)
    用单链保存集合元素,元素由键盘输入。输入以-1结束,将所建链表打印输出。链表结构如下图所示:提示:1.链表中数据元素为整型,typedef int ElemType;2.用结构体自定义链表结构Set_LinkList ;3.初始化链表函数init(),该函数可创建空链表L,返回L的头指针地址;4.链表插入结点函数......
  • 一文详细解读自动驾驶与机器人所需各种传感器的原理与优缺点
    更多优质内容,请关注公众号:智驾机器人技术前线1.激光雷达(LiDAR)工作原理:激光雷达通过发射短脉冲的激光束,测量光束从目标物体反射回来所需的时间(即飞行时间),从而计算出物体的距离。LiDAR通常通过旋转激光发射器来获取360度的视场,生成点云数据,反映周围环境的三维信息。优势:高......
  • 前端网页工作原理
    1.简要介绍        先安装好Ngnix或者Apache,接着把写好的网页文件放到指定目录,然后在浏览器中输入网址就可以打开网页了。        至于网页文件一般只有三种:html(骨架)、css(设置样式,大小、颜色、位置等)、javascript文件一般是处理交互或者与后端通信的......
  • abc174F Range Set Query
    给定数组A[N],有Q个询问,每个询问给出l[i]和r[i],问区间[l[i],r[i]]内有多少个不同的数?1<=N,Q<=5E5;1<=A[i]<=N;1<=l[i]<=r[i]<=N分析:对询问按右端点从小到大排序,然后从左到右依次处理每个A[i],将下标i的位置置为1,如果前面出现过A[i],则把上一次出现的位置置为0,然后处理右端点为i的......
  • java中Set的介绍与实现:HashSet、LinkedHashSet、TreeSet
    在Java中,Set是Collection接口的一个子接口,它是一个不包含重复元素的集合,且通常不保证维护元素的有序或迭代顺序。Set接口主要用于确保集合中每个元素的唯一性。Set接口的主要方法:booleanadd(Ee):将指定的元素添加到此集合中(如果它尚未在集合中)。booleanremove(Objec......
  • 软考信安总结-第十三章 网络安全漏洞防护技术原理与应用
    网络安全漏洞概述漏洞概念漏洞又称脆弱性,漏洞=系统自身缺陷根据补丁状况,分为普通漏洞和0day漏洞漏洞威胁主要安全威胁有:敏感信息泄露、非授权访问、身份假冒、拒绝服务解决漏洞问题方法:漏洞检测、漏洞修补、漏洞预防网络安全漏洞分类与管理漏洞来源非技术安全漏洞......
  • 《 C++ 修炼全景指南:十四 》大数据杀手锏:揭秘 C++ 中 BitSet 与 BloomFilter 的神奇性
    本篇博客深入探讨了C++中的两种重要数据结构——BitSet和BloomFilter。我们首先介绍了它们的基本概念和使用场景,然后详细分析了它们的实现方法,包括高效接口设计和性能优化策略。接着,我们通过对比这两种数据结构的性能,探讨了在不同应用场景中的选择依据。最后,博客还涵盖......
  • 在K8S中,DaemonSet类型的资源特性有哪些?
    在Kubernetes(K8s)中,DaemonSet是一种特殊的控制器资源对象,其核心特性和用途使得它非常适合用于在集群的每个节点上运行守护进程或服务。以下是DaemonSet类型的资源特性的详细阐述:1.确保每个节点上运行Pod副本节点级部署:DaemonSet确保集群中的每个节点(或满足特定条件的节点)上都运......
  • PTA JAVA语言 面向对象程序设计 作业二 6-3 Person类 构造Person类。包括姓名(name),性
    6-3Person类 谢谢大佬关注,不定期分享学习笔记,希望大佬能多多支持,三连必回单位 山东科技大学构造Person类。包括姓名(name),性别(sex)和年龄(age)。提供所有属性的set和get函数,提供print函数打印其信息输入描述:姓名(name),性别(sex)和年龄(age)输出描述:用户信息裁判测......