在Go(Golang)中检测链表的周期起始节点的方法

141 阅读2分钟

概述

目的是在一个给定的链表中找出循环的起始节点。如果链接列表中的最后一个节点指向前面的另一个节点,那么链接列表中就存在一个循环。

例子

上面的链表有一个循环。循环的起始节点是节点 2。以下是我们可以遵循的方法

  • 首先,检测给定的链表是否有一个周期。有两个指针。一个是慢速指针,另一个是快速指针。这两个指针最初都指向头部节点

  • 现在将慢指针移动1个节点,将快指针移动2个节点。

slow := slow.Next
fast := fast.Next.Next
  • 如果慢指针和快指针在任何时间点上都是一样的,那么这个链表就有细胞。

  • 快指针和慢指针只能在循环中的一个节点上相遇。让我们假设他们在节点3相遇。现在得到循环的长度。其长度为3

  • 然后在节点的头部保持一个指针,另一个指针与它保持周期长度的距离。因此,一个指针将被加在节点1处,另一个指针将在节点4处。

  • 移动这两个指针,直到它们是相同的。它们将在周期开始的节点(即节点2)相遇。

程序

以下是相同的程序。

package main

import "fmt"

func main() {
	first := initList()
	ele4 := first.AddFront(4)
	first.AddFront(3)
	ele2 := first.AddFront(2)
	first.AddFront(1)

	//Create cycle
	ele4.Next = ele2

	output := cycleStartNode(first.Head)
	fmt.Println(output.Val)

}

type ListNode struct {
	Val  int
	Next *ListNode
}

type SingleList struct {
	Len  int
	Head *ListNode
}

func (s *SingleList) AddFront(num int) *ListNode {
	ele := &ListNode{
		Val: num,
	}
	if s.Head == nil {
		s.Head = ele
	} else {
		ele.Next = s.Head
		s.Head = ele
	}
	s.Len++
	return ele
}

func initList() *SingleList {
	return &SingleList{}
}
func cycleStartNode(head *ListNode) *ListNode {
	if head == nil || head.Next == nil {
		return nil
	}

	slow := head
	fast := head

	cycleExists := false

	for slow != nil && fast != nil && fast.Next != nil {
		slow = slow.Next
		fast = fast.Next.Next

		if slow == fast {
			cycleExists = true
			break
		}
	}

	if !cycleExists {
		return nil
	}

	cycleNode := slow

	curr := cycleNode

	lengthCycle := 1

	for curr.Next != cycleNode {
		lengthCycle++
		curr = curr.Next
	}

	curr = head

	for i := 0; i < lengthCycle; i++ {
		curr = curr.Next
	}

	for head != curr {
		head = head.Next
		curr = curr.Next
	}

	return head
}

输出

2