【Java】 劍指offer(2) 不修改數組找出重復的數字


 

本文參考自《劍指offer》一書,代碼采用Java語言。

更多:《劍指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
FindDuplication2

 

復雜度

時間復雜度說明:函數countRange()將被調用O(logn)次,每次需要O(n)的時間。

時間復雜度:O(nlogn)  (while循環為O(logn),coutRange()函數為O(n))

空間復雜度:O(1)

 

 更多:《劍指Offer》Java實現合集

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM