LeetCode - 链表快慢指针类总结

链表快慢指针

      快慢指针中的快慢指的是指针沿链表移动的步长,即每次向前移动速度的快慢。快指针每次沿链表向前移动两步fast=fast->next->next,慢指针每次向前移动一步slow=slow->next

一、回文链表

请判断一个链表是否为回文链表。用 O(n) 时间复杂度和 O(1) 空间复杂度解决此题。

示例 1:输入: 1->2 ;输出: false
示例 2:输入: 1->2->2->1 ;输出: true

      分析:使用快慢指针来找到链表的中点: 首先我们设置两个指针slow和fast,slow指针每次移动一步,fast指针每次移动两步;如果链表中节点个数为奇数时,当快指针无法继续移动时,慢指针刚好指向中点;如果链表中节点个数为偶数时,当快指针走完,慢指针指向中点前一个节点。然后反转中间节点后的链表与中间节点前未反转的链表进行比较。

// 使用快慢指针来找到链表的中点:
ListNode *slow=head,*fast=head;
while(fast->next&&fast->next->next)
{
	slow=slow->next;//slow指针每次走一步
	fast=fast->next->next;//fast指针每次走两步
}
class Solution {
public:
    bool isPalindrome(ListNode* head) {//O(n)、O(1)
    ListNode* slow = head, *fast = head,  *prev = nullptr;
	while(fast->next&&fast->next->next)
	{
		slow=slow->next;//slow指针每次走一步
		fast=fast->next->next;//fast指针每次走两步
	}
    while (slow){//reverse
        ListNode* ovn = slow->next;
        slow->next = prev;
        prev = slow;
        slow = ovn;
    }
    while (head && prev){//check
        if (head->val != prev->val){
            return false;
        }
        head = head->next;
        prev = prev->next;
    }
    return true;
}
};

二、环形链表

给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。
为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。说明:不允许修改给定的链表。

      分析:快慢指针中,因为每一次移动后,快指针都会比慢指针多走一个节点,所以他们之间在进入环状链表后,不论相隔多少个节点,慢指针总会被快指针赶上并且重合,此时就可以判断必定有环。快指针每次走两步,慢指针每次走一步,那么存不存在一种情况,快指针直接“越过了”慢指针,到达了慢指针的下一个格子呢?答案是不可能的,我们假设这种状态存在,也就是快指针越过了慢指针,领先慢指针一个格子,逆推一步,慢指针倒退一格,快指针倒退两格,那么快慢指针依然相遇,也就是说,快指针永远不可能直接越过慢指针,到达后一格。
      利用快慢指针来判断链表中是否有环的思路就是

  1. 设置两个指针,慢指针每次走一步,快指针每次走两步;
  2. 如果快慢指针相遇,那么链表含有环;
  3. 如果快指针到达了链表尾部且没有与慢指针相遇,那么链表不含有环。如不存在环,fast遇到NULL退出。
 class Solution {
  public:
	  bool hasCycle(ListNode *head) {
		  if (head == nullptr)
			  return false;
		  ListNode* slow = head;
		  ListNode* fast = head;
		  while(fast->next&&fast->next->next)
		  {
			  slow = slow->next;
			  fast = fast->next->next;
			  if (slow == fast)
				  return true;
		  }
		  return false;
	  }
  };

      利用双指针来找出环形链表的环入口指针的思路就是:

  1. 先定义两个指针p1和p2指向链表的头结点
  2. 如果链表中有n个节点,则指针p1先在链表上向前移动n步,然后两个指针以相同的速度向前移动;
  3. 当第二个指针指向环的入口节点时,第一个指针已经围绕着环走了一圈,又回到了入口节点。
  class Solution {
  public:
	  ListNode *detectCycle(ListNode *head) {
		  //验证是否是环形链表
		  if (head == nullptr)
			  return head;
		  ListNode* slow = head;
		  ListNode* fast = head;
		  ListNode *meetNode= nullptr;
		  while(fast->next&&fast->next->next)
		  {
			  slow = slow->next;
			  fast = fast->next->next;
			  if (slow == fast) {
				  meetNode = slow;
				  break;
			  }
		  }
		  slow = head;
		  fast = head;
		  if (meetNode == nullptr)
			  return nullptr;
		  //得到环的数目
		  int cyclenum = 1;
		  ListNode* temp = meetNode;
		  while (temp->next!=meetNode) {
			  cyclenum++;
			  temp = temp->next;
		  }
		  //先移动fast节点cysclenum步
		  for (int i = 0; i < cyclenum; i++)
			  fast = fast->next;
		 //同时移动slow和fast
		  while (slow != fast) {
			  slow = slow->next;
			  fast = fast->next;
		  }
		  return slow;
	  }
  };



