C++之map与set的使用与原理+拓展avl树(详解)

前言:map和set是C++里面的两种常用STL容器,他们被设计为关联式容器,这两个容器存储的元素都是唯一的,并且每个元素与键(key)相关联,简单来说就是两个存储不重复元素的容器。那就有人有疑问了,我们已经有了vector和list等容器了,为什么还要这两个容器呢?这个问题算是问到点子上了,假设一个场景,我们已经插入了很多个元素,我们突然需要找到这个元素的位置,我们该怎么办,如果是vector等容器,我们一般就会遍历,这样子的查找效率是O(N),有没有办法降低查找效率呢?当然有,就是把元素存储在map或者set里面,它们底层是平衡搜索树,查找效率在logN到2logN之间,是不是快了很多,在很多查找需求频繁的场景是不是很实用?

目录

一,set

1)set的使用

2)set的原理

3)二叉排序树

4)二叉平衡树(AVL)

1)右单旋

2)左单旋

3)左单旋加右单旋。

4)右单旋加左单旋

5)set如何实现的

二,map

1)map的内部构造

2)map成员函数

三,multiset与multimap

1)介绍


一,set

1)set的使用

先看一张来自C++官网关于set的介绍

其中的T是指key,也就是你插入的元素,compare是比较函数,允许自定义,如果不自定义的话,默认升序,Alloc是内存池开辟空间。

再看看构造函数

可以看到有两种构造方法,第一种是插入一个元素,也是图中最后一行,中间的是迭代器构造。

现在我们来看一个具体的例子。

vector<string> v={"hello","word"};
string s="hello";
set<string> s(v.begin(),v.end());
set<string> s1(s);

当然set也支持很多操作,有很多的成员函数

由于这里面的内容够多,并且与我以前的博客有较大重叠,因此这里不做详细讲解,但这里提供官网,想要仔细了解可以前往官网,如果不会阅读的话可以看我往期博客,里面介绍了如何阅读官网英文文档

set - C++ Referenceicon-default.png?t=N7T8https://legacy.cplusplus.com/reference/set/set/?kw=set

2)set的原理

我之前说了,set底层是平衡搜索二叉树(红黑树),我们在这里给大家介绍一下,平衡二叉树在这里的工作原理。

3)二叉排序树

首先我们要了解,二叉排序树:

二叉搜索树又称二叉排序树,它或者是一棵空树,或者是具有以下性质的二叉树:
若它的左子树不为空,则左子树上所有节点的值都小于根节点的值
若它的右子树不为空,则右子树上所有节点的值都大于根节点的值
它的左右子树也分别为二叉搜索树

当达到这些原则的时候,我们在二叉排序树里面查找某个元素是很快的,每次我们都能排出另一个结点及其以下的所有结点不包含我们想要的结果。

4)二叉平衡树(AVL)

但是也存在缺点,树的形状很大程度上影响了我们的查找效率,例如下图

当树的形状变成这样子,我们查找元素就和遍历没多大差距了,那怎么办呢?这时候平衡二叉树就横空出世了,我们在每棵树上加一个平衡因子,也就是类似一个整形,用来干嘛呢?平衡因子来记录左右子树的高度差,当高度差大于2的时候就需要我们来调整了。那我们该怎么调整呢?

1)右单旋

我们来看第一种情况。(我们是边插入边调整,当平衡因子绝对值等于2的时候开始解决不平衡问题)

注:h为未知数,节点上方的数字是平衡因子,这是把某种情况具体抽象出来了,60并不一定是根节点

在这种情况下,平衡因子因为插入的新结点而变的不平衡了,我们该怎么调整呢?

解决方案就是右单璇,60的左指针指向30的右孩子,30的右指针则指向60,然后把原本60父亲结点指向60的指针改为指向30,便完美解决了。(注意不要访问空指针)

但是这个有前提条件,首先最开始的60结点的平衡因子要是-2,而且60左孩子结点的平衡因子要是-1。那如果是1呢?这个就比较麻烦了我们放在最后面讲。

2)左单旋

现在我们继续看另一种情况

