首页 查找单链表中间值
文章
取消

查找单链表中间值

问题

通过一次循环查找单链表中的中间值。

解法

常规

遍历一遍,得到单链表的长度,计算出中间值位置,再遍历一遍,遍历到中间值位置即可。

代码略。

快慢指针

用两个指针 pq,开始都指向头结点。p 向后移动一位,q 向后移动两位。重复前面步骤,直到 q 到头。

如果单链表长度为奇数,q 最后可以正好到头, 此时 p 的位置即为中间值。

如果单链表长度为偶数,q 最后只能走一步到头,走不了两步。此时 p 的位置和 p.next 为中间值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
package main

import "fmt"

type Node struct {
	data int
	next *Node
}

type LinkedList struct {
	head *Node
	tail *Node
}

func (list *LinkedList) init() {
	(*list).head = nil
	(*list).tail = nil
}

func (list *LinkedList) empty() bool {
	return (*list).head == nil
}

func (list *LinkedList) append(data int) {
	node := Node{
		data: data,
		next: nil,
	}
	if (*list).head == nil {
		// 没有头部
		(*list).head = &node
		(*list).tail = &node
	} else {
		// 有头部
		(*list).tail.next = &node
		(*list).tail = &node
	}
}

func (list *LinkedList) appendMany(data ...int) {
	for _, d := range data {
		node := Node{
			data: d,
			next: nil,
		}
		if (*list).head == nil {
			(*list).head = &node
			(*list).tail = &node
		} else {
			(*list).tail.next = &node
			(*list).tail = &node
		}
	}
}

func (list *LinkedList) show() []int {
	if (*list).empty() {
		// 空链表
		return []int{}
	}
	var result []int
	p := (*list).head
	for p != nil {
		result = append(result, p.data)
		p = p.next
	}

	return result
}

func findMid(list *LinkedList) []int {
	if (*list).empty() {
		// 为空
		return []int{}
	}
	// p在后,q 在前
	p, q := (*list).head, (*list).head
	for q.next != nil {
		if q.next.next != nil {
			// 后面第二节点不为空,可以走两步
			p = p.next
			q = q.next.next
		} else {
			// 后面第二个节点为空,不能再走了,此时p和p.next为中间两个数
			return []int{p.data, p.next.data}
		}
	}
	// 此时q.next=nil,q 走到头了,p 的位置即是中间
	return []int{p.data}
}

func main() {
	var list LinkedList
	list.init()
	fmt.Printf("list: %v \n", list.show())
	fmt.Printf("list mid: %v \n\n", findMid(&list))

	list.append(2)
	fmt.Printf("list: %v \n", list.show())
	fmt.Printf("list mid: %v \n\n", findMid(&list))

	list.appendMany(2, 5, 8, 20, 21, 17)
	fmt.Printf("list: %v \n", list.show())
	fmt.Printf("list mid: %v \n\n", findMid(&list))

	list.append(9)
	fmt.Printf("list: %v \n", list.show())
	fmt.Printf("list mid: %v \n\n", findMid(&list))
}

输出:

1
2
3
4
5
6
7
8
9
10
11
list: []
list mid: []

list: [2]
list mid: [2]

list: [2 2 5 8 20 21 17]
list mid: [8]

list: [2 2 5 8 20 21 17 9]
list mid: [8 20]

拓展:查找倒数第n个

我们将上面的问题进行拓展,查找单链表中倒数第 n 个元素的值。

同样我们使用双指针,拉开 n-1 的距离即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
// 获取倒数第n个元素,如果不存在返回 -1
func findNrdFromLast(list *LinkedList, n int) int {
	if n < 1 {
		return -1
	}
	if (*list).empty() {
		// 为空
		return -1
	}
	// p在后,q 在前
	p, q := (*list).head, (*list).head
	// 使 p 和 q 拉开n-1的距离
	for i := 0; i < n-1; i++ {
		if q.next != nil {
			q = q.next
		} else {
			// p 和 q 拉不开n-1 的距离,则不存在倒数第n个元素
			return -1
		}
	}
	// 同步向右移动,直到 q 到头
	for q.next != nil {
		q = q.next
		p = p.next
	}
	// q 到头了,p 的位置即是倒数第 n 个
	return p.data
}

func main() {
	var list LinkedList
	var n = 3
	list.init()
	fmt.Printf("list: %v \n", list.show())
	fmt.Printf("list last %vrd: %v \n\n", n, findNrdFromLast(&list, n))

	list.append(2)
	fmt.Printf("list: %v \n", list.show())
	fmt.Printf("list last %vrd: %v \n\n", n, findNrdFromLast(&list, n))

	list.appendMany(2, 5, 8, 20, 21, 17)
	fmt.Printf("list: %v \n", list.show())
	fmt.Printf("list last %vrd: %v \n\n", n, findNrdFromLast(&list, n))
}

输出:

1
2
3
4
5
6
7
8
list: []
list last 3rd: -1

list: [2]
list last 3rd: -1

list: [2 2 5 8 20 21 17]
list last 3rd: 20
本文由作者按照 CC BY 4.0 进行授权