日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

剑指Offer #06 旋转数组的最小数字(二分查找)| 图文详解

發布時間:2025/3/20 编程问答 13 豆豆
生活随笔 收集整理的這篇文章主要介紹了 剑指Offer #06 旋转数组的最小数字(二分查找)| 图文详解 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

題目來源:牛客網-劍指Offer專題
題目地址:旋轉數組的最小數字

題目描述

把一個數組最開始的若干個元素搬到數組的末尾,我們稱之為數組的旋轉。
輸入一個非遞減排序的數組的一個旋轉,輸出旋轉數組的最小元素。
例如數組{3,4,5,1,2}為{1,2,3,4,5}的一個旋轉,該數組的最小值為1。
NOTE:給出的所有元素都大于0,若數組大小為0,請返回0。

題目解析

方法一:
最直觀的方法莫過于漠視題目給的條件“非遞減排序”,直接遍歷整個數組找到里面最小的元素。就算我很菜,也不忍下手寫這樣的代碼。

于是,對暴力法稍加優化才有了第一種算法:以第一個元素 array[0]array[0]array[0] 為基準找到第一個比它小的元素,這個就是原數組的第一個元素,即最小值。找不到則返回第一個元素,說明沒有元素參與旋轉。
雖然做了優化,在極端情況下還是要遍歷整個數組,時間復雜度 O(n)O(n)O(n)

import java.util.ArrayList; public class Solution {public int minNumberInRotateArray(int [] array) {//特判if (array.length == 0) {return 0;}//以第一個元素為基準int flag = array[0];for (int i = 1; i < array.length; i++) {//找到第一個比它小的元素,即為答案if (flag > array[i]) {return array[i];}}return flag;} }

方法二:
通常在有序的序列中查找某個值的問題都可以用二分查找,本題雖然不是整個序列又是有序的,但是它也算是局部有序,也可以用上二分查找。

經過觀察我們可以發現,旋轉數組可以分成左右兩個部分,而我們要找的數就在分界線的右邊。算法描述如下:

  • 首先,我們設定左右指針 leftleftleftrightrightright ,然后通過判斷中間指針 midmidmid 指向的數是否比 rightrightright 指向的數小。
  • 如果是,則說明 midmidmid 位于數組右邊部分,于是將 rightrightright 指針移動到該位置上;否則,說明 midmidmid 位于數組左邊部分,于是將指針移動到該位置上。
  • 最后不斷重復上訴過程,直到找到數組兩個部分的邊界所在,即 right?left=1right-left=1right?left=1。這時,array[right]array[right]array[right] 就是我們要找的最小值。

二分查找的過程如上圖所示 ,算法時間復雜度為 O(logn)O(logn)O(logn)

import java.util.ArrayList; public class Solution {public int minNumberInRotateArray(int [] array) {if (array.length == 0) {return 0;}int left = 0, right = array.length - 1;while (array[left] >= array[right]) {int mid = (left + right) / 2;//找到邊界所在if (right - left == 1) break;if (array[mid] < array[right]) {right= mid;} else {left= mid;}}return array[right];} }

雖然這樣在牛客網中通過了,但是我發現了一組可以hack這份代碼的數據:
[3,4,1,3,3,3,3][3,4,1,3,3,3,3][3,4,1,3,3,3,3]
正確答案: 111
代碼返回: 333

方法三:
有問題就肯定會有對策,這里借用牛客網大佬FINACK的思路。

分下面三種情況進行討論:

  • array[mid] > array[right]時,
    出現這種情況的array類似[3,4,5,6,0,1,2],此時最小數字一定在mid的右邊。
    ∴\therefore left = mid + 1
  • array[mid] == array[right]時,
    出現這種情況的array類似 [1,0,1,1,1] 或者[1,1,1,0,1],此時最小數字不好判斷在mid左邊還是右邊,這時只好逐個嘗試。
    ∴\therefore right = right - 1
  • array[mid] < array[right]:
    出現這種情況的array類似[2,2,3,4,5,6,6],此時最小數字一定就是array[mid]或者在mid的左邊。因為右邊必然都是遞增的。
    ∴\therefore right = mid
  • 注意這里有個坑:如果待查詢的范圍最后只剩兩個數,那么mid 一定會指向下標靠前的數字,比如 array = [4,6],此時array[left] = 4 ;array[mid] = 4;array[right] = 6;如果right = mid - 1,就會產生錯誤, 因此right = mid。

    import java.util.ArrayList; public class Solution {public int minNumberInRotateArray(int [] array) {if (array.length == 0) {return 0;}int left = 0, right = array.length - 1;while (left < right) {int mid = (left + right) / 2;if (array[mid] > array[right]) {left = mid + 1;} else if (array[mid] == array[right]) {right--;} else {right = mid;}}return array[left];} }

    只是可惜,在所有數字都一樣的情況下,算法的時間復雜度會退化成 O(n)O(n)O(n)。當然,這是沒有辦法的事情。


    如果本文對你有所幫助,要記得點贊哦~

    總結

    以上是生活随笔為你收集整理的剑指Offer #06 旋转数组的最小数字(二分查找)| 图文详解的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。