代码随想录算法训练营第三天 | 链表

链表基础理论

注意

head := &ListNode{}的方式初始化头节点,值为0
如果想要头节点值为nil,需要var head *ListNode = nil

golang如何定义链表?

// 定义单向链表
type Node struct {
    value int // 节点的值
    next *Node // 指向下一个节点的指针
}

为什么.next可以取到下一个值?

这是因为结构体Node中定义了一个名为next的字段,它是一个指向Node类型的指针。当我们创建一个Node类型的变量时,我们可以用点号(.)来访问它的字段,例如:

node := Node{value: 10, next: nil} // 创建一个Node类型的变量
fmt.Println(node.value) // 访问value字段,输出10
fmt.Println(node.next) // 访问next字段,输出nil

当我们把一个Node类型的变量赋值给另一个Node类型的变量的next字段时,我们就建立了一个指向关系,例如:

node1 := Node{value: 10, next: nil} // 创建第一个节点
node2 := Node{value: 20, next: nil} // 创建第二个节点
node1.next = &node2 // 把第二个节点的地址赋值给第一个节点的next字段

这样,我们就可以通过第一个节点的next字段来访问第二个节点,例如:

fmt.Println(node1.next.value) // 访问第二个节点的value字段,输出20

如何创建一个链表

按题中的意思,给定一个头节点head = [1,2,6,3,4,5,6],如何生成一个链表?

package main

import "fmt"

// 定义一个节点结构体
type Node struct {
    value int // 节点的值
    next *Node // 指向下一个节点的指针
}

// 定义一个打印链表的函数,接受头节点作为参数
func printList(head *Node) {
    // 创建一个临时变量,指向头节点
    temp := head
    // 遍历链表,打印每个节点的值
    for temp != nil {
        fmt.Print(temp.value, " ")
        temp = temp.next
    }
    fmt.Println()
}

func main() {
    // 创建一个切片,包含1, 2, 6, 3, 4, 5, 6这七个值
    slice := []int{1, 2, 6, 3, 4, 5, 6}

    // 创建一个空的链表
    var head *Node = nil

    // 使用一个for循环,遍历切片中的元素,并依次插入到链表尾部
    for _, v := range slice {
        // 创建一个新的节点,值为v
        newNode := &Node{value: v}
        // 如果头节点为空,把新节点赋值给头节点
        if head == nil {
            head = newNode
        } else {
            // 否则,找到链表的最后一个节点,并把新节点接到它后面
            tail := head // 从头部开始遍历
            for tail.next != nil { // 找到最后一个节点
                tail = tail.next
            }
            tail.next = newNode // 在最后一个节点后面插入新节点
        }
    }

    // 打印链表中的所有值
    printList(head)

}

203. 移出链表元素

移出链表元素

标准的方法,死记硬背即可

使用虚拟节点的方法,写法固定

package main

import "fmt"

// 定义单向链表
type ListNode struct {
	Val  int       // 数据域
	Next *ListNode // 指针域
}

// 定义一个删除节点的函数,接收一个链表的头节点和要删除的节点的值
func deleteNode(head *ListNode, val int) *ListNode {
	dummyHead := &ListNode{}
	dummyHead.Next = head
	cur := dummyHead
	for cur.Next != nil && cur != nil {
		if cur.Next.Val == val {
			cur.Next = cur.Next.Next
		} else {
			cur = cur.Next
		}
	}
	// 返回头节点
	return dummyHead.Next
}

// 定义一个打印链表的函数,接收一个链表的头节点
func printList(head *ListNode) {
	// 创建一个临时变量指向头节点
	cur := head
	for cur != nil {
		fmt.Print(cur.Val, " ")
		cur = cur.Next
	}
	fmt.Println()
}

func main() {
	// 创建一个切片
	slice := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 0}
	// 创建一个空链表
	var head *ListNode = nil

	//遍历切片中的元素,依次插入链表的尾部
	for _, v := range slice {
		// 创建一个新节点,值为v
		newNode := &ListNode{Val: v}
		//如果头节点为空,把新节点赋值给头节点
		if head == nil {
			head = newNode
		} else {
			//找到链表的最后一个节点,把新节点接在它后面
			tail := head // 从头节点开始找
			for tail.Next != nil {
				tail = tail.Next
			}
			tail.Next = newNode // 把新节点接在最后一个节点后面
		}
	}
	// 打印原始链表
	fmt.Println("原始链表:")
	printList(head)

	// 删除链表中的元素
	fmt.Println("删除链表中的元素5后: ")
	head = deleteNode(head, 5)
	printList(head)
}

