前情提示:Go语言学习者。本文参考https://labuladong.gitee.io/algo,代码自己参考抒写,若有不妥之处,感谢指正

关于golang算法文章,为了便于下载和整理,都已开源放在:

涉及题目

Leetcode 141.环形链表

Leetcode 142.环形链表II

Leetcode 704. 二分查找

Leetcode 167.两数之和 II - 输入有序数组

正文

我把双指针技巧再分为两类,一类是「快慢指针」,一类是「左右指针」。前者解决主要解决链表中的问题,比如典型的判定链表中是否包含环;后者主要解决数组(或者字符串)中的问题,比如二分查找。

一、快慢指针的常见算法

快慢指针一般都初始化指向链表的头结点 head,前进时快指针 fast 在前,慢指针 slow 在后,巧妙解决一些链表中的问题。

1、判定链表中是否含有环

这应该属于链表最基本的操作了,如果读者已经知道这个技巧,可以跳过。

单链表的特点是每个节点只知道下一个节点,所以一个指针的话无法判断链表中是否含有环的。

如果链表中不含环,那么这个指针最终会遇到空指针 null 表示链表到头了,这还好说,可以判断该链表不含环。

/**
* Definition for singly-linked list.
* type ListNode struct {
* Val int
* Next *ListNode
* }
*/
func hasCycle(head *ListNode) bool{
for head != nil{
head = head.Next
}
return false
}

但是如果链表中含有环,那么这个指针就会陷入死循环,因为环形数组中没有 null 指针作为尾部节点。

经典解法就是用两个指针,一个跑得快,一个跑得慢。如果不含有环,跑得快的那个指针最终会遇到 null,说明链表不含环;如果含有环,快指针最终会超慢指针一圈,和慢指针相遇,说明链表含有环。

/** 141
* Definition for singly-linked list.
* type ListNode struct {
* Val int
* Next *ListNode
* }
*/
func hasCycle(head *ListNode) bool {
var fast, slow *ListNode
fast = head
slow = head
for fast != nil && fast.Next != nil{
fast = fast.Next.Next
slow = slow.Next
if fast == slow{
return true
}
}
return false
}

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

这个问题一点都不困难,有点类似脑筋急转弯,先直接看代码:

// 142
func detectCycle(head *ListNode) *ListNode{
var fast, slow *ListNode
fast = head
slow = head
for fast != nil && fast.Next != nil{
fast = fast.Next.Next
slow = slow.Next
if fast == slow{
break
}
}
// 上面的代码类似 hasCycle函数
if fast == nil || fast.Next == nil{
// fast 遇到空指针说明没有环
return nil
}
slow = head
for slow != fast{
fast = fast.Next
slow = slow.Next
}
return slow
}

可以看到,当快慢指针相遇时,让其中任一个指针指向头节点,然后让它俩以相同速度前进,再次相遇时所在的节点位置就是环开始的位置。这是为什么呢?

第一次相遇时,假设慢指针 slow 走了 k 步,那么快指针 fast 一定走了 2k 步,也就是说比 slow 多走了 k 步(也就是环的长度)。

设相遇点距环的起点的距离为 m,那么环的起点距头结点 head 的距离为 k - m,也就是说如果从 head 前进 k - m 步就能到达环起点。

巧的是,如果从相遇点继续前进 k - m 步,也恰好到达环起点。

所以,只要我们把快慢指针中的任一个重新指向 head,然后两个指针同速前进,k - m 步后就会相遇,相遇之处就是环的起点了。

3、寻找链表的中点

类似上面的思路,我们还可以让快指针一次前进两步,慢指针一次前进一步,当快指针到达链表尽头时,慢指针就处于链表的中间位置。

for fast != nil && fast.next != nil{
fast = fast.next.next
slow = slow.next
}
// slow 就在中间位置
return slow

当链表的长度是奇数时,slow 恰巧停在中点位置;如果长度是偶数,slow 最终的位置是中间偏右:

寻找链表中点的一个重要作用是对链表进行归并排序。

回想数组的归并排序:求中点索引递归地把数组二分,最后合并两个有序数组。对于链表,合并两个有序链表是很简单的,难点就在于二分。

但是现在你学会了找到链表的中点,就能实现链表的二分了。关于归并排序的具体内容本文就不具体展开了。

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

我们的思路还是使用快慢指针,让快指针先走 k 步,然后快慢指针开始同速前进。这样当快指针走到链表末尾 null 时,慢指针所在的位置就是倒数第 k 个链表节点(为了简化,假设 k 不会超过链表长度):

var slow,fast *ListNode
slow = fast = head
for k-- > 0{
fast = fast.next
}
for fast != nil{
slow = slow.next
fast = fast.next
}
return slow

二、左右指针的常用算法

左右指针在数组中实际是指两个索引值,一般初始化为 left = 0, right = nums.length - 1 。

1、二分查找

前文「二分查找」有详细讲解,这里只写最简单的二分算法,旨在突出它的双指针特性:

// 704
func search(nums []int, target int) int{
left := 0
right := len(nums) - 1
for left <= right{
mid := (right + left) / 2
if nums[mid] == target{
return mid
}else if nums[mid] < target{
left = mid + 1
}else{
right = mid -1
}
}
return -1
}

2、两数之和

直接看一道 LeetCode 题目吧:

只要数组有序,就应该想到双指针技巧。这道题的解法有点类似二分查找,通过调节 left 和 right 可以调整 sum 的大小:

// 167
func twoSum(numbers []int, target int) []int {
// 左右指针在数组的两端初始化
left := 0
right := len(numbers)-1
for left < right{
sum := numbers[left] + numbers[right]
if sum == target{
// 题目要求的索引是从1开始的
return []int{left+1, right+1}
}else if sum < target{
left++ // 让sum大一点
}else{
right-- // 让sum小一点
}
}
return []int{-1, -1}
}

