map和set的使用

序列式容器和关联性容器

首先序列式容器和我们之前学的线性表很相似,序列式容器的功能就只是单纯的储存数据。序列式容器例如:vactor/list/deque等等

而关联式容器则并不单纯的储存数据,数据之间式存在关联关系的,有了这个关联关系我们才能更好地去做查找。关联式容器由map/set等等。

两者的的区别,我要往map中插入一个数据,我能不能保证这个节点一定插在根节点的左子树上呢?很明显是不能的,而我想往vector的中部,头部,尾部任意一个位置插入数据都是可行的。

所以我们要知道关联式容器,并不是单纯的为了储存数据。

set接口简介

set是包含在头文件set中的

#include<set>

首先我们来看一下set的原型:

可以看到set的原型是一个类模板,而这里的第一个模板参数给了一个T,这就是我们在二叉搜索树那里学的key。

然后Compare是一个仿函数,因为set的底层是一个搜索树,而搜索树是需要去比较key的大小的。然后最后一个是空间配置器。空间配置器是用于给容器申请内存的(我们暂时不用管),而仿函数内部已经默认有了一个所以我们也不需要管。所以我们在使用set的时候,只用给第一个即可。下面我们来看set的接口。

首先就是默认成员函数,构造析构,和拷贝构造。

详细的构造

可以看到构造也是支持无参,迭代器,和传另一个set构造的。

至于拷贝和析构自然也是一样的。

这里我们需要知道的一点就是,因为set的底层是一颗树,所以拷贝和析构这颗树的代价是很大的。树的节点越多影响也就越大。

下面是迭代器

对于迭代器,和我们之前学的容器一样和正常的去使用就可以了。这里因为set是支持迭代器的,自然也就支持范围for了。

下面是关于set容量相关的东西。

然后就是set对节点修改的接口了

可以看到和其它容器不同的是set是没有头插,尾插和任意位置插入的概念的,因为set是一个关联式容器,而关联式容器各个数据之间是存在联系的。

然后下面就是查找

总结一下:set是一个key搜索模型的容器,是用于判断在不在的。所以不存在修改节点的概念,所以set具有增删查的功能但不具备改的功能。

set接口的简单使用

insert

我们先来看insert使用需要传递的参数

可以看到insert支持三种模式第一种直接插入一个值,第二个在某个位置插入一个值(尽量不要使用这种方式,使用可能会出现很多的坑,然后回让你的set搞出问题)第三个支持插入一个迭代器区间。

在日常使用中我们基本上使用的是第一种插入方式,第三个偶尔会使用后。

我们来使用第一种方式插入5个数据:

从遍历出来的结果就可以看到set的特点,那就是set遍历走完是一个升序。这说明set底层二叉搜索树走的是一个中序遍历。

set还有一个特点

当我们插入一个之间已经存在于树中的值时。

会发生什么呢?

可以看到我在下面重复插入的5,3和1是没有进入到set中的。

虽然set的底层是一棵红黑树,但是即使是红黑树,那也存在一个前提就是它是一棵搜索树既然是搜索树那么对于重复的值在key模型下是不会插入的(会返回false)。

但我们去去上面的那张图片上看insert返回的是一个pair,在pair中确实存在一个bool类型的参数

那么这个pair是个什么东西呢?

pair类

可以看到这个pair是一个类,这个类里面存在两个值,具有两个值。

下面是在sgi中对于pair的定义

所以这里的pair就只是一个模板类,它具有两个成员变量(first,secend)。而这两个成员的具体类型则由我们自己传递。

那么我们现在去看insert的返回值就能够理解了,可以看到insert返回了一个pair对象,而这个pair对象的first是一个迭代器,而第二个类型是一个bool值。那么如果我想在上面接收insert的返回值要怎么写呢?

那么这里就存在了一个问题那就是为什么在上面的pair那里能够直接使用iterator,而我们这里需要指定类域呢?

首先这个insert实现的时候,肯定是在set的内部实现的,那么此时这里的iterator,本身便在类域中了,此时这个insert函数是不会受访问限定符的限制的(这个iterator它知道是该类域中的iterator),但是此时我们在这里使用的时候,肯定是在类外的。所以这里需要指定类域。

那么为什么要这么做呢?为什么不直接返回一个false,这个在下面使用map的时候会解释。