这个时候我们要作出与右单旋方法,相反的旋转方法,左单旋。

我们将30左指针由指向60改为指向60的左孩子结点,60的右指针指向30,再将原本30父亲指向它的指针改为指向60。

前提条件,平衡因子为2,并且右孩子的平衡因子为1。

3)左单旋加右单旋。

刚刚细心的人已经发现了,我们还有两种情况,平衡因子还有两种情况,本身平衡因子为-2,但左孩子结点平衡因子为1。这种情况我们还能不能单纯的右旋吗?

这样子我们会发现我们旋转了个寂寞。那该怎么办呢?

我们首先要将这个30再细分一下

这个时候我们先把30进行一个左单旋

很多人这时候可能问,假如是新插入结点的位置的c呢?我的回答是毫不影响,因为c高度就算是h,c这边的子树也不可能比a高,这个时候我们就把树调整为了上面讲的第一种情况,我们这个时候就可以不看30了,我们来看60,把60进行右单选得到平衡二叉树。

4)右单旋加左单旋

还有最后一种情况

相信认真听了前面的老铁已经发现了解决方法,没错就是右单旋加左单旋

5)set如何实现的

set的底层就是类似于平衡搜索二叉树,每次插入元素都会对树的形状进行排序和调整,那如果利用迭代器遍历的时候它是怎么得到我们想要的顺序呢?当然是中序啦,大家仔细分析中序就是有序。很多人又有疑问,那迭代器++是什么情况,它们的的地址是不连续的,这个是因为set的底层对这些运算符重载了,大家对这个有兴趣可以看我往期的STL之List的那篇文章,我在那里详细讲了。

二,map

1)map的内部构造

map和set的原理差不多,底层但是平衡搜索树,但是map是KV型容器,也就是通过某一个值key来找到value这个值,举个例子。

在学校食堂吃饭的时候,我们需要通过校园卡来付费,在我们把卡放在付款机上面的时候,首先它们会通过查找我们的姓名,学号等信息来判断我们是不是学生,卡了有没有足够的钱,而这些信息就是key,通过key查找到我们的余额,并且扣费,这是目的,也就是value。

  key其一个帮助查找的工具,value才是目的,当然也不是绝对的,可以根据我们的需求来使用。那如何使用呢?

首先我来看函数参数

除了T代表value,其他应该不需要我介绍吧

直接快进到构造函数

同样有两种构造方法和直接自定义compare函数和内存池。图中最后一行不要以为是一个参数,实际是有两个参数(key,value),之所以之显示一个参数,是因为被pair包装起来了,pair是什么呢?

pair是库里面的一个容器,T1代表key,key不可修改,T2代表value,允许修改。

再看看pair里面的构造

成员变量有first和second分别代表key和value。

另一种方法就是我们熟悉的迭代器了,要注意map迭代器返回的是pair,pair里面包括key和value

map<string,string> m({"apple","苹果"});//隐式构造
m.insert(make_pair("string","字符"));//make_pair库函数构造
map<string,string> m1(m.begin(),m.end());//迭代器构造
for(auto e:m)
cout<<m.first<<":"<<m.second<<endl;//注意迭代器直接解引用并不是值,而是pair
2)map成员函数

这里都是构造函数,析构函数,拷贝构造函数,迭代器

这里面我们重点讲一下,【】运算符重载。先来看底层代码

它实际很简单,它调用了insert函数,为什么呢?因为insert不仅只有插入的作用,还有查找的功能,如果已经存在,它就会返回找到的结点,如果没有就会插入一个,然后访问second并返回它的引用就达到了我们想要的效果,是不是很妙。

3)map的原理

它的原理与set一样,底层同样是红黑树,大家可以参考上面的set。

三,multiset与multimap

1)介绍

multiset,multimap与set,map的差别在允不允许有重复元素,在前面我们说了map和set不能用于重复元素,如果insert重复元素就不会插入,而是会返回已经存在的元素,[]运算符重载就利用了这个特点。而multiset与·multimap是支持有重复元素,我给大家画一个有重复元素的图,方便大家理解