output:

原始链表:
1 2 3 4 5 6 7 8 9 0 
删除链表中的元素5后: 
1 2 3 4 6 7 8 9 0 

707 设计链表

设计链表
添加头节点、尾节点、中间节点;定位索引处的值、删除索引处的值。
初始化一个切片[]int{1, 2, 3, 4, 5},生成链表后,在该链表的基础上实现链表的基本操作。

package main

import "fmt"

type Node struct {
	val  int   // 节点值
	prev *Node // 前驱节点
	next *Node // 后继节点
}

// 定义一个链表结构体
type MyLinkedList struct {
	head *Node // 头节点
	tail *Node // 尾节点
	size int   // 链表长度
}

// 初始化链表
func Constructor() MyLinkedList {
	return MyLinkedList{
		head: &Node{},
		tail: &Node{},
		size: 0,
	}
}

// this 是方法的接受者,可以使用任何合法的标识符号命名,但是通常使用this或self来表示
// 获取链表中下标为index的节点的值,如果下标无效,则返回-1
func (this *MyLinkedList) Get(index int) int {
	// 如果下标小于0或者大于等于链表长度,返回-1
	if index < 0 || index >= this.size {
		return -1
	}
	// 创建一个临时变量,指向头节点
	cur := this.head
	// 遍历链表,直到找到下标为index的节点或者到达链表尾部
	for i := 0; i < index && cur != nil; i++ {
		cur = cur.next
	}
	// 如果找到了下标为index的节点,返回它的值,否则返回-1
	if cur != nil {
		return cur.val
	} else {
		return -1
	}
}

// 将一个值为val的节点插入到链表中第一个元素之前,在插入完成后,新节点会成为链表的第一个节点
func (this *MyLinkedList) AddAtHead(val int) {
	// 创建一个新的节点,值为val,prev为nil,next指向原来的头节点
	newNode := &Node{
		val:  val,
		next: this.head,
	}
	// 如果头节点不为空,把它的prev指向新节点
	if this.head != nil {
		this.head.prev = newNode
	}
	// 把新节点赋值给头节点
	this.head = newNode
	// 如果尾节点为空,说明链表为空,把新节点也赋值给尾节点
	if this.tail == nil {
		this.tail = newNode
	}
	// 链表长度加1
	this.size++
}

// 将一个值为val的节点追加到链表的最后一个元素
func (this *MyLinkedList) AddAtTail(val int) {
	// 创建一个新的节点,值为val,next为nil,prev指向原来的尾节点
	newNode := &Node{
		val:  val,
		prev: this.tail,
	}
	// 如果尾节点不为空,把它的next指向新节点
	if this.tail != nil {
		this.tail.next = newNode
	}
	// 把新节点赋值给尾节点
	this.tail = newNode
	// 如果头节点为空,说明链表为空,把新节点也赋值给头节点
	if this.head == nil {
		this.head = newNode
	}
}

// 在链表的第index个节点之前插入一个值为val的节点,如果index等于链表的长度,则说明是在链表尾部添加,如果index大于链表的长度,则不会插入节点
func (this *MyLinkedList) AddAtIndex(index int, val int) {
	// 如果下标小于0,相当于在头部插入新节点
	if index <= 0 {
		this.AddAtHead(val)
		return
	}
	// 如果下标等于链表长度,相当于在尾部插入新节点
	if index == this.size {
		this.AddAtTail(val)
		return
	}
	// 如果下标大于链表长度,不插入新节点
	if index > this.size {
		return
	}

	// 创建一个临时变量,指向头节点
	cur := this.head
	// 遍历链表,直到找到下标为index的节点或者到达链表尾部
	for i := 0; i < index && cur != nil; i++ {
		cur = cur.next
	}

	// 如果找到了下标为index的节点,创建一个新的节点,值为val,prev指向原来的节点的prev,next指向原来的节点
	if cur != nil {
		newNode := &Node{
			val:  val,
			prev: cur.prev,
			next: cur,
		}
		// 如果原来的节点的prev不为空,把它的next指向新节点
		if cur.prev != nil {
			cur.prev.next = newNode
		}
		// 把原来节点的prev指向新节点
		cur.prev = newNode
		//如果原来的节点为头节点,将新节点赋值给头节点
		if cur == this.head {
			this.head = newNode
		}
		// 链表长度加1
		this.size++
	}
}