这里如果你不想使用这一大串类型声明,也可以使用一个auto,但是这里也可以看到auto的一个坏处,这里我们因为看来insert函数的声明,已经pair类型的定义,所以我们在使用a的时候知道,a中的第二个参数才是bool值,而第一个是一个迭代器,如果使用auto,但是却不去看insert函数的声明的话,这里大概率是无法得到bool值得。

这也正是auto得坏处,虽然能够让代码变得简洁,但是如果不熟悉接口的话容易出错。

至于范围for肯定是可以使用的,因为范围for的底层是一个迭代器,而栈就没有范围for,因为栈没有迭代器。

对于迭代器的接口没有什么好说的,直接使用即可。

erase

下面我们简单的看一下erase函数的使用

也是支持三种方式位置,值和迭代器区间删除,那么元素的迭代器从哪里来呢?自然是find来的。

find返回的就是一个迭代器。

那么如果find没有找到这个元素会返回什么呢?

可以看到会返回end。

所以可以这么使用:

那么这里我们思考一个问题,如果erase的时候我们传递的不是一个有效的迭代器,erase这里会发生什么呢?

可以看到这里就直接崩溃了。

所以如果传递的是一个迭代器,一定要判断是否找到了。

在上面我们看到除了迭代器之外也可以直接使用值来删除,那么如果这里删除一个不存在的值会有什么影响吗?

可以看到没有任何的影响。

总结一下对于erase来说,如果传递的是迭代器,那么必须要保证这个迭代器的值是有效的,而如果传递的是一个值,那无所谓存在就删除,不存在那也没有影响。那么这里如果传递的是一个值,我们如何知道他是删除成功了的呢?

我们也是可以知道的。

可以看到这个erase会返回一个size_type,而这个size_type就是一个无符号整型的重命名,所以这里如果删除成功返回的是一个1,不然返回的是0。那么这里为什么是size_type而不是bool呢?这和下面的一个容器有关。

至于emplace系列,因为我暂时还没有学习到这里,所以就暂时不写了。

lower_bound

这个函数是给一个值然后返回一个迭代器。

至于实际意义:

我们直接看示例:

上面的示例lower_bound返回的是30的迭代器,而upper_bound返回的是70的迭代器,那么这是为什么呢?

因为凡是迭代器区间都是左闭右开的,只有返回的是70的迭代器,才能把我们输入的60的迭代器,包含进去。erase函数也是这样,不会删除70,因为左闭右开。

所以我们才能在下面的erase那里直接将30到60的数据全部删除。

那么如果是下面的这样呢?

这里lower_bound中的值是一个25,这里会返回什么呢?

可以看到这里的结果是不变的所以这里能够得出结论:

所以这里如果你在upper那里写一个65返回的依旧是70的迭代器。如果你给一个70,那么自然返回的就是80的迭代器了。

所以如果你想在set中寻找某一个范围内的值就可以使用lower_bound和upper_bound。

equal_range

那么这个接口的作用又是什么呢?可以看到这里返回的是一个pair。

我们来看下面的示例:

这里如果值是30那么这里返回的就是30和40的迭代器。

那么如果我们在equal_range中放的值是一个35,那么这里又会返回什么呢?

可以看到这里返回的是40

总结这里的first是返回大于等于val,而secend返回的是大于value的值。

这个函数对set其实没有太大的作用,但是对于另外一个容器是很有用的。

下面是swap函数,以及clear函数。swap函数自然就是在底层交换两颗树的根了,至于clear自然是将树中的值销毁。

那么最后还有一个函数count

count

这个函数会返回在set中value的个数,在set中value的个数就只有两种1个或者是0个。

而这里就可以使用count来判断在不在来了。

最后还有一个注意的点就是set是不支持修改的。

因为改了就可能不是搜索树了,key模式不支持更改,但是keyvalue模式是支持的。

multiset容器

我们在之前的set容器的时候,看到set容器时不支持重复值插入修改的,但是multiset是支持的。

这里你可以认为multiset是一棵变异的搜索树。

这里我们就可以总结一下set的功能就是排序+去重。

而multiset的作用就是排序。

那么这里一个相同的值来了以后multiset是怎么插入的呢?

这个相同的值是插入在了父节点的左边还是右边呢?

