数据结构与算法之双指针法

735 阅读3分钟

双指针法

双指针法包括快慢指针法,对撞指针法。

快慢指针法,两个指针都从头出发,快指针移动速度比慢指针快。

对撞指针法,一个指针从头出发,一个指针从尾出发,最后两个指针碰头。


两道题

  1. 验证回文字符串

给定一个字符串,验证它是否是回文串,只考虑字母和数字字符,可以忽略字母的大小写

说明:本题中,我们将空字符串定义为有效的回文串。

示例 1:

输入: "A man, a plan, a canal: Panama" 输出: true 示例 2:

输入: "race a car" 输出: false


解题思路: 使用对撞指针法,一个指针从头遍历,一个指针从尾部遍历,当两个指针能够相遇,则说明字符串回文,否则不回文。

class Solution {
public:
    bool isPalindrome(string s) {
        int length = s.length();
        transform(s.begin(), s.end(), s.begin(), ::tolower);
        int i = 0, j = length - 1;
        while(i < j){
            // 去除不考虑的字符,只比对字母和数字字符。
            while(!('a' <= s[i] && s[i] <= 'z' || '0' <= s[i] && s[i] <= '9')){
                i++;
                if(i == length){
                    return true;
                }
            }
            while(!('a' <= s[j] && s[j] <= 'z' || '0' <= s[j] && s[j] <= '9')){
                j--;    
                
            }
            
            if(s[i] != s[j]){
                return false;
            }
            i++;
            j--;
        }
        // i,j相遇,字符串回文,返回true
        return true;
     }
};

  1. 环形链表

给定一个链表,判断链表中是否有环。

为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。

示例 1:

输入:head = [3,2,0,-4], pos = 1 输出:true 解释:链表中有一个环,其尾部连接到第二个节点。

示例 2:

输入:head = [1,2], pos = 0 输出:true 解释:链表中有一个环,其尾部连接到第一个节点。

示例 3:

输入:head = [1], pos = -1 输出:false 解释:链表中没有环。


解题思路:使用快慢指针法,一个快指针,一个慢指针。如果链表不存在环,快指针会先到达链表尾部,此时就可以返回false。如果链表存在环,那么快指针最终会追上慢指针,与慢指针相遇,这时返回true。

为什么快指针一定会和慢指针相遇

我们将慢指针的移动过程分为两个阶段:非环部分与环形部分。

  1. 慢指针在走完非环部分阶段后进入环形部分:此时,快指针必然早一步进入了环形部分(因为它移动快),快指针迭代次数=非环部分长度=N。
  2. 两个指针都在环形部分中:考虑两个在环形跑道上的选手,快指针叫快跑者,慢指针叫慢跑者。快跑者每次移动两步而慢跑者每次移动一步。速度差值为1,因此需要经过 (二者之间的距离/速度差值 )次迭代后,快跑者可以与慢跑者相遇(追上)。

想象一下,在一个环形跑道上,是不可能相遇不了的,一定会相遇的。因为速度差值为1,二者之间的距离差距最大就为环形部分的长度K。每迭代一次,距离缩小1。最大的迭代次数都不会超过环形部分的长度。

为什么相遇就一定有环?

快指针比较快,如果没有环,它会到达NULL,结束。不会再与慢指针相遇。因此,相遇即是有环。

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    bool hasCycle(ListNode *head) {
        // 只有一个结点或不存在结点则一定没有环
        if(head == NULL || head->next == NULL){
            return false;
        }
        ListNode* fast = head, * slow = head;
        while(fast && fast->next){
            slow = slow->next;
            fast = fast->next->next;
            if(slow == fast){
                return true;
            }
        }
        //fast == NULL or fast->next == NULL无环
        return false;
        
    }
};