// 如果下标有效,则删除链表中下标为index的节点。
func (this *MyLinkedList) DeleteAtInedex(index int) {
	// 如果下标小于0或者大于等于链表长度,不做任何操作
	if index < 0 || index >= this.size {
		return
	}

	//创建一个临时变量,指向头节点
	cur := this.head
	//	遍历链表,直到找到下标为index的节点或者到达链表尾部
	for i := 0; i < index && cur != nil; i++ {
		cur = cur.next
	}

	// 如果找到了下标为index的节点,把它从链表中删除
	if cur != nil {
		// 如果他的prev不为空,把它prev的next指向它的next
		if cur.prev != nil {
			cur.prev.next = cur.next
		}
		// 如果他的next不为空,把它next的prev指向它的prev
		if cur.next != nil {
			cur.next.prev = cur.prev
		}

		// 如果他是头节点,把它的next赋值给头节点
		if cur == this.head {
			this.head = cur.next
		}
		// 如果他是尾结点,把它的prev赋值给尾结点{
		if cur == this.tail {
			this.tail = cur.prev
		}
		// 链表长度减1
		this.size--
	}
}

// 打印链表中所有元素的值和前后指针地址(用于调试)
func (this *MyLinkedList) Print() {
	fmt.Printf("List size: %d\n", this.size)
	fmt.Printf("Head: %v\n", this.head)
	fmt.Printf("Tail: %v\n", this.tail)
	for e := this.head; e != nil; e = e.next {
		fmt.Printf("value: %d, prev: %p, next: %p\n", e.val, e.prev, e.next)
	}
}

// 定义一个初始化链表的函数,接收一个切片作为参数,返回一个链表
func InitList(slice []int) *MyLinkedList {
	// 创建一个空链表
	list := Constructor()
	// 遍历切片,把切片中的元素依次添加到链表中
	for _, v := range slice {
		list.AddAtTail(v)
	}
	// 返回链表
	return &list
}

func main() {
	slice := []int{1, 2, 3, 4, 5}
	// 初始化链表
	list := InitList(slice)
	// 打印链表
	fmt.Println("初始化链表:")
	list.Print()

	// 在链表头部添加元素
	list.AddAtHead(0)
	// 打印链表
	fmt.Println("在链表头部添加元素:")
	list.Print()
	// 在链表尾部添加元素
	list.AddAtTail(6)
	// 打印链表
	fmt.Println("在链表尾部添加元素:")
	list.Print()
	// 在链表中间插入元素
	list.AddAtIndex(3, 7)
	// 打印链表
	fmt.Println("在链表中间插入元素:")
	list.Print()
	//获取链表下标为2的元素
	fmt.Printf("获取链表下标为2的元素:%d\n", list.Get(2))
	//获取链表下标为100的元素,应该是-1,表示不存在
	fmt.Println(list.Get(100))
	// 删除链表下标为2的元素
	list.DeleteAtInedex(2)
	// 打印链表
	fmt.Println("删除链表下标为2的元素:")
	list.Print()
}


output:
初始化链表:
List size: 0
Head: &{0 <nil> <nil>}
Tail: &{5 0xc0000080f0 <nil>}
value: 0, prev: 0x0, next: 0x0
在链表头部添加元素:
List size: 1
Head: &{0 <nil> 0xc000008078}
Tail: &{5 0xc0000080f0 <nil>}
value: 0, prev: 0x0, next: 0xc000008078
value: 0, prev: 0xc000008150, next: 0x0
在链表尾部添加元素:
List size: 1
Head: &{0 <nil> 0xc000008078}
Tail: &{6 0xc000008108 <nil>}
value: 0, prev: 0x0, next: 0xc000008078
value: 0, prev: 0xc000008150, next: 0x0
在链表中间插入元素:
List size: 1
Head: &{0 <nil> 0xc000008078}
Tail: &{6 0xc000008108 <nil>}
value: 0, prev: 0x0, next: 0xc000008078
value: 0, prev: 0xc000008150, next: 0x0
获取链表下标为2的元素:-1
-1
删除链表下标为2的元素:
List size: 1
Head: &{0 <nil> 0xc000008078}
Tail: &{6 0xc000008108 <nil>}
value: 0, prev: 0x0, next: 0xc000008078
value: 0, prev: 0xc000008150, next: 0x0