这个问题的答案是无所谓,你插入在左边还是右边都是一样的。

例如下图:

上面的这个树结构是会进行旋转(不平衡)的。

所以你插入在右边是可能旋转到左边,你插入在左边是可能旋转到右边,所以这里你插入在左边还是右边是无所谓的。

至于为何会旋转的,以及平衡的概念之后,我还没有学习。就不写了。

至于如果再插入一个5如何插入呢?可以直接就是大于走右边小于走左边,这样就会变成下图。

这样中序去走这两个5也是在一起的。

那么这里有一个问题,现在我在我上面的代码修改了一下让其拥有了好几个2,那么下面如果我使用find来找2返回的是哪一个2呢?修改后的插入。

可以看到返回的是中序的第一个2.所以总结就是:在multiset中如果存在多个值返回的是中序的第一个值。

所以我们就能够知道这个函数对于multiset的意义了

例如这里如果你想要删除multiset中所有的2.

那么这个函数就可以帮助你,因为这个函数的pair中first储存大于等于val的迭代器,刚好就能够找到最左端的2,而secend储存的是大于val的迭代器,自然就能找到最右端的2。最后自然就能够删除全部的2了。

所以这个接口是具有这个意义的。同理count接口最后返回的是一个无符号整型(返回val的个数),对于multiset也就具有了意义。

但是其实用这个去做erase也没有太大的意义,因为你直接使用erase(2),也能删除全部的2。删除了几个2也能从n中读出

总结所以如果你想要在multiset中找到一个值的存在的范围那么使用equal_range是可以的。

这也是为什么erase函数等等的返回值是这样的。

其余的接口这两个容器是差不多的

map的使用

map是包含在头文件map中的

#include<map>

首先map和set一样也是分成了两个一个是普通的map,一个就是multimap。

首先map是一个kv型的结构。

至于接口函数

和set是差不多的,但是多了一个operator[](这个很重要需要重点关注)。

下面我们来使用通过map的实现声明那里我们知道我们要传入两个类型一个是key,一个是T。

这里我们就写两个string。

insert

那么这里插入值,要怎么插入呢?

可以看到这里要我们输入的是一个value_type的类型值,那么这个又是什么呢?还有为什么两个值变成一个值了呢?

可以看到这里的value_type不是一个简单的key,而是一个pair,而这个pair的第一个关键字就是Key,第二个关键字就是T(也就是value)。

所以我们这里需要这么插入:

从上面的图我们可以看到在pair中,key被一个const修饰,代表着在map中key是不能被修改的,但是,value没有const修饰所以这里的value是可以被修改的。而我们在插入写pair的时候,不加const也没有关系,因为在内部会转化。

那么这里内部是如何转化的呢?

这里需要先明确一点那就是类模板的参数不同,那么就是不同的类型。

例如vector<int>和vector<char>不是同一个类型。

我的pair传过去的模板参数明明是string和string,而这里需要的是const string和string这里是怎么转化的呢?这里的实参的pair和形参的pair很明显也就是不同的类型。

那么这里能够运行是因为一个很灵活的实现。

这个灵活就在于这个拷贝构造,这个拷贝构造不是写死的,这也就意味着你可以传入自己的pair,即使不是完全和库中需要的类型一样,例如这里的const string和string,但是只要这是一个符合条件的初始化,string可以初始化给const string,那么这里就可以构造出一个pair<const stroing,string>对象。

甚至于可以这么写:

const char* 自然也是可以初始化const string的,同理const char* 也可以初始化给string。

而这里的函数可以是拷贝构造,也可以是一个拷贝构造。

所以这里只要你传入的参数是可以初始化first和secend那么这里都是可以的,例如上面的strin和string,可以初始化给const string和string,const char* 和const char* 自然也是可以初始化给const string和string的。

即这里的pair的构造既可以是构造也可以是拷贝构造。

而我们在平时一般也不和上面一样去给map插入值,而是使用下面的make_pair函数

这是一个函数模板。而函数模板的特点就是可以自己推演模板参数。

所以我们平时可以这么写:

这里的make_pair构造出的pair类型是下面的这样

虽然这里两个pair的参数基本都是不符合map的插入要求的(const string和string),但是在传参给insert的时候,可以直接使用着两个pair构造出两个符合insert插入条件的pair。

