2.链表(代码随想录——python版本)
2.链表(代码随想录——python版本)
链表的概念:
链表是由指针串联在一起的线性结构,一个节点(node)由两部分组成:
- 数据域——用来存储数据;
- 指针域——用来指向下一个节点(如果是双指针,另一个指向前一个结点),最后一个节点的指针指向null(空)。
链表的入口节点称为链表的头节点。
我们发现,在Python中是没有链表这样的数据类型的,所以我们需要自己定义链表类:
class listnode(object):# 这是一个单链表类型。def __init__(self,num = 0, next = None):self.num = numself.next = next
如果我们建立了一个链表节点head,并将其赋值给了tmp,那么它们操作的便是同一份链表,tmp对链表的操作也会影响到head对链表的访问。
链表的类型:
单链表:
上文所写的就是单链表。
双链表:
每一个node有两个指针,一个指向前node一个指向后node,可以向前查找也可以向后查找。(单链表只能够向后查找)。
循环列表:
链表的首尾相连,用来解决约瑟夫环问题。
链表的存储:
与数组不同,其内存空间并不是连续的,只是靠着指针将它们结合起来。(也就是说物理上是不连续的仅仅在逻辑上连续)。
基础操作:
删除一个节点:
找到要删除的节点的前一个节点,将next指向后一个节点即可。(python中有自己的内存回收机制,不用我们手动去回收,单要是C++的话是需要的。)
class listnode(object):def __init__(self, num, next=None):self.num = numself.next = nexthead = listnode(1)
head.next = listnode(2)
head.next.next = listnode(3)
print(head.num,head.next.num,head.next.next.num,head.next.next.next)head.next = head.next.next
print(head.num,head.next.num,head.next.next)
下面我们试着将其封装一个函数:
203. 移除链表元素 - 力扣(LeetCode)
不设置虚拟节点(不建议那么写,因为真的很麻烦。。。特别是判断head是否为空需要判断多次,我将错误代码也附加在下面,代码随想录中并没有这样的实现,若有更好的想法欢迎交流):
思路:
- 首先,判断head是否为空,若为空,直接返回head;
- 然后判断head是否需要删除,若需要,将head变为head的后一个节点:
- 如果更新后的head为空,一定要及时退出,不然会报我下面贴出来的错误。
- 然后创建一个tmp,指向head,由于已经保证了head不会再被删除,所以直接比较tmp.next.val的值就可以了,若要删除,将tmp.next的值更新为tmp.next.next;否则,tmp变为tmp.next,为下一次循环做准备。
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:def removeElements(self, head: Optional[ListNode], val: int) -> Optional[ListNode]:if head == None:return headelse:# 先考虑头节点位置怎么删除while head.val == val:head = head.nexttmp = headwhile tmp.next:# 现在我们其实已经确保了起始位置的val不会是val了if tmp.next.val == val:tmp.next = tmp.next.nextelse:tmp = tmp.nextreturn head
报错信息如下:
AttributeError: 'NoneType' object has no attribute 'val'^^^^^^^^while head.val == val:
Line 12 in removeElements (Solution.py)^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ret = Solution().removeElements(param_1, param_2)
Line 54 in _driver (Solution.py)_driver()
Line 69 in <module> (Solution.py)
正确的代码:
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:def removeElements(self, head: Optional[ListNode], val: int) -> Optional[ListNode]:if head == None:return headelse:# 先考虑头节点位置怎么删除while head.val == val:head = head.nextif head == None:return headtmp = headwhile tmp.next:# 现在我们其实已经确保了起始位置的val不会是val了if tmp.next.val == val:tmp.next = tmp.next.nextelse:tmp = tmp.nextreturn head
设置一个虚节点:
设置一个虚节点,让我们可以使用一个相同的规则去删除节点。
这是怎么做到的呢,我们创建一个虚拟的头节点,dummy_node其next指向head,这样我们的所有删除都是和上文删除tmp一样的了,需要注意的是返回的值应该为dummy_node.next。
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:def removeElements(self, head: Optional[ListNode], val: int) -> Optional[ListNode]:# 建立一个临时的虚拟头节点tmp_head = ListNode(next = head)# current为循环要用的,为什么不直接用tmp_head,因为我们要返回新的头节点,不应该变动虚拟头节点的位置,current指向的也是和虚拟头节点指向的链表current = tmp_headwhile current.next != None:# 为什么直接比较next.val的值呢,因为最开始的位置是虚拟头节点而不是头节点if current.next.val == val:# 指向原来节点的后一个节点的current.next = current.next.nextelse:# 将当前节点变为下一个节点,为循环做准备current = current.next# 返回虚拟头节点的next指向就是新的头节点return tmp_head.next
小结:
- 虚拟节点的概念!
增加一个节点:
创建一个节点然后将要插入部位的前一个node的next指向它,它的next指向原位置的元素。
我们可以发现如果我们在head处插入是非常方便的O(1),要是想要从末尾添加一个node则需要先遍历原链表一次才能够实现,这是非常麻烦的O(n)。
这里不给出代码啦,因为下面的题目会全部写一遍~
设计一个链表:
707. 设计链表 - 力扣(LeetCode)
链表类(节点类):
class ListNode:def __init__(self,val = 0, next = None):self.val = valself.next = next
MyLinkedList类:
class MyLinkedList:def __init__(self):# 先需要创建一个虚拟头节点:self.dummy_head = ListNode()self.size = 0def get(self, index: int) -> int:def addAtHead(self, val: int) -> None:self.dummy_head.next = ListNode(val, self.dummy_head.next)size += 1def addAtTail(self, val: int) -> None:def addAtIndex(self, index: int, val: int) -> None:def deleteAtIndex(self, index: int) -> None:# Your MyLinkedList object will be instantiated and called as such:
# obj = MyLinkedList()
# param_1 = obj.get(index)
# obj.addAtHead(val)
# obj.addAtTail(val)
# obj.addAtIndex(index,val)
# obj.deleteAtIndex(index)
反转链表:
206. 反转链表 - 力扣(LeetCode)
双指针:
设定两个指针,pre表示前一个节点,cur表示当前节点;将pre初始化为None,cur初始化为head;cur和pre一起向前移动,这样就能一直更改指向顺序实现反向,但是我们发现,如果更改完顺序后,我们没有办法遍历原链表的顺序了,所以要使用tmp变量去暂存cur.next的值。
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:def reverseList(self, head: Optional[ListNode]) -> Optional[ListNode]:pre = Nonecur = headwhile cur:# 由于我们之后要将cur后的next反向,所以要在赋值前将cur.next的值保存下来tmp = cur.next# 将cur指向pre(前一个节点)cur.next = pre# 更新pre和cur的位置pre, cur = cur, tmp# 返回pre(即为头节点)return pre
递归:
思路还是双指针。
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:def reverseList(self, head: Optional[ListNode]) -> Optional[ListNode]:# 定义递归def recur(cur, pre):# 和双指针法一样,指定当cur为None为退出条件if cur == None: return preelse:# 一样需要记录cur.nexttmp = cur.nextcur.next = pre# 记住一定要return否则程序运行会有问题,在新一轮递归中,tmp为cur,pre为curreturn recur(tmp, cur)return recur(head, None)
小结:
- 要牢记双指针(改变顺寻,交换);
- 递归的运用。
两两交换链表的节点:
24. 两两交换链表中的节点 - 力扣(LeetCode)
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:def swapPairs(self, head: Optional[ListNode]) -> Optional[ListNode]:dummy_head = ListNode(next = head)# cur和dummy_head指向的是同一个链表cur = dummy_head# 保证至少还有两个元素在链表内while cur.next and cur.next.next:# 暂存第一个节点,因为会改变虚拟节点的指向tmp1 = cur.next# 暂存第三个节点,因为会改变第二个节点的指向tmp2 = cur.next.next.nextcur.next, cur.next.next,cur.next.next.next = cur.next.next,tmp1,tmp2cur = cur.next.nextreturn dummy_head.next
小结:
- 要注意循环结束的条件;
- 画图进行交换的分析,确定哪些节点需要暂存起来
删除链表中的倒数第n个节点:
19. 删除链表的倒数第 N 个结点 - 力扣(LeetCode)
关键在于找到倒数第n个节点的位置在哪,要删除倒数第n个节点,那么操作指针要指向上一个节点。
方法:
使用虚拟头节点(不需要对操作的节点是不是头节点进行特殊操作);使用双指针法,两个指针先都指向dummy_head,让快指针先移动n步,然后再让快慢指针一起移动,直到快指针指向了None处,慢指针就找到了倒数第n个,此时慢指针便找到了倒数第n个节点的位置,但我们要做的是删除,所以快指针应该先走n+1步。
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:def removeNthFromEnd(self, head: Optional[ListNode], n: int) -> Optional[ListNode]:dummy_node = ListNode(next = head)fast = dummy_nodeslow = dummy_nodefor i in range(n+1):fast = fast.nextwhile fast:fast = fast.nextslow = slow.nextslow.next = slow.next.nextreturn dummy_node.next
- 双指针法(倒数第n,交换,反序);
- 难点在于如何找到倒数第n个元素在哪,让快指针比慢指针先走n步,慢指针所在的位置就是倒数第n个节点的位置;
- 清楚删除节点是要找到删除节点的前一个元素。
链表相交:
面试题 02.07. 链表相交 - 力扣(LeetCode)
说实话本来以为就是遍历链表找到值相同的就能够返回了,但没想到居然是找节点相同的(虽然还是没搞明白示例的1为什么不对,但看下面评论貌似是被给定的值给限制死了,但是代码里也没有体现啊喂,而且先给出图再问有没有相交是否有点。。。)
首先循环遍历两个链表,得出两个链表的长度,然后找出较长的那一端,将其指针往后挪动多的长度的位数,这样之后,curA和curB就可以一起进行移动,并判断是否相等了,只要curA==curB(指针域相同,数值域也相同,就认为找到交点了)。写的有点啰嗦,实则可以进行优化,可以优化的部分注明在下面的代码中。
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = Noneclass Solution:def getIntersectionNode(self, headA: ListNode, headB: ListNode) -> ListNode:curA = headAcurB = headBlenA, lenB = 0, 0while curA:curA = curA.nextlenA += 1while curB:curB = curB.nextlenB += 1curA, curB = headA, headB# 这边是可以优化的,写成lenA<lenB然后将lenA,lenB以及curA和curB互换位置,之后的代码便都是一样的了。if lenA >= lenB:x = lenA - lenBfor i in range(x):curA = curA.nextwhile curA:if curA == curB:return curAelse:curA, curB = curA.next,curB.nextelse:x = lenB - lenAfor i in range(x):curB = curB.nextwhile curA:if curA == curB:return curAelse:curA, curB = curA.next,curB.nextreturn None
环形列表:
142. 环形链表 II - 力扣(LeetCode)
题目要求比较多,不仅要判断是不是环形还要判断入口在哪,说实话有种无从落笔的感觉。。。
判断是不是环形:
首先,我们使用双指针法(实话,没想到,看了随想录的视频讲解才明白)。设定快慢指针都指向头节点,然后,让慢节点一个节点一个节点的更新,快节点则以两个节点的速度更新(在环上就是以一个节点的速度接近慢节点,以免丢失)。同时需要对快节点和其下一个节点是否为空进行判断,若是空说明不是环,直接退出;若有节点则继续,由于循环次数不固定,所以使用while循环。
判断入口在哪:
这是一个数学问题,首先设定head到节点的长度为x,快慢节点相遇的位置慢节点顺时针走过的距离为y,剩下的圆环路径值为z;慢指针走过的长度为x+y;快指针走过的长度为x+y+n(y+z);
由于快指针的速度是慢指针的两倍:
2(x+y) = x+y+n(y+z)——>x = (n-1)(y+z)+z
从这个式子我们好像得不出说明东西,那我们让n=1,发现x = z,让n=2,发现 x = y+z +z;规律就是一定会走过一个z和n-1个环的长度和x是一样的,那么将一个指针挪回head,另一个指针停留在原位置,以一个单位的速度一起往后移动,相遇的位置就是入口。
为什么慢指针走不了一圈呢?
假设在进入时,两个指针都在起点位置,过了一圈,那么快慢指针刚好会在入口位置相遇,这样最远的位置都只有一圈,其他位置就更不用说了。
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = Noneclass Solution:def detectCycle(self, head: Optional[ListNode]) -> Optional[ListNode]:# 双指针法fast,slow = head, head# 判断当前快指针和快指针的下一个节点是否存在,若不存在说明不存在环while fast and fast.next:# 慢指针以1个单位的速度前进slow = slow.next# 快指针以两个单位的速度前进,即以一个单位的速度逼近快指针,保证不会错过慢指针fast = fast.next.next# 若两个相遇了,说明存在环if fast == slow:# x = n (y + z) - y == (n-1)(y+z) + z—— # x为起点到入口的距离,y为慢指针走过的环的距离(不到一圈),z为剩余的环的距离# 将其中的一个指针挪到起点,另一个保留在原位置,都以一个单位的速度前进直至相遇slow = headwhile slow != fast:slow = slow.nextfast = fast.next# 返回相遇点,就是入口return slowreturn None
小结:
- 对快慢指针的认识还是太浅了;
- 对如何判断环有了基本认识;
- 感觉链表问题基本都是双指针问题。
链表小结:
- 虚拟头节点的方法很常用,需要牢记;
- 链表的许多问题都可以转换为双指针问题,没有思路的时候可以试试;
- 将head赋值给多给不同的指针,这些指针操作的都是一个链表;
- 创建节点时能够一步到位就一步到位,不要太啰嗦(我容易犯的毛病)
过完第一遍感觉只是有了一个大致的印象,后面好像代码随想录会有双指针法的专栏,依然会坚持学习的。估计可能会5-6刷整个代码随想录(后期熟练之后刷题应该会很快,毕竟我才研0还有很多时间~)
相关文章:

2.链表(代码随想录——python版本)
2.链表(代码随想录——python版本) 链表的概念: 链表是由指针串联在一起的线性结构,一个节点(node)由两部分组成: 数据域——用来存储数据;指针域——用来指向下一个节点…...

6个解决“由于找不到vcruntime140_1.dll无法继续执行代码”问题的方法
vcruntime140_1.dll丢失的问题在Windows操作系统中相对常见,它通常与Microsoft Visual C Redistributable有关。本文将详细解读vcruntime140_1.dll丢失的原因、解决方法以及预防措施,帮助用户更好地应对这一问题。 一,vcruntime140_1.dll文件…...

常用数据库获取表,视图,列,索引信息
一、分页获取数据库用户的所有表 (1)、Oracle,OceanBase(Oracle内核版),DM 使用ALL_TABLES,需要添加当前用户作为查询条件 select a3.* from (select a2.* from (select a1.*, rownum rn1 from ( select t1.table_name, t2.comments fro…...

架构设计笔记-16-嵌入式系统架构设计理论与实践
目录 知识要点 嵌入式微处理器 存储器(memory) 内(外)总线逻辑 嵌入式操作系统(Embedded Operating System,EOS) 通用中间件 嵌入式中间件的一般架构 典型嵌入式中间件系统 案例分析 1…...

SpringSecurity使用介绍
1、SpringSecurity 1.1 SpringSecurity简介 Spring Security是基于Spring的安全框架,提供了包含认证和授权的落地方案;Spring Security底层充分利用了Spring IOC和AOP功能,为企业应用系统提供了声明式安全访问控制解决方案;SpringSecurity可…...

# Js 回调函数
Js 回调函数 文章目录 Js 回调函数回调函数的定义和使用回调函数的常见用途异步操作事件处理 回调函数的优点和缺点优点缺点 回调地狱解决回调地狱的方法使用 Promise使用 async/await 应用函数式编程中的回调函数高阶函数函数柯里化 异步编程中的回调函数回调函数的错误处理传…...

COOLSHELL文章:从Code Review 谈如何做技术【阅读笔记】
从Code Review 谈如何做技术原文链接:https://coolshell.cn/articles/11432.html#google_vignette 工程师需要有责任心和修养,不是做出来就了事,而是要做漂亮。 这也是山寨和工业的区别,只以做出来为标准是劳动密集型的装配生产线…...

3.1.1 ReactOS系统中二叉树创建一个MEMORY_AREA节点
二叉树中创建一个MEMORY_AREA节点: 二叉树中创建一个MEMORY_AREA节点: MmCreateMemoryArea() 参数AddressSpace是MADDRESS SPACE结构指针,所指向的数据结构代表着一个进程的用 户空间。 参数BaseAddress是个指针,用来给定和返回内…...

三、Linux 安装全攻略
Linux 安装全攻略 在当今的科技时代,Linux 操作系统以其稳定性、安全性和高度的可定制性而备受青睐。本文将详细介绍 Linux 的安装过程,包括关键步骤和下载资源获取方式,帮助你顺利踏上 Linux 之旅。 一、为什么选择 Linux Linux 有许多优…...

Ansible自动化工具
一、Ansible概述 1.1 什么是Ansible Ansible 是一个开源的自动化工具,用于配置管理、应用程序部署和任务自动化。它让你可以通过编写简单的 YAML 文件(剧本,Playbooks),轻松管理和配置多个服务器。Ansible 的特点是无…...

Flutter Container组件
Over the past few years, I’ve been fortunate to collaborate with interior designers, and there’s a distinct flair to their approach to crafting captivating interiors. It’s not just about arranging furniture randomly; they meticulously plan layouts, sele…...

IPv6 DNS简介
IPv6网络中的每台主机都是由IPv6地址来标识的,用户只有获得待访问主机的IPv6地址,才能够成功实现访问操作。对于用户来讲,记住主机的IPv6地址是相当困难的,因此设计了一种字符串形式的主机命名机制,这就是域名系统。用…...

【Python-AI篇】数据结构和算法
1. 算法概念 1.1 什么是数据结构 存储,组织数据的方式 1.2 什么是算法 实现业务目的的各种方法和思路算法是独立的存在,只是思想,不依附于代码和程序,可以使用不同语言实现(java,python,c&a…...

VideoCLIP-XL:推进视频CLIP模型对长描述的理解
摘要 对比语言-图像预训练(CLIP)已被广泛研究并应用于众多领域。然而,预训练过程中对简短摘要文本的重视阻碍了CLIP理解长描述的能力。在视频方面,这个问题尤为严重,因为视频通常包含大量详细内容。在本文中ÿ…...

【vue】vue-router_ vue3路由管理器
代码获取 vue-router_ vue3路由管理器 ⼀、基本介绍 1. 单⻚应⽤程序介绍 1.1 概念 单⻚应⽤程序:SPA(Single Page Application)是指所有的功能都在⼀个HTML⻚⾯上实现 1.2 具体⽰例 单⻚应⽤⽹站: ⽹易云⾳乐 https://music.163.com/ 多⻚应⽤⽹…...

昇思MindSpore进阶教程--Diffusion扩散模型(上)
大家好,我是刘明,明志科技创始人,华为昇思MindSpore布道师。 技术上主攻前端开发、鸿蒙开发和AI算法研究。 努力为大家带来持续的技术分享,如果你也喜欢我的文章,就点个关注吧 正文 关于扩散模型(Diffusi…...

Nginx:proxy_pass指令
proxy_pass 指令在 Nginx 中是实现反向代理和负载均衡的重要指令。 一. 反向代理 在反向代理的场景下,proxy_pass 指令用于将接收到的请求转发给另一个后端服务器。后端服务器地址可以是 IP 地址加端口、域名加端口、或者一个完整的 URL。 注意事项 proxy_pass …...

【AI学习】Mamba学习(十):HiPPO总结
前面用五篇文章陆续学了HiPPO框架。 这里再进行一下总结。 总结 HiPPO,高阶多项式投影,high-order polynomial projection operators 为了解决从序列数据中建模和学习的问题,尤其是长序列,十万甚至百万长度的序列,使…...

AI编程新纪元:Cursor与V0引领的技术变革
#1024程序员节 | 征文# AI编程新纪元:Cursor与V0引领的技术变革 作为一名SAP业务顾问,虽然我懂一些ABAP开发,但是我对于前后端开发是完全不懂的,我一直对前后端开发怀有浓厚兴趣,总想着自己能开发出一些好玩的东西&…...

python——类
问:小编为什么突然开始发python?难道C语言你不行了? 废话少说,让我们进入python中的类的学习!! (一)基本知识 (1)掌握类的概念 1、类的定义: 即…...

走廊泼水节——求维持最小生成树的完全图的最小边权和
题目 思考 代码 #include <bits/stdc.h> using namespace std; const int N 6010; const int M N; int p[N], sz[N]; struct edge{int a;int b;int c;bool operator < (const edge& v) const{return c < v.c;} }e[M]; int find(int x) {if(p[x] ! x) p[x] …...

LC:动态规划-买卖股票
文章目录 121. 买卖股票的最佳时机122. 买卖股票的最佳时机 II714. 买卖股票的最佳时机含手续费309. 买卖股票的最佳时机含冷冻期 121. 买卖股票的最佳时机 链接:https://leetcode.cn/problems/best-time-to-buy-and-sell-stock/description/ 使用贪心,…...

FLINK SQL 任务参数
在Flink SQL任务中,参数配置对于任务的性能和稳定性至关重要。以下是对运行时参数、优化器参数和表参数的详细解析: 一、运行时参数 运行时参数主要影响Flink作业在执行过程中的行为。以下是一些关键的运行时参数: 并行度(Para…...

HCIP——以太网交换安全(四)DHCP Snooping
目录 一、DHCP Snooping的知识点 二、DHCP Snooping实验拓扑 三、总结 一、DHCP Snooping的知识点 1.1、DHCP snooping 概述: ①DHCP Snooping使能DHCP的一种安全特性,用于保证DHCP客户端从合法的DHCP服务端获取IP地址。DHCP服务器记录DHCP客户端IP…...

k8s worker 节点关机 sts 管理的 pod 无法迁移
背景 1.28.2 版本 k8s 中的一台 worker 节点内存异常,需要关机换内存,正好可以测试一下 pod 的迁移。 发现 deployment 管理的 pod 是能够重新创建飘到其他节点上的,但是 statefulset 管理的 pod 一直处于 Terminating 状态无法迁移&#…...

排序04 视频播放建模
视频播放时长 用p拟合y,t是用户的实际观看时长,用y和p熵作为损失函数,使得p接近y。 输出z,对z做sigmoid变换。 exp(z)可以视为对播放时长的预估 视频完播 回归方法 二元分类方法 调整:预估完播率不能直接使用...

【常见大模型API调用】第三篇:清华智谱--智谱AI
1. 公司及模型介绍 智谱AI是一家由清华大学计算机系知识工程实验室的技术成果转化而来的AI知识智能技术开发商。智谱AI致力于打造新一代认知智能大模型,专注于做大模型的中国创新。 2024年1月16日,智谱AI在首届技术开放日上发布了新一代基座大模型GLM-…...

LayerSkip – Meta推出加速大型语言模型推理过程的技术
我们提出的 LayerSkip 是一种端到端的解决方案,可加快大型语言模型(LLM)的推理速度。 首先,在训练过程中,我们采用了层间丢弃技术(layer dropout),早期层间丢弃率较低,后期层间丢弃率较高。 其次…...

环境变量与本地变量(Linux)
引言 在当今的计算机技术领域,Linux操作系统以其稳定性和灵活性而广受欢迎。它不仅是服务器和开发者的首选平台,也是探索计算机科学和系统编程的宝库。在这个强大的操作系统中,环境变量与本地变量扮演着至关重要的角色,它们是管理…...

【完-网络安全】Windows防火墙及出入站规则
文章目录 防火墙入站和出站的区别域网络、专用网络、公用网络的区别 防火墙 防火墙默认状态一般是出站允许,入站阻止。 入站和出站的区别 入站就是别人来访问我们的主机,也就是正向shell的操作 出站就是反向shell,主机需要主动连接kali&am…...