this什么意思?

this是一个方法的接收者,也就是调用这个方法的MyLinkedList类型的指针。
在Go语言中,方法是一种特殊的函数,它可以绑定到某个类型上,这个类型称为方法的接收者。方法的接收者可以是任何类型,不一定是结构体,也可以是基本类型,切片,映射等。方法的接收者可以使用任何合法的标识符命名,但是通常使用this或者self来表示。
例如,如果有一个结构体Person,定义了一个方法SayHello,接收者是*this,那么这个方法可以这样调用:

p := Person{name: "Alice"} // 创建一个Person类型的变量
p.SayHello() // 调用SayHello方法

在SayHello方法内部,this就表示调用这个方法的Person类型的指针,也就是p。所以可以使用this来访问或修改p的字段或者其他方法。例如:

func (this *Person) SayHello() {
    fmt.Println("Hello, my name is", this.name) // 访问this的name字段
    this.Greet() // 调用this的Greet方法
}

206 反转链表

攻略

将链表内的指针转变方向,使链表翻转

package main

import "fmt"

type ListNode struct {
	Val  int
	Next *ListNode
}

func reverseList(head *ListNode) *ListNode {
	var pre *ListNode
	cur := head
	for cur != nil {
		next := cur.Next //先保存下一个节点到临时变量,因为指针反转后会断开与下一个节点的链接
		cur.Next = pre   //指针反转,下一个节点指向pre
		pre = cur        //pre指向当前节点
		cur = next       //cur指向下一个节点
	}
	return pre
}

// 传入一个切片生成链表
func initList(slice []int) *ListNode {
	// 创建一个空链表
	head := &ListNode{}
	// 遍历切片元素,依次插入链表尾部
	for _, v := range slice {
		// 创建一个新节点,值为v
		newNode := &ListNode{Val: v}
		// 如果头节点为空,赋值给头节点
		if head == nil {
			head = newNode
		} else {
			tail := head
			for tail.Next != nil {
				tail = tail.Next
			}
			tail.Next = newNode
		}
	}
	return head
}

func PrintList(head *ListNode) {
	cur := head
	for cur != nil {
		fmt.Print(cur.Val, "")
		cur = cur.Next
	}
	fmt.Println()
}

func main() {
	slice := []int{1, 2, 3, 4, 5}
	head := initList(slice)
	fmt.Println("反转前链表:")
	PrintList(head)
	fmt.Println("反转后链表:")
	head = reverseList(head)
	PrintList(head)

}


output:
反转前链表:
012345
反转后链表:
543210

注意

  • 翻转指针时,需要先把下一个节点存入一个临时变量中,因为指针断开后,会与下一个节点失去联系。
  • 向后传递时,应该先将当前节点cur赋予前面的节点pre,然后再将next临时变量赋予cur,即
next := cur.Next //先保存下一个节点到临时变量,因为指针反转后会断开与下一个节点的链接
cur.Next = pre   //指针反转,下一个节点指向pre

pre = cur  //pre指向当前节点
cur = next //cur指向下一个节点

两两交换链表中的节点

攻略

package main

import "fmt"

/*
有链表dummy,1,2,3,4,5
1. 定义虚拟头节点,初始化为当前节点cur;
2. cur指向2、2指向1、1指向3;
3. 节点2作为cur
4. cur指向4、4指向3、3指向5

当链表数量位偶数时,1,2,3,4,
指针指到4,后边位nil,所以cur.next == nil为结束条件;
当链表数量位奇数时,1,2,3,4,5,
指针指到4,cur.next.next == nil 为结束条件

for循环时,首先要进行cur.next != nil的判定,否则如果先判定cur.next.next == nil
如果cur.next为空的话,会指向空指针

两两交换节点时:
先由cur-》2,此时cur-〉1会断开,所以需要先保存节点1的地址到临时变量temp
同理,节点2-》1后,节点2-〉3会断开,也要先保存节点3的地址到临时变量temp1*/

type ListNode struct {
	Val  int
	Next *ListNode
}