而在使用插入的时候,一般都是使用make_pair函数。

从这里可以看到模板是很厉害的。

下面我们来看map的迭代器。

这里我们就能够理解了,因为在map中储存的是一个pair,而不是我们在模拟kv模式的二叉树那里直接放两个值。在pair中first是一个key,而secend就是value。而这里在底层通过调用重载的operator*。获得pair的对象,再从派人对象中打印first和secend的值。这个在之后的map和set的模拟实现那里会有更加详细的说明。

那么我们知道除了operator之外其实还存在一个操作符重载,那就是operator->。那么什么时候使用operator,什么时候使用operator->呢?

这个在模拟实现list的时候说过:

当容器中储存的数据类型是一个自定义类型时就使用箭头去访问。而在上图中的pair就是一个自定义类型,所以上面的代码还可以这么写。

他的本质调用其实是下面这样调用的:

那么这里是怎么调用的呢?首先operator()返回的是一个自定义类型的指针,而自定义类型的指针在去使用->访问first或者是访问second。

而这里省略一个箭头就是简洁的写法了。

这里自然也是可以使用范围for的,因为存在迭代器自然就可以使用范围for.

那么这里为什么要使用&呢?

因为如果不使用引用,那么在底层就相当于是将一个pair对象赋值给了kv,这个代价是很大的,所以这里使用引用,如果你不想修改这个值的化,加一个const即可。

其次这里再将const去掉之后可以做到下面的事情:

这里就是pair的first可以修改,而second是可以修改的。那么这里是怎么做到的呢?

原理就是因为在底层的pair那里

first是被const修饰的,而seconde则没有。

下面则是map的erase函数

可以看到的就是map的erase函数是只需要key就可以完成删除的。

而也可以使用迭代器删除,而迭代器自然也是可以使用find找到的。

map统计次数

如何使用map去统计水果出现的次数呢?

就是这样输入的这个字符串如果在map中没有找到,那就插入,而如果找到了,而find返回的正是那个key的迭代器,而使用迭代器自然能够完成修改。

下面打印一下

其实除了上面的方法外还可以使用一个operator[]来完成。

以前的[]都是随机访问,你输入的是数字几,最后就会返回的就是容器中的第几个数据。而这里的[]则是你给一个key,[]返回一个key对应的value的引用。所以这里能够改变value。

那么这个[]是如何实现的呢?

下面我们来看这个函数的底层实现:

而要弄懂这个函数,我们需要回忆一下insert的返回值。

从中可以看到如果插入失败了,insert返回的pair中会把第二个bool值设置为false,而第一个则是已经存在的key所在节点的iterator,而插入成功了,insert返回的pair中会把第二个bool值设置为true,然后返回的是新插入key所在节点的iterator.

那么上面的那个mapped_type是什么呢?这个就是value()默认构造的匿名对象,如果这个value是一个int那么就是0,如果这个value是一个自定义类型就会调用自定义类型的默认构造。如果是指针那就是一个nullptr。

就拿上面的例子,如果我们现在插入这个水果(key),是之前没有存在的那么这里就会调用int的默认构造(自然就是0)。

如何理解上面的这张图呢?首先insert里面是使用了我们之前输入的key去构造了一个pair<string,int>插入到了map中,如果插入成功了那么在insert返回的那个pair中的第一个参数(iterator)就会储存新插入key所在节点的iterator,否则储存的就是已经存在的key所在节点的iterator。

然后在返回insert返回了第一个pair之后,我们通过.first拿到,已经插入/没有插入已经存在节点的iterator。然后通过这个iterator(这里的itearator就已经是map中某个节点的迭代器了)去访问value(因为value就储存在second中)。

这里还是要知道insert是具有查找和插入两个功能的,无论你是插入成功还是失败,无非成功返回的是新插入的key对应的迭代器。而失败返回的是已经存在的key所对的迭代器。

通过这个key的迭代器就能够修改value了。这里调用second的时候不能是点(.),因为这个iterator是自定义类型的迭代器,所以需要使用->去访问。

下面我们就能够总结[]的功能了

而在用的角度map的重点就是[]。

而multimap其它方面和map是一样的,但是不支持[]

因为在multimap中运行多个key,而在insert那里就不知道要返回哪一个key的迭代器,所以multimap是不支持[]的。其余函数基本和map是一样的。

