一、对快慢指针的理解
快慢指针中的快慢指的是移动的步长,即每次向前移动速度的快慢。例如可以让快指针每次沿链表向前移动2,慢指针每次向前移动1次。
二、应用
1.链表的中间结点
-
题目(力扣876):给定一个带有头结点 head 的非空单链表,返回链表的中间结点。如果有两个中间结点,则返回第二个中间结点。
示例1:输入:[1,2,3,4,5]
输出:此列表中的结点 3
返回的结点值为 3 。
示例2:输入:[1,2,3,4,5,6]
输出:此列表中的结点 4
返回的结点值为4。 -
思考:
定义指针slow,fast,快指针移动的步长为慢指针的2倍,当快指针到达链表末尾,此时慢指针正处于链表正中间即指向中间结点。
如果链表结点数为偶数个则返回第二个中间结点,我们只需要注意循环中的判断条件即可达到要求,具体看图解:
-
如果要求偶数个结点,输出左中间结点,那么改一下循环判断条件即可,具体看图解:
代码:
struct ListNode* middleNode(struct ListNode* head){
struct ListNode *fast = head, *slow = head;
if( head == NULL || head->next == NULL) { //特殊情况,链表为空或只有一个结点
return head;
}
while(fast != NULL && fast->next != NULL) { //必须判断快指针p->next是否为NULL,否则p=p->next>next-会出错
slow = slow->next;
fast = fast->next->next;
}
return p;
}
2.判断单链表是否存在环
- 题目(力扣414):
- 思考:如果是环形链表的话,快慢指针最终会相遇,类似于环形跑道中两人一直赛跑,跑的快的人总会比慢的人多跑一圈此时两人相遇。
- 代码:
bool hasCycle(struct ListNode *head) {
struct ListNode *slow = head, *fast = head; //p为慢指针,q为快指针
if(head == NULL) {
return false;
}
while(slow !=NULL && fast != NULL && fast->next != NULL) { //必须判断p->next是否为NULL,如果p->next == NULL,那么q=q->next->next会出错
slow = slow->next;
fast = fast->next->next;
if(slow == fast) { //相遇,存在环
return true;
}
}
return false;
}
3.输出链表中倒数第k个节点
- 题目:(力扣面试题22)
思考:
假设链表总结点为n,要想从倒数第k个节点输出,指针则应走(n-k)步再输出,那怎样才能让指针走(n-k)步呢?
这时定义快慢指针fast=head,slow=head, fast指针先走k步,接下来slow在链表头部随着fast一同走,直至fast==NULL,此时slow就走了(n-k)步。 - 代码:
struct ListNode* getKthFromEnd(struct ListNode* head, int k){
struct ListNode *fast = head, *slow = head;
while (k != 0) { //快指针先走k步
fast = fast->next;
k--;
}
while (fast) { //走剩余的(n-k)步
fast = fast->next;
slow = slow->next;
}
return head;
}