本文參考自《劍指offer》一書,代碼采用Java語言。
題目
在一個長度為n+1的數組里的所有數字都在1到n的范圍內,所以數組中至少有一個數字是重復的。請找出數組中任意一個重復的數字,但不能修改輸入的數組。例如,如果輸入長度為8的數組{2, 3, 5, 4, 3, 2, 6, 7},那么對應的輸出是重復的數字2或者3。
思路
數組長度為n+1,而數字只從1到n,說明必定有重復數字。可以由二分查找法拓展:把1~n的數字從中間數字m分成兩部分,若前一半1~m的數字數目超過m個,說明重復數字在前一半區間,否則,在后半區間m+1~n。每次在區間中都一分為二,知道找到重復數字。
更簡單的思路:把該數組看作一個鏈表,下標代表當前結點,值代表next指針,具體參考Find the Duplicate Number,時間復雜度僅為O(n)
測試用例
1.數組中帶一個或多個重復數字
2.數組中不包含重復的數字
3.無效輸入測試用例(空數組,數組數字越界等)
完整Java代碼
(含測試代碼)
/** * * @Description 不修改數組找出重復的數字 * * @author yongh * @date 2018年7月16日 上午11:47:44 */ /* * 題目:在一個長度為n+1的數組里的所有數字都在1到n的范圍內,所以數組中至 * 少有一個數字是重復的。請找出數組中任意一個重復的數字,但不能修改輸入的 * 數組。例如,如果輸入長度為8的數組{2, 3, 5, 4, 3, 2, 6, 7},那么對應的 * 輸出是重復的數字2或者3。 */ public class FindDuplication2 { /** * 找到數組中一個重復的數字 * 返回-1代表無重復數字或者輸入無效 */ public int getDuplicate(int[] arr) { if (arr == null || arr.length <= 0) { System.out.println("數組輸入無效!"); return -1; } for (int a : arr) { if (a < 1 || a > arr.length - 1) { System.out.println("數字大小超出范圍!"); return -1; } } int low = 1; int high = arr.length - 1; // high即為題目的n int mid, count; while (low <= high) { mid = ((high - low) >> 2) + low; count = countRange(arr, low, mid); if (low == high) { if (count > 1) return low; else break; // 必有重復,應該不會出現這種情況吧? } if (count > mid - low + 1) { high = mid; } else { low = mid + 1; } } return -1; } /** * 返回在[low,high]范圍中數字的個數 */ public int countRange(int[] arr, int low, int high) { if (arr == null) return 0; int count = 0; for (int a : arr) { if (a >= low && a <= high) count++; } return count; } // ==================================測試代碼================================== /** *數組為null */ public void test1() { System.out.print("test1:"); int[] a = null; int dup = getDuplicate(a); if (dup >= 0) System.out.println("重復數字為:" + dup); } /** *數組數字越界 */ public void test2() { System.out.print("test2:"); int[] a = { 1, 2, 3, 4 }; int dup = getDuplicate(a); if (dup >= 0) System.out.println("重復數字為:" + dup); } /** *數組帶重復數字 */ public void test3() { System.out.print("test3:"); int[] a = { 1, 2, 3, 2, 4 }; int dup = getDuplicate(a); if (dup >= 0) System.out.println("重復數字為:" + dup); } public static void main(String[] args) { FindDuplication2 f2 = new FindDuplication2(); f2.test1(); f2.test2(); f2.test3(); } }

test1:數組輸入無效!
test2:數字大小超出范圍!
test3:重復數字為:2
復雜度
時間復雜度說明:函數countRange()將被調用O(logn)次,每次需要O(n)的時間。
時間復雜度:O(nlogn) (while循環為O(logn),coutRange()函數為O(n))
空間復雜度:O(1)