写的不好,请见谅。

希望这篇博客能对你有所帮助,如果发现了任何错误,欢迎指出。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【Redis】环境配置

环境配置 Linux版本&#xff1a; Ubuntu 22.04.2 LTS 下载redis sudo apt install redis 启动redis redis-server 输入redis-server启动redis竟然报错了&#xff0c;原因是redis已经启动&#xff0c;网上大多数的解决方案如下&#xff1a; ps -ef | grep -i redis 查询redi…

顺序表——leetcode

原地删除数据 我们的思路这里给的是双指针&#xff0c;给两个指针&#xff0c;从前往后移动&#xff0c;如果不是val就覆盖&#xff0c;如果是我就跳过&#xff0c;大家一定要看到我们的条件是原地修改&#xff0c;所以我们不能另开一个数组来实现我们这道题目。 这里我们给两…

【React】02.create-react-app基础操作

文章目录 当前以及未来的开发&#xff0c;一定是&#xff1a;组件化开发如何划分组件React的工程化/组件化开发create-react-app基础运用运用react常用版本一个React项目中&#xff0c;默认会安装 2023年最新珠峰React全家桶【react基础-进阶-项目-源码-淘系-面试题】 当前以及…

docker进阶

文章目录 docker 进阶Part1 常用命令总结docker version 查看docker客户端和服务端信息docker info 查看更加详细信息docker images 列出所有镜像基本用法常用选项 docker search 搜索镜像基本用法示例用法 docker pull 拉取镜像基本用法示例用法 docker rmi 删除镜像基本用法示…

这样的软件测试报告模板你绝对没见过!!!

测试报告如此重要&#xff0c;那么我们应该如何撰写呢&#xff1f;为了让大家彻底掌握测试模板的撰写&#xff0c;所以本文结构如下&#xff1a; 1、测试报告写给谁看&#xff1f; 2、测试报告的基本骨架&#xff08;通过|不通过&#xff09;&#xff1f; 3、测试报告如何才能达…

RSA ——Rational Structure Architecture r入门教程

&#xff08;一&#xff09;UML概述 UML&#xff0c;即统一建模语言&#xff08;Unified Modeling Language&#xff09;&#xff0c;是一种通用的面向对象的可视化建模语言。其核心目的是为软件的面向对象描述和建模提供一种标准化的方法。UML并不是一种编程语言&#xff0c;因…

海外跨境电商云厂商阿里云、华为云、九河云选择攻略

近几年出海已成为趋势&#xff0c;而要打有准备的仗&#xff0c;就必须先收集关键信息。国内企业出海需要对海外市场进行深入的分析和挖掘&#xff0c;数据分析技术可以帮助国内企业更好地了解海外市场的需求和趋势&#xff0c;包括市场调研、用户画像、销售数据分析等方面&…

如何将 ruby 打包类似于jdk在另一台相同架构的机器上面开箱即用

需求 目前工作中使用到了ruby作为java 项目的中转语言&#xff0c;但是部署ruby的时候由于环境的不同会出现安装依赖包失败的问题&#xff0c;如何找到一种开箱即用的方式类似于java 中的jdk内置jvm这种方式 解决 TruffleRuby 完美解决问题&#xff0c;TruffleRuby 是使用 T…

餐饮连锁品牌2023:端起“外卖碗”,吃上“下沉饭”

作者 | 陈小江 文 | 螳螂观察 “没想到&#xff0c;蜜雪(蜜雪冰城&#xff09;能到我们这乡镇来开&#xff0c;我觉得挺意外的。「柏记水饺」也算挺大一品牌&#xff0c;没想到也能来&#xff08;我们&#xff09;乡镇”。 谈起不断有连锁品牌进镇开店&#xff0c;黑龙江讷河…

框架安全-CVE 漏洞复现DjangoFlaskNode.jsJQuery框架漏洞复现

目录 服务攻防-框架安全&CVE复现&Django&Flask&Node.JS&JQuery漏洞复现中间件列表介绍常见语言开发框架Python开发框架安全-Django&Flask漏洞复现Django开发框架漏洞复现CVE-2019-14234&#xff08;Django JSONField/HStoreField SQL注入漏洞&#xff…

9. linux系统设置开机自启动发射热点

