【LeetCode & 剑指offer刷题】数组题5:3 数组中重复的数字(287. Find the Duplicate Number)

【LeetCode & 剑指offer 刷题笔记】目录(持续更新中...)

287. Find the Duplicate Number
Given an array nums containing n + 1 integers where each integer is between 1 and n (inclusive), prove that at least one duplicate number must exist. Assume that there is only one duplicate number, find the duplicate one.
Example 1:
Input: [1,3,4,2,2]Output: 2
Example 2:
Input: [3,1,3,4,2]
Output: 3
Note:
  1. You must not modify the array (assume the array is read only).
  2. You must use only constant, O(1) extra space.
  3. Your runtime complexity should be less than O(n2).
  4. There is only one duplicate number in the array, but it could be repeated more than once.
//问题:查找数组中的重复数字长度为n+1,所有数字在1~n范围内,找出数组中任意一个重复数字,不能修改数组
/*
方法一:sort
若可以改变数组,可以直接sort,O(nlogn),O(1)
若不能改变数组,可以复制vector,再sort,O(nlogn), O(n)
*/
class Solution
{
public:
    int findDuplicate(vector<int>& nums)
    {
        if(nums.empty()) return -1; //表示没有重复数字
       
        sort(nums.begin(), nums.end());
        for(int i = 1; i<nums.size(); i++)
        {
            if(nums[i] == nums[i-1]) return nums[i];
        }
        return -1;
       
    }
};
/*
方法二:哈希表
将元素当做key值,如果之前出现过了,则返回重复数字
O(n), O(n)
*/
class Solution
{
public:
    int findDuplicate(vector<int>& nums)
    {
        if(nums.empty()) return -1; //表示没有重复数字
       
        unordered_set<int> seen;
        for(int num:nums)
        {
            if(seen.find(num) != seen.end()) return num; //如果在set中找到了该值,说明重复了
            else seen.insert(num); //否则插入key值
        }
        return -1;
       
    }
};
/*
方法三:也可以参见《剑指offer》P39中的解法(下标比较交换法) O(n),O(1)但会改变数组
问题:长度为n的数组中所有数字在0~n-1范围内,找出重复的数字
例:1 2 0 3 2 4
i=0,
    2 1 0
    0 1 2
i=1...
 
*/
#include <algorithm>
class Solution {
public:
    // Parameters:
    //        numbers:     an array of integers
    //        length:      the length of array numbers
    //        duplication: (Output) the duplicated number in the array number
    // Return value:       true if the input is valid, and there are some duplications in the array number
    //                     otherwise false
   
    bool duplicate(int a[], int length, int* duplication)
    {
        //1.数组异常情况处理
        if(a == nullptr || length < 0) return false;
        //2.数组值不符合条件时的处理
        for(int i = 0; i < length; i++)
        {
            if(a[i]<0 || a[i]>length-1) return false;
        }
       
        /*
        3. 比较a[i]与i
            如果相等,i++
            如果不相等,比较a[i]与a[a[i]],若相等,为重复数,退出;若不相等就交换。
        */
        for(int i = 0; i<length; i++)
        {
            while(a[i] != i)
            {
                if(a[i] == a[a[i]])
                {
                    *duplication = a[i];
                    return true; //这里也可以返回重复的数字                   
                }
                else
                    swap(a[i], a[a[i]]); //每个数字最多交换2次(第一次为被交换方,第二次为交换方,到应处位置),故整个程序复杂度为O(n),o(1)
                    //a[i]被换到它应处的位置
            }
        }
       
        return false;
    }
};
 
/*
方法三:快慢指针法   O(n),O(1) 且不用改变数组
索引看做当前结点地址,将存储数看做指向下一个结点的指针,则重复数字即为环入口(索引或结点地址)
把第一个结点当做头结点
例子:
1 4 3 5 2 2
索引分别为0、1、2、3、4、5
0    1    4    2    3    5
1 -> 4 -> 2 -> 3 -> 5 -> 2
               ↑←-------←↓
注:
    (1)如果在多一个重复数字2,则会多一个结点指向2,但是该结点永远无法被访问到,因为没有结点指向它
    (2)如果数组中不存在重复数字,则为循环链表,相当于循环链表,这个时候需返回-1.
    (3)如果多个数字重复,只有最前面的重复数字构成环,其他重复数字不会在链表中,所以只能检测一个重复数字
    (4)如果数组中有数字0,则该结点会指向头结点,从而形成循环链表,而其他结点会被丢失(所以题目限定不包含0,如果要包含0或负数的话,可以把整个数组预处理一遍,为负数时说明输入非法,为0时,可以将各数加1)
 
*/
class Solution
{
public:
    int findDuplicate(vector<int>& nums)
    {
        if(nums.empty()) return -1; //表示没有重复数字
       
        int slow = nums[0];
        int fast = nums[nums[0]];
        while(slow != fast) //假设存在重复数字,则会在环内相遇,假设不存在重复数字,形成循环链表,在头结点相遇
        {
            slow = nums[slow];
            fast = nums[nums[fast]];
        }
       
        int entry = 0;
        if(entry == slow) return -1; //如果不存在重复数字,为循环链表(环入口在起始位置),则返回-1
        while(entry != slow)
        {
            entry = nums[entry];
            slow = nums[slow];
        }
        return entry;
       
    }
};
 

 

posted @ 2019-01-05 14:02  wikiwen  阅读(341)  评论(0编辑  收藏  举报