《劍指Offer》第二版之旋轉數組的最小數字(八)

目錄


題目:
把一個數組最開始的若干個元素搬到數組的末尾,我們稱之爲數組的旋轉。輸入一個遞增排序的數組的一個旋轉,輸出旋轉數組的最小元素。例如,數組{3,4,5,1,2}爲{1,2,3,4,5}的一個旋轉,該數組的最小值爲1。
思路:
我們注意到旋轉之後的數組實際上可以劃分爲兩個排序的子數組,而前面子數組的元素都大於或者等於後面子數組的元素。我們還注意到最小的元素剛好是這兩個子數組的分界線。在排序的數組中我們可以用二分查找法實現O(logn)的查找。
步驟:
1.和二分查找法一樣,我們用兩個指針分別指向數組的第一個元素和最後一個元素。按照題目中旋轉的規則,第一個元素應該是大於或者等於最後一個元素的。
2.接着我們可以找到數組中間的元素。如果該中間元素位於前面的遞增子數組,那麼它應該大於或者等於第一個指針指向的元素。此時數組中最小的元素應該位於該中間元素的後面。我們可以把第一個指針指向該中間元素,這樣可以縮小尋找的範圍。移動之後的第一個指針仍然位於前面的遞增子數組。
3.同樣,如果中間元素位於後面的遞增子數組,那麼它應該小於或者等於第二個指針指向的元素。此時該數組中最小的元素應該位於該中間元素的前面。我們可以把第二個指針指向該中間元素,這樣也可以縮小尋找的範圍。移動之後的第二個指針仍然位於後面的遞增子數組。
4.不管是移動第一個指針還是第二個指針,查找範圍都會縮小到原來的一半。接下來我們再用更新之後的兩個指針重複做新一輪的查找。
代碼:
package test;

public class RotateArray {

	public static void main(String[] args) throws Exception {
		int[] numbers = {3, 4, 5, 1, 2};
		System.out.println(min(numbers));
	}
	
	public static int min(int[] numbers) throws Exception {
		//1.數據校驗
		if(numbers == null || numbers.length <=0)
			throw new Exception("Invalid parameters");
		
		int index1 = 0;
		int index2 = numbers.length - 1;
		int indexMid = index1;
		//2.開始進行二分查找
		while(numbers[index1] >= numbers[index2]) {
			//3.如果相鄰則表明已找到對應數據
			if(index2 - index1 == 1) {
				indexMid = index2;
				break;
			}
			
			indexMid = (index1 + index2) / 2;
			
			//4.如果下標爲index1、index2和indexMid指向的三個數字相等,
			//則只能順序查找
			if(numbers[index1] == numbers[index2] 
					&&numbers[indexMid] == numbers[index1])
				return minInOrder(numbers, index1, index2);
			
			if(numbers[indexMid] >= numbers[index1])
				index1 = indexMid;
			else if (numbers[indexMid] <= numbers[index2])
				index2 = indexMid;
		}
		return numbers[indexMid];
	}

	private static int minInOrder(int[] numbers, int index1, int index2) {
		int result = numbers[index1];
		for(int i = index1 + 1; i <= index2; i++) {
			if(result > numbers[i])
				result = numbers[i];
		}
		return result;
	}
}

注:該算法的時間複雜度爲O(logn)。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章