leetcode——876.链表的中间结点
问题描述
给定一个头结点为 head 的非空单链表,返回链表的中间结点。
如果有两个中间结点,则返回第二个中间结点。
来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/middle-of-the-linked-list
1.1 示例
输入:[1,2,3,4,5]
输出:此列表中的结点 3 (序列化形式:[3,4,5])
返回的结点值为 3 。 (测评系统对该结点序列化表述是 [3,4,5])。
注意,我们返回了一个 ListNode 类型的对象 ans,这样:
ans.val = 3, ans.next.val = 4, ans.next.next.val = 5, 以及 ans.next.next.next = NULL.
输入:[1,2,3,4,5,6]
输出:此列表中的结点 4 (序列化形式:[4,5,6])
由于该列表有两个中间结点,值分别为 3 和 4,我们返回第二个结点。
1.2 提示
- 单链表遍历法:先遍历一次,计算链表的长度,进而计算链表中间结点的下标(注意偶数结点的时候,得到的是中间的第二个结点),然后再遍历一次,来到所要求结点的位置。
缺点:
必须先遍历完整个链表,然后才可以进行计算中间结点坐标,再遍历到一半,找到中间结点;
在链表的长度很长的时候,这种方法之前的等待会很久。
- 快慢指针(同步指针): 使用两个指针变量,刚开始都位于链表的第 1 个结点,一个永远一次只走 1 步,一个永远一次只走 2
步,一个在前,一个在后,同时走。这样当快指针走完的时候,慢指针就来到了链表的中间位置。
算法思想:快慢指针的前进方向相同,且它们步伐的「差」是恒定的,根据这种确定性去解决链表中的一些问题。
代码
单链表遍历法
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode* middleNode(struct ListNode* head) {
// 第一步,先遍历链表,求链表的长度
// int n = 0, j = 0, k = 0;
int n = 0, j = 0;
// 1、求出单链表的长度n
struct ListNode* cur = head;
while (cur != NULL) {
n++;
cur = cur->next;
}
/* 2、按照常规的想法分偶数和奇数两种情况
if (n % 2 == 0) {
// 链表长度是偶数
cur = head;
while (cur != NULL && j < n/2 ) { // 有两个中间节点,求第2个
j++;
cur = cur->next;
}
return cur;
} else {
// 链表长度是奇数
cur = head;
while (cur != NULL && k < n/2 ) { // 只有1个中间节点
k++;
cur = cur->next;
}
return cur;
}
*/
// 2、上面的代码明显是重复的,可以提炼并简化为下面的一段代码
cur = head;
while (cur != NULL && j < n/2 ) {
j++;
cur = cur->next;
}
return cur;
}
快慢指针法
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode* middleNode(struct ListNode* head){
//定义快慢两个指针
struct ListNode* slow = head;
struct ListNode* fast = head;
//快指针走2步,慢指针走1步,当快指针走到终点时,慢指针就会停在中间
while(fast != NULL && fast->next != NULL){
fast = fast->next->next;
slow = slow->next;
}
return slow;
}