1. 说明 某种情况下需要使用wifi进行通信时&#xff0c;可以在linux系统中发射一个热点让以使别的设备能够连接&#xff0c;然后进行通信。一般情况下可以在有无线wifi发射器的情况下&#xff0c;每次linux系统开机后&#xff0c;手动设置开启热点&#xff0c;但这种方式比较麻…

分享一下怎么做陪诊小程序

在当今快节奏的社会中&#xff0c;人们的生活压力越来越大&#xff0c;尤其是在大城市中&#xff0c;由于工作繁忙&#xff0c;生活节奏快&#xff0c;很多人都感到看病难、看病贵的问题。为了解决这一问题&#xff0c;陪诊小程序应运而生。陪诊小程序是一种可以提供线上预约、…

Redis Cluster (Redis 集群),使用Redis自带的集群功能搭建无主模式集群

文章目录 一、概述二、模拟配置说明三、脚本方式创建 Redis Cluster3.1 配置创建脚本3.2 启动集群实例3.3 创建集群3.4 测试集群3.5 停止集群实例3.6 删除&#xff08;清空&#xff09;集群 四、手动创建集群 Redis Cluster4.1 启动集群实例4.2 手动创建集群4.4 测试集群 五、集…

C/C++药房管理 2021年3月电子学会青少年软件编程(C/C++)等级考试一级真题答案解析

目录 C/C药房管理 一、题目要求 1、编程实现 2、输入输出 二、算法分析 三、程序编写 四、程序说明 五、运行结果 六、考点分析 C/C药房管理 2021年3月 C/C编程等级考试一级编程题 一、题目要求 1、编程实现 随着信息技术的蓬勃发展&#xff0c;医疗信息化已经成为…

十八、模型构建器(ModelBuilder)快速提取城市建成区——批量掩膜提取夜光数据、夜光数据转面、面数据融合、要素转Excel(基于参考比较法)

一、前言 前文实现批量投影栅格、转为整型,接下来重点实现批量提取夜光数据,夜光数据转面、夜光数据面数据融合、要素转Excel。将相关结果转为Excel,接下来就是在Excel中进行阈值的确定,阈值确定无法通过批量操作,除非采用其他方式,但是那样的学习成本较高,对于参考比较…

【C++】详解map和set基本接口及使用

文章目录 一、关联式容器与键值对1.1关联式容器&#xff08;之前学的都是序列容器&#xff09;1.2键值对pairmake_pair函数&#xff08;map在插入的时候会很方便&#xff09; 1.3树形结构的关联式容器 二、set2.1set的基本介绍2.1默认构造、迭代器区间构造、拷贝构造&#xff0…

ROS常用命令及多机(TX2 与虚拟机)通信步骤

目录 ROS常用命令 创建工作空间 编译工作空间 创建功能包 编译功能包 检查环境变量 查看ROS节点关系图 rviz使用 Ubuntu常用命令 ROS多机通信步骤 配置网络 设置网络名称 修改bash 互ping虚拟机与tx2 tx2中启动master 虚拟机中添加镜像源 TX2中添加镜像源 ROS常…

mybatisplus多数据源

适用于多种场景&#xff1a;纯粹多库、 读写分离、 一主多从、 混合模式等 场景说明&#xff1a; 我们创建两个库&#xff0c;分别为&#xff1a;mybatis_plus&#xff08;以前的库不动&#xff09;与mybatis_plus_1&#xff08;新建&#xff09;&#xff0c;将mybatis_plus库…

前端移动web详细解析四

移动 Web 第四天 01-vw适配方案 vw和vh基本使用 vw和vh是相对单位&#xff0c;相对视口尺寸计算结果 vw&#xff1a;viewport width&#xff08;1vw 1/100视口宽度 &#xff09; vh&#xff1a;lviewport height ( 1vh 1/100视口高度 ) vw布局 vw单位的尺寸 px 单位数…

Ubuntu 搭建 DHCP ivp6 server 步骤

Ubuntu 搭建 DHCP ivp6 server 步骤 安装 DHCP server安装 radvd&#xff08;实现局域网路由功能)测试运行 安装 DHCP server apt 安装 isc-dhcp-server sudo apt-get install isc-dhcp-server修改配置文件 /etc/dhcp/dhcpd6.conf 内容如下&#xff1a; lease-time 7200; lo…