func SwapPairs(head *ListNode) *ListNode {
	//定义虚拟头节点指向头节点
	dummy := &ListNode{
		Next: head,
	}
	// dummy初始化为cur,通过cur指针处理后面两个节点的交换操作:1->2 交换为 2->1
	cur := dummy
	// 链表内节点数如果为偶数,如1->2->3->4->5->6, cur指针会在指向第六个节点时结束,此时结束条件为cur.Next == nil;
	// 链表内节点数如果为奇数,如1->2->3->4->5->6->7, cur指针会在指向第六个节点时结束,此时结束条件为cur.Next.Next == nil;
	// 在结束判定时,应该先写cur.Next != nil, 否则如果先写 cur.Next.Next != nil 会导致一旦存在cur.Next为空的情况,会先指向空指针,导致报错
	for cur.Next != nil && cur.Next.Next != nil {
		// 首先需要将第一个节点和第三个节点的地址保存到临时变量,因为一旦将 cur.Next -> 2以及将 2.Next -> 1,节点1和节点3的就会被断开
		temp1 := cur.Next           //存储节点1
		temp3 := cur.Next.Next.Next //存储节点3
		//下面开始交换节点的操作
		cur.Next = cur.Next.Next //前一个节点指向处于交换状态两节点1,2的节点2
		cur.Next.Next = temp1    //处于交换状态的两节点1,2中的节点2 指向 节点1
		temp1.Next = temp3       // 处于交换状态的两节点1,2中的节点1 指向后面的节点3
		cur = cur.Next.Next      // 当前节点像后移两位,处于下一对交换状态的两节点3,4的前一个位置
	}
	return dummy.Next // 交换结束后,返回头节点

}

// 定义一个函数打印链表的节点,传入头节点作为参数
func PrintList(head *ListNode) {
	// 创建一个临时变量,指向头节点
	temp := head
	// 遍历链表,打印链表的值
	for temp != nil {
		fmt.Print(temp.Val, " ")
		temp = temp.Next
	}
	fmt.Println()
}

// 定义一个函数,将一个切片转换为链表.传入slice切片,返回链表头节点
func SliceToList(slice []int) *ListNode {
	// 创建一个空链表
	var head *ListNode = nil
	// 遍历切片中的元素
	for _, v := range slice {
		// 创建一个新节点
		newNode := &ListNode{
			Val: v,
		}
		// 如果头节点为空,将新节点赋值给头节点
		if head == nil {
			head = newNode
		} else {
			// 找到链表的最后一个节点,将新节点放在他后面
			tail := head // 从头节点开始找
			for tail.Next != nil {
				tail = tail.Next
			}
			tail.Next = newNode
		}
	}
	return head
}
func main() {
	// 创建一个切片,将其转换为链表
	slice := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
	head := SliceToList(slice)
	//打印原始链表
	PrintList(head)
	// 两两交换链表中的节点
	head = SwapPairs(head)
	//打印交换后的链表
	PrintList(head)

}

output:
0 1 2 3 4 5 6 7 8 9 
1 0 3 2 5 4 7 6 9 8 

注:

  • 链表内节点数如果为偶数,如1->2->3->4->5->6, cur指针会在指向第六个节点时结束,此时结束条件为cur.Next == nil;
  • 链表内节点数如果为奇数,如1->2->3->4->5->6->7, cur指针会在指向第六个节点时结束,此时结束条件为cur.Next.Next == nil;
  • 在结束判定时,应该先写cur.Next != nil, 否则如果先写 cur.Next.Next != nil 会导致一旦存在cur.Next为空的情况,会先指向空指针,导致报错
  • 首先需要将第一个节点和第三个节点的地址保存到临时变量,因为一旦将 cur.Next -> 2以及将 2.Next -> 1,节点1和节点3的就会被断开
  • 当前节点cur向后移两位,处于下一对交换状态的两节点3,4的前一个位置;

删除链表中倒数第N个节点

试题、视频与攻略

package main

import "fmt"

/* 删除第n个节点 */

type ListNode struct {
	Val  int
	Next *ListNode
}

func RemoveNthFromEnd(head *ListNode, n int) *ListNode {
	/* 1. 设置一个快指针right,一个慢指针left,初始在虚拟头节点位置;
	2. 要定位倒数第n个节点,先让快指针向后走n步,然后两个指针一起向后走,知道快指针right指向nil,此时left就指向了倒数第n个节点
	3. 如果要删除left指向的节点,需要将left先向前移动一个位置left-1,然后将left-1指向left+1,
	4. 为了方便,在最开始的时候,先让right向后走n+1个节点,此时遍历完毕后,left指向的就是倒数第n-1个节点了 */
	dummy := &ListNode{
		Next: head,
	}
	left := dummy
	right := dummy
	i := 1
	for right != nil {
		right = right.Next
		if i > n+1 {
			left = left.Next
		}
		i++
	}
	left.Next = left.Next.Next
	return dummy.Next
}

