判断一个单向链表中是否存在环

本文探讨如何在单向链表中判断是否存在环。通过使用快慢指针(Floyd 算法)的方法,可以有效地检测链表节点是否形成环状结构。这种方法避免了使用额外的数据结构,且能确保在最坏情况下线性时间复杂度完成检查。
package main

import "fmt"

/*
	题目: 判断一个单向链表中是否存在环
	最优解思路:1. 使用两个指针,起点都从链表的头结点开始,第一个指针每一步走一个节点,第二个指针每一步走两个节点,
如果存在环两个指针一定会相遇的。这类似于小学中学的追击问题:在一个环形跑道上两个运动员同一地点出发一个运动员速度快另一个速度慢,两个运动员一定会相遇。
*/

// 假设链表节点如下
type Node struct {
	num  int
	next *Node
}

// 查看给定的链表是否有环
func findRing(head *Node) bool {
	// p1 p2 指针
	p1 := head
	p2 := head
	for {
		// p1 每循环一次移动一个节点
		if p1 != nil {
			p1 = p1.next
		} else {
			return false
		}
		// p2 每循环一次移动两个节点
		if p2 != nil && p2.next != nil {
			p2 = p2.next.next
		} else {
			return false
		}
		// 判断p1 p2是否相等,如果相等则说明存在环
		if p1 == p2 {
			return true
		}
	}
}

/*
	问题扩展: 1. 求出环的长度。 2. 求出入环点
	思路:1. 环长 = p2前进的距离 - p1 前进的距离 (首次相遇)
		 2. 根据相遇问题解析的到,首次相遇点到入环点的距离(运动方向距离)与起点到入环点的距离相等,由此我们需要求得相遇点,
			然后两个指针相同速度一个从head出发,一个从入环点出发,两个指针会在入环点相遇
*/

// 1. 求出环长度
func getRingLength(head *Node) int {
	// p1 p2 指针,每一次分别走一步和两步,从head出发求出首次相遇各走了多少个节点
	p1 := head
	p2 := head
	sum1 := 0
	sum2 := 0
	for {
		p1 = p1.next
		p2 = p2.next.next
		sum1 += 1
		sum2 += 2
		if p1 == p2 {
			return sum2 - sum1
		}
	}
}

// 2. 求出入环点
func getEntrance(head *Node) *Node {
	p1 := head
	p2 := head
	p3 := head
	for {
		p1 = p1.next
		p2 = p2.next.next
		if p1 == p2 {
			for {
				// 找到首次相遇点
				// 然后p1从首次相遇点出发,p3从head出发,速度都为1,他们会在入环点相遇
				p3 = p3.next
				p1 = p1.next
				if p1 == p3 {
					// 找到入环点
					return p1
				}
			}
		}
	}
}

// 构造数据来测试
func main() {
	// 1. 构造一条存在环的链表
	node01 := &Node{num: 1}
	node02 := &Node{num: 2}
	node03 := &Node{num: 3}
	node04 := &Node{num: 4}
	node05 := &Node{num: 5}
	node06 := &Node{num: 6}
	node07 := &Node{num: 7}
	node08 := &Node{num: 8}
	node09 := &Node{num: 9}
	node10 := &Node{num: 10}
	node01.next = node02
	node02.next = node03
	node03.next = node04
	node04.next = node05
	node05.next = node06
	node06.next = node07
	node07.next = node08
	node08.next = node09
	node09.next = node10
	// 形成环
	node10.next = node05
	/*
		形成如下环
		 1-2-3-4-5-6- 7
				 |    |
		         10-9-8
	*/
	// 判断是否存在环
	boolen := findRing(node01)
	fmt.Println(boolen)
	// 求环长度
	ringLength := getRingLength(node01)
	fmt.Println(ringLength)
	// 求出入环点
	entranceNode := getEntrance(node01)
	fmt.Println(entranceNode)
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值