旋轉陣列的最小數字(Java)
阿新 • • 發佈:2018-12-30
題目:
把一個數組最開始的若干個元素搬到陣列的末尾,我們稱之為陣列的旋轉。輸入一個遞增排序的陣列的一個旋轉,輸出旋轉陣列的最小元素。例如陣列{3, 4, 5, 1, 2}為{1, 2, 3, 4, 5}的一個旋轉,該陣列的最小值為1。
第一思路:
剛拿到這道題我相信大多數人和我一樣想到了時間複雜度為O(n)的查詢演算法,即遍歷一遍陣列找到自己滿足題意的最小值元素。可以如果這樣做,就違背了題意旋轉陣列,即沒有利用旋轉陣列中的兩部分有序原則。故需再思考。
最優思路:
(1)旋轉陣列實際上可以劃分為兩個排序的子陣列,而且前面的子陣列元素都大於或者等於後面的子陣列的元素;(2)最小的元素剛好是這兩個字陣列的分界線;因為在排序的陣列中我們可以用二分查詢法實現O(logn)的查詢。故旋轉陣列找最小元素值也可以採取二分查詢的思路。
首先我們定義兩個指標p1指向陣列的第一個元素和p2指向陣列的最後一個元素,接著我們找到中間的元素。如果該元素位於前面遞增陣列中,它應該大於或者等於第一個指標指向的元素,我們將p1指向中間元素;如果中間元素位於後面的遞增陣列中,那麼它應該小於或者等於第二個指標指向的元素,我們把p2指向中間元素。不管移動p1還是p2,查詢的範圍都會縮小為原來一半,接下來利用更新後的兩個指標重複上面的查詢。按照上面的思路,p1始終在前面遞增陣列,p2始終在後面遞增陣列,最終p1會指向前面子陣列的最後一個元素,第二個指標會指向後面子陣列的第一個元素,即最小元素,結束迴圈。
有一特例是p1、p2和中間值都相等,這時候就老老實實迴圈遍歷一次陣列,找到最小值。
程式碼:
public class Main { //一般情況時的查詢演算法 public int Min(int arrays[]){ if(arrays == null){ try { throw new Exception("Invalid parameters"); } catch (Exception e) { e.printStackTrace(); } } int index1 = 0; int index2 = arrays.length - 1; int indexMid = index1; while(arrays[index1] > arrays[index2]){ if(index2 - index1 == 1){ //如果差值為1,則index2下標的數一定是最小數 indexMid = index2; break; } indexMid = ( index1 + index2 ) / 2; //取中間值 if(arrays[index1] == arrays[index2] && arrays[indexMid] == arrays[index1]){ //特例 return MinInOrder(arrays); } if(arrays[indexMid] >= arrays[index1]){ //如果中間值大於index1對應的值,則修改index1的值為中間值 index1 = indexMid; }else if(arrays[indexMid] <= arrays[index2]){ //否則修改index2的值為中間值 index2 = indexMid; } } return arrays[indexMid]; //indexMid對應的一定是最小值 } //特例時的查詢演算法 private int MinInOrder(int[] arrays) { int result = arrays[0]; for (int i = 0; i < arrays.length; i++) { if(result > arrays[i]){ result = arrays[i]; } } return result; } //測試 public static void main(String[] args) { int num1[] = {4, 5, 6, 7, 1, 2, 3}; int num2[] = {1, 0, 1, 1, 1}; int num3[] = {1, 1, 1, 0, 1}; Main m1 = new Main(); System.out.println(m1.Min(num3)); System.out.println(m1.Min(num2)); System.out.println(m1.Min(num1)); } }
小結:
這道題是二分查詢的變形,考查二分查詢。以及接收新事物的理解能力與轉化能力。