总结:链表相关的题目大部分可以用双指针解决。i.e.,找链表倒数第k个节点,找链表中间1/2节点,检测链表是否存在环。快慢指针
1、找链表中倒数第k个节点[1]
输入一个链表,输出该链表中倒数第k个节点。为了符合大多数人的习惯,本题从1开始计数,即链表的尾节点是倒数第1个节点。例如,一个链表有6个节点,从头节点开始,它们的值依次是1、2、3、4、5、6。这个链表的倒数第3个节点是值为4的节点。
/*双指针
1)p1比p2先行k-1步,当p1到达最后一个节点,p2指向倒数第k个节点
2)p1比p2先行k步,当p1到达最后终点(NULL),p2指向倒数第k个节点
*/
代码:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* getKthFromEnd(ListNode* head, int k) {
/*双指针
p1比p2先行k-1步
*/
ListNode* p1 = head;
ListNode* p2 = head;
//p1先行k-1步
int step = 0;
while (step<k-1 && p1)
{
step++;
p1 = p1->next;
}
if (!p1) return NULL;
while (p1->next)
{
p1 = p1->next;
p2 = p2->next;
}
return p2;
}
};
2、链表的中间节点[2]
给定一个带有头结点 head
的非空单链表,返回链表的中间结点。
如果有两个中间结点,则返回第二个中间结点。
思路::快慢指针,快指针p2一次走2步,慢指针p1一次走1步。
需要特殊处理的是:偶数节点是输出第二个中间节点。
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* middleNode(ListNode* head) {
if (!head)
return head;
ListNode* p1 = head, *p2=head;
while (p2->next)
{
p1 = p1->next;
p2 = p2->next;
if (p2->next)
p2 = p2->next;
}
return p1;
}
};
3、环形链表[3]
判断链表是否有环,若存在环,找出入环起点。
思路:
1)确定是否有环,快慢指针,快指针*fast每次移动2步(或多步),慢指针*slow每次移动1步,若存在环,二者一定会在环上相遇,且相遇时慢指针尚未走完1圈(证明见leetcode官方题解);否则无环。
2)确认有环(快慢指针相遇后),将其中一个移到链表起点head,而后二者每次均移动1步,相遇点即为入环起点。
附加 3)判断环的长度:其中一个指针不动,另外一个一步一步走,再次相遇时走的步数即为环的长度。
c++代码
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode *detectCycle(ListNode *head) {
if (!head)
return head;
ListNode *fast = head, *slow=head;
while (fast->next&&fast->next->next)
{
slow = slow->next;
fast = fast->next->next;
if (slow == fast) //有环,确定入环起点
{
fast = head;
while (fast != slow)
{
fast = fast->next;
slow = slow->next;
}
return slow;
}
}
return NULL;
}
};
4、回文链表[4]
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
bool isPalindrome(ListNode* head) {
/*
1. 找到链表中点(n+1)/2, p2
2. p2反转
3. 顺序比较p1和p2
4. 反转p2,恢复原样
*/
ListNode* p1 = head, *p2 = head;
while (p2)
{
p2 = p2->next;
if (p2)
{
p2 = p2->next;
p1 = p1->next;
}
}
p2 = reverseList(p1);
ListNode* p = p1;
p1 = head;
bool flag = true;
while (p1&&p2)
{
if (p1->val != p2->val)
flag = false;
p1 = p1->next;
p2 = p2->next;
}
p2 = reverseList(p);
return flag;
}
ListNode* reverseList(ListNode* head) {
if (head==NULL)
return head;
ListNode* newlist = head;
head = head->next;
newlist->next = NULL;
while (head)
{
ListNode* tmp = head->next;
head->next = newlist;
newlist = head;
head = tmp;
}
return newlist;
}
};
[1]https://leetcode-cn.com/problems/lian-biao-zhong-dao-shu-di-kge-jie-dian-lcof/
[2]https://leetcode-cn.com/problems/middle-of-the-linked-list/
[3]https://leetcode-cn.com/problems/linked-list-cycle-ii/
[4]https://leetcode-cn.com/problems/palindrome-linked-list-lcci/