只要中序是有序的,那么插入的元素就没问题。

2)map与set中迷惑成员函数?

大家有没有在前面map与set的成员函数图里面发现这个图,很多人会有疑问,map,set里面没有重复元素,这个统计元素个数的函数有什么用?其实count函数并不是为map与set准备的,而是为multiset与multimap准备的,因为multiset与multimap里面允许有重复元素。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/447112.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

PostgreSQL中In, Exists在SQL查询中到底有无区别

前言 SQL查询当中&#xff0c;In和Exists子查询到底有无区别&#xff1f;记得很多年以前&#xff0c;确实是有相关的使用戒条的&#xff0c;或者说存在一些使用的惯用法。试图完全抹开两者的区别&#xff0c;就有点过了。 两者的主要区别&#xff1a; 从目的上讲&#xff0c…

微信小程序开发系列(二十七)·小程序生命周期详细介绍

目录 1. 小程序生命周期介绍 2. 应用生命周期 3. 页面生命周期 1. 小程序生命周期介绍 一个小程序完整的生命周期由 应用生命周期、页面生命周期 和 组件生命周期 三部分来组成。 应用生命周期&#xff1a;是指应用程序进程从创建到消亡的整个过程。 小程序的生命&#…

python——By.ID/By.NAME

一、通过元素的id属性来定位元素 from selenuim import webdriver from selenuim.webdriver.common.by import Bydriver webdriver.Chrome() driver.maximize_window() driver.get(http://xxx) time.sleep(3)# 通过By.ID找到唯一页面元素后&#xff0c;输入abcd driver.find_…

简析内部审计数字化转型的方法和路径

简析内部审计数字化转型的方法和路径 内部审计是一种独立的、客观的确认和咨询活动&#xff0c;包括鉴证、识别和分析问题以及提供管理建议和解决方案。狭义的数字化转型是指将企业经营管理和业务操作的各种行为、状态和结果用数字的形式来记录和存储&#xff0c;据此再对数据…

华为OD机试 - 模拟数据序列化传输(Java JS Python C C++)

题目描述 模拟一套简化的序列化传输方式,请实现下面的数据编码与解码过程 编码前数据格式为 [位置,类型,值],多个数据的时候用逗号分隔,位置仅支持数字,不考虑重复等场景;类型仅支持:Integer / String / Compose(Compose的数据类型表示该存储的数据也需要编码)编码后数…

VSCode安装C语言编译环境

目录 一、在vscode下载C/C扩展 二、配置gcc环境 1.访问网站&#xff1a;https://sourceforge.net/projects/mingw-w64/files/ 2.解压并复制bin目录 三、配置gcc环境 四、在cmd检查是否配置成功 五、vscode配置gcc环境 六、在vscode运行C文件 运行.c代码 七、在vscode运…

JVM-2

目录 1.虚拟机类加载机制 2.JVM常见回收算法 2.1标记清除算法 2.2标记整理算法 2.3标记复制算法 3.分代垃圾回收机制 新生代收集 第一次垃圾回收 第二次垃圾回收 第N此垃圾回收 老年代收集 4.补充 Stop The World Java的对象结构 1.虚拟机类加载机制 双亲委派模式…

理解STM32的低功耗模式

低功耗模式简介 TM32的低功耗模式是特别设计来减少微控制器在不活跃状态下的能耗。这些模式允许STM32在保持核心功能的同时尽可能减少电力消耗&#xff0c;适合用在电池供电或需长期运行的场景。理解各种低功耗模式如何节能&#xff0c;主要包括以下几个方面&#xff1a; 关闭…

DenseNet笔记

&#x1f4d2;from ©实现pytorch实现DenseNet&#xff08;CNN经典网络模型详解&#xff09; - 知乎 (zhihu.com) 是什么之 DenseBlock 读图&#xff1a; x0是inputH1的输入是x0 (input)H2的输入是x0和x1 (x1是H1的输出) Summary&#xff1a; 传统卷积网&#xff0c;网…

C语言——函数指针——函数指针数组 (详解)

函数指针数组 函数指针数组的作用 函数指针数组是一个数组&#xff0c;其中的每个元素都是一个函数指针。函数指针是指向函数的指针变量&#xff0c;可以用来调用相应的函数。函数指针数组的作用是可以根据需要动态地选择并调用不同的函数。 函数指针数组的使用场景有很多&…

C++ Standard Library简介

目录 ​编辑 引言&#xff1a; Boost C Libraries&#xff1a;截至本文编写时间最新版本 1.84.0 STL源码分析&#xff1a; C STL源码分析&#xff08;一&#xff09;&#xff1a;STL体系结构和一些基础知识 libc&#xff1a; 概述 libc 入门 现状 平台和编译…

BetterDisplay for mac V2.2.5 强大的mac显示器管理开源工具

BetterDisplay是Mac OS 一个很棒的工具&#xff01; 它允许您将显示器转换为完全可扩展的屏幕 管理显示器配置覆盖 允许亮度和颜色控制 提供 XDR/HDR 亮度升级&#xff08;Apple Silicon 和 Intel Mac 上兼容的 XDR 或 HDR 显示器的额外亮度超过 100% - 多种方法可用&#x…

【Linux】文件系统扩展——软硬链接

目录 对文件建立软硬链接 软链接 硬链接 对文件建立软硬链接 对 log 文件建立软链接&#xff1a; ln -s log log.soft.link 对 test 文件建立硬链接&#xff1a; ln test test.hard.link log.soft.link 和 test.hard.link 在 Linux 中都只是文件名&#xff0c;为了方便…

springboot整合shiro的实战教程(三)

文章目录 授权实现0.页面资源授权2.代码授权方式3.方法调用授权4.授权数据持久化5. 创建dao方法6.实现service接口7.修改自定义realm 授权实现 0.页面资源授权 <%taglib prefix"shiro" uri"http://shiro.apache.org/tags" %><shiro:hasAnyRoles…

Java对接(BSC)币安链 | BNB与BEP20的开发实践(二)BNB转账、BEP20转账、链上交易监控

上一节我们主要是环境搭建&#xff0c;主要是为了能够快速得去开发&#xff0c;有些地方只是简单的介绍&#xff0c;比如ETH 、web3j等等这些。 这一节我们来用代码来实现BNB转账、BEP20转账、链上交易监控 话不多说&#xff0c;我们直接用代码实现吧 1. BNB转账 /*** BNB转…

后端八股笔记-----mysql

Mysql 聚合查询————可以用增加一个实例表解决 多表查询————可以优化sql语句进行查询 &#x1f446; 显示Using index condition的话 说明是有优化的空间 &#x1f446;唯一索引指的是类似主键这种数据内容唯一的属性 &#x1f446;图中的最后一个sql的索引…

Math类 --Java学习笔记

Math 代表数学&#xff0c;是一个工具类&#xff0c;里面提供的都是对数据进行操作的一些静态方法 Math提供的常用方法

Kafka的分区机制

Kafka的分区机制是其核心功能之一&#xff0c;旨在提高可扩展性和并行处理能力。下面概述了Kafka分区的基本概念和工作原理&#xff1a; Kafka分区基本概念 分区&#xff08;Partition&#xff09;&#xff1a;Kafka中的主题&#xff08;Topic&#xff09;可以细分为多个分区…

OD_2024_C卷_200分_7、5G网络建设【JAVA】【最小生成树】

package odjava;import java.util.Scanner;public class 七_5G网络建设 {public static void main(String[] args) {Scanner sc new Scanner(System.in);int n sc.nextInt(); // 基站数量&#xff08;节点数&#xff09;int m sc.nextInt(); // 基站对数量&#xff08;边数&…

【Docker】在 Ubuntu20.04 上配置 Docker 开发环境

【Docker】在 Ubuntu20.04 上配置 Docker 开发环境 1 安装 Docker2 加入 Docker 用户组 1 安装 Docker 参考文档: Link 卸载以避免冲突 for pkg in docker.io docker-doc docker-compose docker-compose-v2 podman-docker containerd runc; do sudo apt-get remove $pkg; done设…