(全)关于链表节点(中间节点及其前驱后驱节点)的双指针查找问题
LEETCODE很多链表的题目都涉及链表中间节点的查询问题。双指针法是解决这类问题的快速算法。然而由于链表的奇偶数不同,链表为空或者仅仅有一个节点等因素、双指针法很容易出现越界和其他特殊情况的细节BUG。在此我针对一些典型节点,整理一些算法。仅供参考。
中间节点及其前驱和后驱节点定义
- 节点结构体Node
class Node {
int val;
Node *next;
};
- 中间节点(MidNode)
奇数链条:对于链表长度为奇数k的链表而言,中间节点为其k/2+1。
例:1->2->3->4->5 >6->7->8->9->NULL。5为其中间节点。
偶数链条:对于链表长度为奇数k的链表而言,中间节点为k/2。
例:1->2->3->4->5 ->6->7->8->NULL。4为其前中间节点。
当然,存在另一种情况,5也可称之为后中间节点。本文默认为中间节点4。 - 中间结点前驱节点(FRMidNode):为中间节点的前一个节点。
- 中间节点后驱节点(BEMideNode):为中间节点的后一个节点。
节点查找双指针算法
- 中间节点查找算法
Node*findMidNode(Node* &head) {
if (!head || !head->next) return head;
Node *fast = head, *slow = head;//这里注意,快慢指针是同一点起步。同时如果我们定义偶数链条的后一个节点为中间节点的话,那么此处的*slow = head->next;*fast = head->next;自己可以试一下。
while (fast->next&&fast->next->next)//这里格外注意:因为fast指针走了两步,所以不是first!=NULL;而是他的第一后继和第二后继!
{
fast = fast->next->next;
slow = slow->next;
}
return slow;
- 中间节点前驱节点查找算法
这个算法的目的很简单,也很明确。当我们的任务是删除单链表的中间节点时候,我们不得不找到他的前驱节点。这个算法和中间节点查找算法的不同之处。
Node*findRRMidNode(Node* &head) {
if (!head || !head->next||!head->next->next) return head;//此处要排除两个节点的情况。
Node *fast = head->next->next, *slow = head;//这里注意一下,因为我们需要寻找中间节点的前驱节点,所以不得不让快指针先走一步。
while (fast->next&&fast->next->next)//这里格外注意:因为fast指针走了两步,所以不是first!=NULL;而是他的第一后继和第二后继!
{
fast = fast->next->next;
slow = slow->next;
}
return slow;
- 中间节点后驱节点
这个其实没有必要说的,因为很简答,但是不想改前文内容了,还是说一下吧。直接找到中间节点后,slow指针指向的节点就是了。
案例
【题目描述】给定链表的头结点head,实现删除链表中间节点的函数。
例如:
NULL,不删除任何节点;
1->NULL,不删除任何节点;
1->2->NULL,删除节点1;
1->2->3->NULL,删除节点2;
1->2->3->4->NULL,删除节点2;
1->2->3->4->5->NULL,删除节点3;
[题解]我们需要找到中间节点的前驱节点,然后令他指向中间节点的后驱节点就OK。
Node*delidNode(Node* &head) {
if (!head || !head->next) return head;
if (!head->next->next) return head->next;//注意,两个节点的话是无法找到其中间节点的。所以这里特殊处理。
Node *fast = head->next->next, *slow = head;//这里注意一下,因为我们需要寻找中间节点的前驱节点,所以不得不让快指针先走一步。
while (fast->next&&fast->next->next)//这里格外注意:因为fast指针走了两步,所以不是first!=NULL;而是他的第一后继和第二后继!
{
fast = fast->next->next;
slow = slow->next;
}
slow->next = slow->next->next;
return head;
**附注:**这里的链表都默认为不带头指针的链表,head为其第一节点。
当循环判断条件为while (fast->next&&fast->next->next)
,此时快慢指针初始位置和最后循环完毕时,满指针所指示节点关系如下。这里1为链表第一节点。
fast指针初始位置 | slow指针初始位置 | 最后输出slow指针指向 |
---|---|---|
1 | 1 | 前中间节点 |
2 | 2 | 后中间结点 |
1 | 3 | 前中间节点前驱节点 |
1 | 2 | 后中间节点前驱节点 |