二分查詢的本質,搜尋旋轉排序陣列問題
整數陣列 nums 按升序排列,陣列中的值 互不相同 。
在傳遞給函式之前,nums 在預先未知的某個下標 k(0 <= k < nums.length)上進行了 旋轉,使陣列變為 [nums[k], nums[k+1], ..., nums[n-1], nums[0], nums[1], ..., nums[k-1]](下標 從 0 開始 計數)。例如, [0,1,2,4,5,6,7] 在下標 3 處經旋轉後可能變為[4,5,6,7,0,1,2] 。
給你 旋轉後 的陣列 nums 和一個整數 target ,如果 nums 中存在這個目標值 target ,則返回它的下標,否則返回-1。
示例 1:
輸入:nums = [4,5,6,7,0,1,2], target = 0
輸出:4
示例2:
輸入:nums = [4,5,6,7,0,1,2], target = 3
輸出:-1
作者:力扣 (LeetCode)
連結:https://leetcode-cn.com/leetbook/read/top-interview-questions-medium/xvyz1t/
來源:力扣(LeetCode)
著作權歸作者所有。商業轉載請聯絡作者獲得授權,非商業轉載請註明出處。
二分查詢
剛拿到這道題,想到既然是旋轉了一次的陣列,那麼肯定在旋轉中心的兩側元素是區域性有序的如:4567和012,要是我們能找到這個中心,然後在兩側進行二分搜尋就好了。但其實如果尋找中心只能順序查詢,那麼時間複雜度已經是O(n)了為什麼不直接找target呢?
其實如果明白了二分查詢的本質——根據有序元素段判斷元素位置以不斷收縮邊界選擇搜尋空間,這道題也可以很輕鬆的想出來解法。比如,對於一個有序陣列[1,2,3,4,5,6,7]。mid = 4,如果我們要查詢元素6會很自然的去他的右端尋找[mid+1, left]。因為我們知道mid左側的元素有序,故這些元素都比mid 4小,自然也就比6小了。
那麼對於[4,5,6,7,0,1,2],可以想象,無論mid落到哪,在其兩端都至少有一個區域性有序段
程式碼:
public int search(int[] nums, int target) { int l = 0, r = nums.length - 1; int mid = -1; while(l <= r){ mid = l + (r - l) / 2; if(target == nums[mid]){ return mid; } //mid至少會有一端有序,可以利用有序段收縮邊界。 if(nums[mid] >= nums[l]){//左端有序 if(target < nums[mid] && target >= nums[l]){ //元素在左端 r = mid - 1; }else{ //排除左端 l = mid + 1; } }else{//右端有序 if(target > nums[mid] && target <= nums[r]){ //元素在右端 l = mid + 1; }else{ //排除右端 r = mid - 1; } } } return -1; }