双指针算法专题


一、不修改数组找出重复的数字

给定一个长度为n+1 的数组nums,数组中所有的数均在 1∼n 的范围内,其中 n≥1。

请找出数组中任意一个重复的数,但不能修改输入的数组。

样例

给定 nums = [2, 3, 5, 4, 3, 2, 6, 7]。

返回 2 或 3。

思考题:如果只能使用 O(1) 的额外空间,该怎么做呢?

//我的第一反应是一般看到重复什么的,我就会下意识的想到hash,于是我就写了一种数组hash的方法

class Solution {
public:
    int duplicateInArray(vector& nums) {
        int vim[1000010] = {0} ;
        int n = nums.size() ;
        for(int i =  0 ; i < n ;++i)
        {
            vim[nums[i]] ++ ;
            if(vim[nums[i]] > 1 )
                return nums[i] ;
        }
        return 0 ;
    }
};
//这样时间复杂度是o(n) ,空间复杂度是o(n)

然后我又看到了有大佬用模拟链表来做,觉得是很有意思的思路。于是学习了.

class Solution {
public:
    int duplicateInArray(vector& nums) {
        int slow = 0 , fast = 0 ;
        
        do
        {
            fast = nums[nums[fast]] ;
            slow = nums[slow] ;
        }while(slow != fast) ;
    
        fast = 0 ;
        
        do
        {
            fast = nums[fast] ; 
            slow = nums[slow] ;
        }while(fast != slow) ;
   return slow ;
    }
};

时间复杂度是o(n) ,空间复杂度是o(1) 。

妙在用数组模拟链表的思路:

基于是链表找环的问题,题目是这样的

剑指 Offer II 022. 链表中环的入口节点 - 力扣(LeetCode) (leetcode-cn.com)

class Solution {
public:
    ListNode *detectCycle(ListNode *head) {
        if(head == NULL || head->next == NULL) return NULL ;
        ListNode* fast = head->next->next ;
        ListNode* slow = head->next ;
        while(fast && fast->next )
        {
            fast = fast->next->next ;
            slow = slow->next ;
            if(slow == fast) 
            break ;
        }
        if(slow != fast) return NULL ;
        else fast  = head ;

        while(slow != fast)
        {
            slow = slow->next ;
            fast = fast->next ;
        }
        return fast ;
    }
};

大佬采用的是用数组下标映射值的方法,因为本题正好下标的范围其实在0-n,值的范围在1-n,也就是说存在可以相互映射的可能。

把数组全部转换成链表之后是

2->5->2 然后就是死循环了。


文章作者: 罗林
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 罗林 !
  目录