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

【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 ( n 2 ).
  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
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) 且不用改变数组
思路参考问题Linked List Cycle II(找有环链表的环入口)
索引看做当前结点地址,将存储数看做指向下一个结点的指针,则重复数字即为环入口(索引或结点地址)
把第一个结点当做头结点
例子:
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 阅读( ...) 评论( ...) 编辑 收藏

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