三、删除链表倒数第N个节点

给定一个链表,删除链表的倒数第 n 个节点,并且返回链表的头结点。
示例:给定一个链表: 1->2->3->4->5, 和 n = 2。当删除了倒数第二个节点后,链表变为 1->2->3->5

      分析:利用双指针找到链表倒数第N个节点: 一个快指针fast,一个慢指针slow,让快指针先走n步,再让快慢指针一起走,当快指针走到链表最后,慢指针正好走在链表的倒数第n+1个位置,此时删除slow指针的后一个值,即删除了链表第n个位置的值。
      再考虑一些特殊情况,比如链表只有一个值或无值时,任意有效删除之后,这时链表为空。还有要注意的是可能是要删除第一个节点,这时提前走n步的快指针会最后变为空指针,这个时候可以直接返回head -> next。

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
	ListNode* removeNthFromEnd(ListNode* head, int n) {
		if(!head || !head -> next) return NULL;
		ListNode* slow = head;
		ListNode* fast = head;
		for (int i = 0; i < n; i++)
		{
			if (!fast->next)
			{
				 return head -> next; //要注意的是可能是要删除第一个节点,这个时候可以直接返回head -> next
			}
			fast = fast->next;
		}
		while (fast->next){
			slow = slow->next;
			fast = fast->next;	
		}
		slow->next = slow->next->next;
        return head;
	}
};



四、反转链表

反转一个单链表。

示例:输入: 1->2->3->4->5->NULL; 输出: 5->4->3->2->1->NULL

     分析:采用双指针迭代的方法反转链表。 慢指针prev作为快指针反转后的next节点,同时每次反转完一个节点后,更新快慢指针分别向前移动一步。直到快指针为nullptr,返回prev作为反转后的头节点。

class Solution {
  public:
	  ListNode* reverseList(ListNode* head) {
		  if (head == nullptr || head->next == nullptr)
			  return head;
		  ListNode* fast = head;
		  ListNode* prev = nullptr;
		  while (fast)
		  {
			  ListNode* temp = fast->next;
			  fast->next = prev;
			  prev = fast;
			  fast = temp;
		  }
		  return prev;
	  }
  };

五、相交链表

编写一个程序,找到两个单链表相交的起始节点。

如下面的两个链表,相交的起始节点为c1。


在这里插入图片描述

分析:

  1. 先让A链表和B链表分别走的各自链表的结尾计算两个链表的长度。
  2. 若两个链表的最后的节点不相等的话,则说明链表不重合,返回nullptr。
  3. 否则存在链表相交。计算两个链表的长度差为s,先让长链表走s步。
  4. 长链表和短链表同时向前移动,若节点指针相同时,即找到了相交节点。
class Solution {
public:
    ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
        int lenA=0;
        int lenB=0;
        
        ListNode* curA = headA;
        ListNode* curB = headB;
        
        while(curA && curA->next)
        {
            lenA++;
            curA=curA->next;
        }
        while(curB && curB->next)
        {
            lenB++;
            curB=curB->next;
        }
       //此时curA 与curB均走到最后,若两个链表相交则curA等于curB
        //1、不相交,返回NULL
        //2、相交,求交点
        if(curA != curB)
        {
            return NULL;
        }
        else
        {
            int gap = abs(lenA-lenB);//求两个链表长度差值的绝对值
            ListNode* longlist = headA;
            ListNode* shortlist = headB;
            if(lenB > lenA)
            {
                longlist = headB;
                shortlist = headA;
            }
            
            while(gap--)
            {
                longlist = longlist->next;
            }
            
            while(1)
            {
                if(longlist == shortlist)
                {
                    return longlist;
                }
                else
                {
                    longlist = longlist->next;
                    shortlist = shortlist->next;
                }
            }
        }
        
    }
};
发布了76 篇原创文章 · 获赞 6 · 访问量 2784

猜你喜欢

转载自blog.csdn.net/u014618114/article/details/104060875