// 打印链表
func PrintList(head *ListNode) {
	temp := head
	for temp != nil {
		fmt.Print(temp.Val, " ")
		temp = temp.Next
	}
	fmt.Println()
}

// 生成链表
func SliceToList(slice []int) *ListNode {
	/// 初始化头节点
	head := &ListNode{}
	for _, v := range slice {
		newNode := &ListNode{
			Val: v,
		}
		if head == nil {
			head = newNode
		} else {
			//找到最后一个节点
			tail := head
			for tail.Next != nil {
				tail = tail.Next
			}
			tail.Next = newNode
		}
	}
	return head
}

func main() {
	slice := []int{1, 2, 3, 4, 5, 6, 7, 8, 9}
	head := SliceToList(slice)
	PrintList(head)
	// 删除倒数第3个节点
	RemoveNthFromEnd(head, 3)
	PrintList(head)
}


output:
0 1 2 3 4 5 6 7 8 9 
0 1 2 3 4 5 6 8 9 

注:

  • 设置一个快指针right,一个慢指针left,初始在虚拟头节点位置;
  • 要定位倒数第n个节点,先让快指针向后走n步,然后两个指针一起向后走,知道快指针right指向nil,此时left就指向了倒数第n个节点
  • 如果要删除left指向的节点,需要将left先向前移动一个位置left-1,然后将left-1指向left+1,
  • 为了方便,在最开始的时候,先让right向后走n+1个节点,此时遍历完毕后,left指向的就是倒数第n-1个节点了

链表相交

代码随想录

注意:

这里的相交是指针相同,不仅仅是值相同,即该相交指针后共用一个链表

即,如果一个链表A比链表B长了5个节点,那么链表A的前5个节点就是没用的,因为链表B不够长。

只有遍历到长度相等后,才有相交的可能性。

看来相交也讲究青梅竹马啊。

假如有链表A: 1,2,3,4,5,6,7,8,9
链表B: 1,2,7,8,9
两个链表相交部分为7,8,9
当将两个链表结合在一起时,不论是A+B还是B+A

1,2,3,4,5,6,7,8,9,1,2,7,8,9
1,2,7,8,9,1,2,3,4,5,6,7,8,9

假设有两个指针分别遍历上边两个链表,如果相交的话,那么肯定会在一个地方,两个指针相等,后面的就是相交的链表;
当然,如果不相交的话,那么遍历完都是nil。
这个方法的详细解释应该看官方攻略的双指针解法, 讲的比较详细

func GetIntersectionNode(headA, headB *ListNode) *ListNode {
	if headA == nil || headB == nil {
		return nil
	}
	pa, pb := headA, headB
	for pa != pb {
		if pa == nil {
			pa = headB
		} else {
			pa = pa.Next
		}

		if pb == nil {
			pb = headA
		} else {
			pb = pb.Next
		}
	}
	fmt.Println(pa.Val)
	return pa
}

坑:
最开始的时候依旧是定义了两个切片slice转成链表的形式生成了两个链表,寻找相交链表
但是运行了好多次都是得到的nil
为什么leetcode能运行成功,到本地就运行不成功了呢?
因为我定义了两个slice啊!!!两个slice里面元素的地址当然都不一样啦!!!只是值是相同的而已
困扰了我半个小时,可恶!!!

环形链表II

代码随想录
讲的很详细

func detectCycle(head *ListNode) *ListNode {
    left, right := head, head
    // 记得这个for循环的条件,不是for right.Next != nil && right.Next.Next != nil
    // 可能是因为right本身就可能是nil
    for right != nil && right.Next != nil {
        left = left.Next
        right = right.Next.Next
        if left == right {
            // 根据代码随想录攻略中给的公式,head走到环入口节点时,left正好走完当前环后又走了n-1圈
            // 两个指针正好在环入口处相遇,所以left==head时,正好是在环入口处
            for left != head {
                head = head.Next
                left = left.Next
            }
            return head
        }
        
    }
    return nil
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值