算法设计思想(7)— 双指针之快慢指针(判断链表是否有环、返回环的起始位置、寻找无环单链表的中点、单链表倒数第K个数)

news/2025/2/21 9:00:46/

双指针一般分为两类:

  • 快、慢指针,主要解决链表中的问题
  • 左、右指针,主要数组或者字符串问题

快、慢指针一般会初始化指向链表的头节点 head,前进时快指针 fast 向前、慢指针 slow 在后,用于解决链表中的一些问题。

1. 判断链表中是否有环

如果链表不包含环,那么这个指针最终会遇到空指针 null,表示链表已经到头了,接下来再没有元素了。

bool hasCycle(ListNode head) 
{while(head != null){head = head.next;}return false;
}

2

但是如果链表中含有环,那么上面代码会陷入死循环中,因为环形链表中没有 null 指针作为尾部节点。

经典解法是使用双指针,一个跑的快,一个跑的慢。如果不含有环,跑的快的那个指针最终会遇到 null ,说明链表不包含环,如果含有环,快指针最终会超过慢指针一圈,和慢指针相遇时,说明链表中含有环。

bool hasCycle(ListNode head) 
{ListNode fast, slow;fast = slow = head;		//初始化快慢指针指向头节点whie(fast != null && fast.next != null) {fast = fast.next.next;	// 快指针每次前进两步slow = slow.next;	// 慢指针每次前进一步// 如果存在环,那么快、慢指针必然相遇if (fast == slow){return true;}}return false;
}

2

2. 已知链表有环,返回这个环的起始位置

bool hasCycle(ListNode head) 
{ListNode fast, slow;fast = slow = head;		//初始化快慢指针指向头节点while (fast != null && fast.next != null) {fast = fast.next.next;	// 快指针每次前进两步slow = slow.next;	// 慢指针每次前进一步// 如果存在环,那么快、慢指针必然相遇if (fast == slow){break;}}slow = head;while (slow != fast){// 两个指针以相同的速度前进fast = fast.next;slow = slow.next;}// 两个指针相遇的那个单链表节点就是环的起点return slow;
}

4

3. 寻找无环链表的中点

快指针一次前进 2 步,慢指针一次前进 1 步,当快指针到达链表尽头时,慢指针就处于链表的中间位置。

while (fast != null && fast.next != null)
{fast = fast.next.next;slow = slow.next;
}
return slow;

当链表的长度为奇数时, slow 恰好时中点位置,如果为偶数时,slow 最终的位置是中间偏右。

4. 寻找单链表的倒数第 k 个元素

快指针先走 k 步,然后快慢指针开始同步前进。这样当快指针走到链表末尾 null 时,慢指针所在的位置就是倒数第 k 个链表的节点。

ListNode slow, fast;
while(k--)
{fast = fast.next;
}
while (fast != null) 
{fast = fast.next;slow = slow.next;
}
return slow;

http://www.ppmy.cn/news/603046.html

相关文章

算法设计思想(8)— 双指针之左右指针(二分搜索、两数之和、翻转数组)

左、右指针一般运用在数组或者字符串中,实际是指两个索引值,一般初始化为 left0,right len(nums)-1。 1. 二分搜索 func binarySearch(a []int, target int) int {left : 0right : len(a) - 1// 因为搜素区间是 [left, right],…

不懂这些项目管理“黑话”,PM大佬都得被坑

早上好,我是老原。 之前给大家分享了2期技术黑话,分别是项目经理和产品经理一定要懂的技术名词,看数据大家都蛮喜欢,我给大家把往期文章链接放在文末,届时大家可自行前往阅读。 平日里,不少PM和开发、设计…

gRPC 笔记(02)— Google Protocol Buffer 协议语法规则

1. 简介 Protocol Buffers 是一种轻便高效的结构化数据存储格式,可以用于结构化数据序列化,很适合做数据存储或 RPC 数据交换格式。它可用于通讯协议、数据存储等领域的语言无关、平台无关、可扩展的序列化结构数据格式。 ​ 它有以下特点: …

gRPC 笔记(03)— protobuf 文件编写、编译器安装、生成客户端和服务端示例

1. protoc 编译器安装 1.1 二进制安装 将 Proto 协议文件转换为多种语言对应格式的工具,根据对应平台选择对应的安装包,安装包下载地址 https://github.com/protocolbuffers/protobuf/releases cd ~/tmp # 下载 wget https://github.com/protocolbuff…

gRPC 笔记(04)— gRPC 通信模式之简单RPC模式(一元RPC模式)

1. 简单 RPC 模式 一元 RPC 模式也被称为简单 RPC 模式。在该模式中,当客户端调用服务器端的远程方法时,客户端发送请求至服务器端并获得一个响应,与响应一起发送的还有状态细节以及元数据。这种模式下,客户端发送一个请求后&…

gRPC 笔记(05)— gRPC 通信模式之服务器端流 RPC、客户端流 RPC 、双向流 RPC的代码实现

通过使用流 stream,我们可以向服务器或者客户端发送批量数据,服务器和客户端在接收这些数据的时候,可以不必等所有的消息全接收后才开始响应,而是接收到第一条消息的时候就可以及时的响应。 1. 服务端流 RPC 在简单 RPC 模式中&…

gRPC 笔记(06)— gRPC 的 HTTP2 实现流程、简单RPC模式、服务器端RPC模式、客户端RPC模式、双向RPC模式的消息流传递

gRPC 使用 HTTP/2 作为其传输协议,实现通过网络发送消息。这也是 gRPC 能够成为高性能 RPC 框架的原因之一。 在 HTTP/2 中,客户端和服务器端的所有通信都是通过一个 TCP 连接完成的,这个连接可以传送任意数量的双向字节流。 相关术语如下&…

gdb 笔记(01)— gdb 简介、安装、常用功能

1. 简介 gdb(GNU debugger)是 UNIX/Linux 系统中强大的调试工具,它能够调试软件并分析软件的执行过程,帮助我们调查研究程序的正确行为,还能用来分析程序崩溃的原因等。 gdb 支持多种语言,可以支持 C/C 、…