1. 程式人生 > >【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 <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) 且不用改變陣列 思路參考問題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 ;             } };