3、反转数组

func reverse(nums []int){
left := 0
right := len(nums) - 1
for left < right{
// 交换nums[left]和nums[right]
temp := nums[left]
nums[left] = nums[right]
nums[right] = temp
left++
right--
}
}

4、滑动窗口算法

这也许是双指针技巧的最高境界了,如果掌握了此算法,可以解决一大类子字符串匹配的问题,不过「滑动窗口」稍微比上述的这些算法复杂些。

幸运的是,这类算法是有框架模板的,将在后面讲解了「滑动窗口」算法模板,帮大家秒杀几道 LeetCode 子串匹配的问题。

5、双指针技巧套路框架——Go语言版的更多相关文章

  1. 2、动态规划接替套路框架——Go语言版

    前情提示:Go语言学习者.本文参考https://labuladong.gitee.io/algo,代码自己参考抒写,若有不妥之处,感谢指正 关于golang算法文章,为了便于下载和整理,都已开源放在 ...

  2. 4、BFS算法套路框架——Go语言版

    前情提示:Go语言学习者.本文参考https://labuladong.gitee.io/algo,代码自己参考抒写,若有不妥之处,感谢指正 关于golang算法文章,为了便于下载和整理,都已开源放在 ...

  3. 3、回溯算法解题套路框架——Go语言版

    前情提示:Go语言学习者.本文参考https://labuladong.gitee.io/algo,代码自己参考抒写,若有不妥之处,感谢指正 关于golang算法文章,为了便于下载和整理,都已开源放在 ...

  4. 7、滑动窗口套路算法框架——Go语言版

    前情提示:Go语言学习者.本文参考https://labuladong.gitee.io/algo,代码自己参考抒写,若有不妥之处,感谢指正 关于golang算法文章,为了便于下载和整理,都已开源放在 ...

  5. 1、学习算法和刷题的框架思维——Go版

    前情提示:Go语言学习者.本文参考https://labuladong.gitee.io/algo,代码自己参考抒写,若有不妥之处,感谢指正 关于golang算法文章,为了便于下载和整理,都已开源放在 ...

  6. Windows 8.1 with Update 镜像下载(增OEM单语言版)

    该系统已有更新的版本,请转至<Windows 8.1 with update 官方最新镜像汇总>下载. 2014年4月9日凌晨,微软向MSDN订阅用户开放了Windows 8.1 with ...

  7. 寒假答辩作品——掘地求升C语言版

    寒假答辩—掘地求升(C语言版) 前言 这个是作为寒假答辩作品写的. 之前考虑过用Unity写个游戏,但毕竟不熟悉C#,感觉几乎都是在套模板,而且写着不顺手,有想法却只能 看着C#发呆,很是无奈,所以决 ...

  8. 教孩子学编程 python语言版PDF高清完整版免费下载|百度云盘|Python入门

    百度云盘:教孩子学编程 python语言版PDF高清完整版免费下载 提取码:mnma 内容简介 本书属于no starch的经典系列之一,英文版在美国受到读者欢迎.本书全彩印刷,寓教于乐,易于学习:读 ...

  9. 手把手教你搭建SSH框架(Eclipse版)

    原文来自公众号[C you again],若需下载完整源码,请在公众号后台回复"ssh". 本期文章详细讲解了SSH(Spring+SpringMVC+Hibernate)框架的搭 ...

随机推荐

  1. Matlab 调用 Python 脚本

    Matlab 调用 Python 脚本 最近尝试在 Matlab 环境中调用 Python 脚本,这里总结下碰到的几个问题. 1. Python 模块加载 在 Matlab 函数中,想要将 Pytho ...

  2. SNP 过滤(一)

    通用过滤 Vcftools(http://vcftools.sourceforge.net) 对vcf文件进行过滤 第一步:过滤最低质量低于30,次等位基因深度(minor allele count) ...

  3. Golang使用validator进行数据校验及自定义翻译器

    Golang使用validator进行数据校验及自定义翻译器 包下载:go get github.com/go-playground/validator/v10 一.概述 在接口开发经常会遇到一个问题 ...

  4. 浏览器点击URL的响应过程

    原文:http://igoro.com/archive/what-really-happens-when-you-navigate-to-a-url/ 作为一个软件开发者,你一定会对网络应用如何工作有 ...

  5. c#年份筛选

    年份: <script type="text/javascript" src="http://www.shicishu.com/down/WdatePicker.j ...

  6. spring整合mybatis — 更新完毕

    1.准备工作 -- 导入依赖 <dependency> <groupId>org.springframework</groupId> <artifactId& ...

  7. C/C++ Qt 数据库与TableView多组件联动

    Qt 数据库组件与TableView组件实现联动,以下案例中实现了,当用户点击并选中TableView组件内的某一行时,我们通过该行中的name字段查询并将查询结果关联到ListView组件内,同时将 ...

  8. Hadoop【MR开发规范、序列化】

    Hadoop[MR开发规范.序列化] 目录 Hadoop[MR开发规范.序列化] 一.MapReduce编程规范 1.Mapper阶段 2.Reducer阶段 3.Driver阶段 二.WordCou ...

  9. 【Android】我有放入Icon到mipmap,但不显示,只显示安卓机器人Icon(Android 8.0 图标适配)

    首先,放上别人写的博客,而我自己的博客,只会写大概思路,给自己留给备忘 https://blog.csdn.net/guolin_blog/article/details/79417483 其实会发生 ...

  10. SSH服务及通过SSH方式登录linux

    SSH服务及通过SSH方式登录linux 1.检查SSH服务转自:[1]Linux之sshd服务https://www.cnblogs.com/uthnb/p/9367875.html[2]Linux ...