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

歡迎訪問 生活随笔!

生活随笔

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

编程问答

problem k: 查找某一个数_quot;细节魔鬼quot; 二分查找

發布時間:2024/4/19 编程问答 34 豆豆
生活随笔 收集整理的這篇文章主要介紹了 problem k: 查找某一个数_quot;细节魔鬼quot; 二分查找 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
?

二分查找,是一個高效,實用,且易理解的一個查找算法, 通常時間復雜度為O(lgn)。局限性在于,待查找對象必須為有序的數組;數據量過小,優勢不明顯,數據量過大,數組大小受限于內存。

除此之外,二分查找是面試中比較高頻考察的算法。

高頻題目清單:

二分查找應用(簡單)

  • 374 猜數字大小
  • 375 猜數字大小II
  • 35 搜索插入位置
  • 278 第一個錯誤的版本
  • 367 有效的完全平方數
  • 69 x的平方根
  • 441 排列硬幣

二分查找應用(中等)

  • 50 Pow(x,n)
  • 29 兩數相除
  • 34 在排序數組中查找元素的第一個和最后一個位置
  • 540 有序數組中的單一元素
  • 275 H指數II
  • 436 尋找右區間
  • 300 最長上升子序列
  • 718 最長重復子數組
  • 354 俄羅斯套娃信封問題
  • 658 找到K個最接近的元素
  • 162 尋找峰值
  • 4 尋找兩個正序數組的中位數
  • 209 長度最小的子數組
  • 222 完全二叉樹的節點個數
  • 287 尋找重復數

二分查找與旋轉數組

  • 153 尋找旋轉排序數組中的最小值
  • 154 尋找旋轉排序數組中的最小值II
  • 33 搜索旋轉排序數組
  • 81 搜素旋轉排序數組II
  • 74 搜索二維矩陣

二分答案法

  • 378 有序矩陣中第K小的元素
  • 668 乘法表中第K小第數
  • 410 分割數組的最大值
  • 483 最小好進制

刷題技巧

關于二分查找,有個說法 “思路簡單,細節是魔鬼”,相關的刷題技巧和注意事項,網上大神們總結了很多,自己加工總結后的筆記:

1. 題目出現排序數組,優先思考是否可以用二分查找。

2. 找mid值,記住無腦寫 int mid = (left + right) >>> 1

當然 int mid = left + (right - left) / 2 ?或者 int mid = left + ((right - left) >> 1) 也是對的.

但是萬萬不能寫int mid = (left + right) / 2 ,原因是,如果left + right 有可能造成整型溢出,而left + (right - left),因為right和left一般是索引值,所以基本不會溢出。
至于推薦寫int mid = (left + right) >>> 1 ,除了裝逼外,還是jdk官方的標準寫法,也是因為位運算更快的原因。

3. int mid = (left + right) >>> 1,對于偶數數組長度來說,找的是左中點,int mid = (left + right + 1) >>> 1,找的是右中點。

找幾個case測試理解后,牢牢背住就行,不加一是左中點,加一是右中點

4. 循環中的終止條件,寫while(left < right) 還是while (left <= right) ,大有乾坤。

left < right 和left <= right 區別在于,如果左右邊界一樣時,前者會漏掉一個數的查找。不過這也取決于最開始,right的值是否為nums.length -1。使用left < right的好處在,退出循環時,left一定等于right,不用額外考慮返回值。不過需要額外處理最后一個未查找的值。

這兩部篇文章分析了兩種條件的影響和處理調整:

https://leetcode-cn.com/problems/binary-search/solution/er-fen-cha-zhao-xiang-jie-by-labuladong/

https://leetcode-cn.com/problems/search-insert-position/solution/te-bie-hao-yong-de-er-fen-cha-fa-fa-mo-ban-python-/

這篇文章極力安利了第一種寫法:https://zhuanlan.zhihu.com/p/123863325

所有的文章,包括你我自己寫的,都只是個人的見解。

我認為必須了解兩種的寫法具體意義和影響,不同題目下才能具體情況具體處理。


高頻題目

704.二分查找

題目描述:

給定一個 n 個元素有序的(升序)整型數組 nums 和一個目標值 target ?,寫一個函數搜索 nums 中的 target,如果目標值存在返回下標,否則返回 -1。

示例:
輸入: nums = [-1,0,3,5,9,12], target = 9
輸出: 4
解釋: 9 出現在 nums 中并且下標為 4

解題思路:

最最基礎的二分查找題目,考慮以上技巧,分別使用left < right 和left <= right 兩種循環條件寫出代碼,感受邊界處理。

  • left <= right 左閉右閉寫法
  • public?int?search(int[]?nums,?int?target)?{
    ????if?(nums?==?null?||?nums.length?<=?0)?{
    ????????return?-1;
    ????}
    ????int?left?=?0,?right?=?nums.length?-?1,?mid?=?0;
    ????//?左閉右閉區間,搜索不會漏掉值
    ????while?(left?<=?right)?{
    ????????mid?=?(right?+?left)?>>>?1;
    ????????if?(nums[mid]?==?target)?{
    ????????????return?mid;
    ????????}?else?if?(nums[mid]?>?target)?{
    ????????????//?因為循環條件中是右閉區間,
    ????????????//所以要跳過mid值,mid得-1
    ????????????right?=?mid?-?1;
    ????????}else?{
    ????????????left?=?mid?+?1;
    ????????}
    ????}
    ????return?-1;
    }

    2.left < right 左閉右開寫法

    public?int?search2(int[]?nums,?int?target)?{
    ????if?(nums?==?null?||?nums.length?<=?0)?{
    ????????return?-1;
    ????}
    ????int?left?=?0,?right?=?nums.length?-?1,?mid?=?0;
    ????while?(left?????????mid?=?(right?+?left)?>>>?1;
    ????????if?(nums[mid]?==?target)?{
    ????????????return?mid;
    ????????}else?if?(nums[mid]?>?target)?{
    ????????//因為循環條件為右開區間
    ????????//所以跳過mid值,右區間應為mid
    ????????????right?=?mid;
    ????????}else?{
    ????????????left?=?left?+?1;
    ????????}
    ????}
    ????//?處理最后沒查找的元素,
    ????//最后一次left==right一定成立,
    ????//所以這塊left或者right都可以
    ????return?nums[left]?==?target???left?:?-1;
    }

    最后,附上,jdk中的寫法:

    34.在排序數組中查找元素的第一個和最后一個位置

    題目描述:

    給定一個按照升序排列的整數數組 nums,和一個目標值 target。找出給定目標值在數組中的開始位置和結束位置。
    你的算法時間復雜度必須是 O(log n) 級別。
    如果數組中不存在目標值,返回?[-1, -1]。

    示例:
    輸入: nums = [5,7,7,8,8,10], target = 8
    輸出: [3,4]

    解題思路:

    先簡單二分查找到target值后,然后向前向后遍歷,找到第一個不重復的和最后一個不重復的值,這樣做的話,如果數組元素全是同一個target值,那么時間復雜度就是O(n)了,不符合題目要求了。

    所以先解決 如何在O(log n)時間內找到元素第一次出現的位置 :

    和基礎二分查找不同的是,在nums[mid] == target后,不能直接返回mid,而是應該讓縮小右邊界,繼續查找。在退出循環后,對整個數組沒有target值的情況做處理。

    public?int?searchFirst(int[]?nums,?int?target)?{
    ????if?(nums?==?null?||?nums.length?<=?0)?{
    ????????return?-1;
    ????}
    ????int?left?=?0,?right?=?nums.length?-?1,?mid?=?0;
    ????while?(left?<=?right)?{
    ????????mid?=?left?+?(right?-?left)?/?2;
    ????????//?當中點值和target相等時,更新右邊界
    ????????//?不和下面分支判斷合一塊,是為了邏輯清晰
    ????????if?(nums[mid]?==?target)?{
    ????????????right?=?mid?-?1;
    ????????}else?if?(nums[mid]?>?target)?{
    ????????????right?=?mid?-?1;
    ????????}else?{
    ????????????left?=?mid?+?1;
    ????????}
    ????}
    ????//?不用nums[right]?和target比較,
    ????//是因為在中點相等時,跳過了中點,
    ????//且right=mid-1。
    ????//?而left總和mid + 1。?
    ????//?所以用nums[left]和target比較。
    ????//?當最后一次查找,left?=?nums.length?-?1時,
    ????//?left有可能+1,等于nums.length
    ????// nums[left]就有可能下標越界。
    ????return?left?>=?nums.length?||?nums[left]?!=?target???-1?:?left;
    }

    查找目標元素的最后一次出現的位置:
    同樣的,在中點值等于target時,不要返回mid,而是更新左邊界。記得最后在退出循環后,對整個數組沒有target值的情況做處理。

    public?int?searchLast(int[]?nums,?int?target)?{
    ????if?(nums?==?null?||?nums.length?<=?0)?{
    ????????return?-1;
    ????}
    ????int?left?=?0,?right?=?nums.length?-?1,?mid?=?0;
    ????while?(left?<=?right)?{
    ????????mid?=?left?+?(right?-?left)?/?2;
    ????????if?(nums[mid]?==?target)?{
    ????????????left?=?mid?+?1;
    ????????}else?if?(nums[mid]?>?target)?{
    ????????????right?=?mid?-?1;
    ????????}else?{
    ????????????left?=?mid?+?1;
    ????????}
    ????}
    ????//?和找第一個元素位置一樣
    ????//?選nums[right]和target比較是因為:
    ????//?當中點和target相等時,跳過mid,left = mid + 1了。
    ????//?當數組只有一個元素時,right有可能-1,變成-1,造成數組下標越界。
    ????//?當然更好一點,直接?return?left?-?1;
    ????//?因為left初始值是0,如果數組不存在target元素,即返回-1.
    ????return?right?}

    最后,解決這道題,只需:

    public?int[]?searchRange(int[]?nums,?int?target)?{
    ????//?這塊處理邊界條件后,剩下兩個函數可去掉。
    ????if?(nums?==?null?||?nums.length?<=?0)?{
    ????????return?new?int[]{-1,-1};
    ????}
    ????return?new?int[]?{searchFirst(nums,?target),?searchLast(nums,?target)};
    }

    367.有效的完全平方數

    題目描述:

    給定一個正整數 num,編寫一個函數,如果 num 是一個完全平方數,則返回 True,否則返回 False。

    示例一:
    輸入:16
    輸出:true

    示例二:
    輸入:14
    輸出: false

    解題思路:

    這是一道簡單題,我們每次取中點值,然后判斷中點值自乘是否等于num,如果等于,證明該num是完全平方數,否則更新左右指針。

    注意學習點:

    一個數的平方根最大不會超過它本身,再細點,基本不會超過它的一半。, ?解不等式,得到 或者。所以,當a =0,1,2,3時,它們的平方根是超過它們自身一半的,對應的平方根解分別是,0,1,1,1。所以,為了處理這4個特殊情況,初始值left = 0, right = x/2 + 1。

    public?boolean?isPerfectSquare(int?num)?{
    ????int?left?=?0,?right?=?num?/?2?+?1,?mid?=?0;
    ????while?(left?<=?right)?{
    ????????mid?=?(left?+?mid)?>>>?1;
    ????????if?(mid?*?mid?==?num)?{
    ????????????return?true;
    ????????}else?if?(mid?*?mid?>?num)?{
    ????????????right?=?mid?-?1;
    ????????}else?{
    ????????????left?=?mid?+?1;
    ????????}
    ????}
    ????return?false;
    }

    69.x的平方根-sqrt(x)

    題目描述:

    實現 int sqrt(int x)?函數。
    計算并返回 x 的平方根,其中 x 是非負整數。
    由于返回類型是整數,結果只保留整數的部分,小數部分將被舍去。

    示例:輸入: 8
    輸出: 2
    說明: 8 的平方根是 2.82842..., 由于返回類型是整數,小數部分將被舍去。

    解題思路:

    該題最快的解法并不是二分查找,有O(1)的袖珍計算器算法和同是O(lgn)卻更快的牛頓迭代法。
    這兩種數學算法,具體思路參考官方題解:
    https://leetcode-cn.com/problems/sqrtx/solution/x-de-ping-fang-gen-by-leetcode-solution/

    二分查找的具體做法,和上題差不多,初始右邊界,選擇right = x/2 + 1。注意在退出循環后,如果使用while(left <= right),得考慮返回值如何選擇。
    如case 8 的平方根是 2.82842...,應該返回2。left在非return正常退出循環時,可能會mid + 1,不合適,right退出循環時,是mid-1,符合題目取整數的要求。

    public?int?mySqrt(int?x)?{
    ????//?用long?防止int*int后溢出
    ????long?left?=?0,?right?=?x?/?2?+?1,?mid?=?0;
    ????while?(left?<=?right)?{
    ????????mid?=?(left?+?right)?>>>?1;
    ????????long?square?=?mid?*?mid;
    ????????if?(square?==?x)?{
    ????????????return?(int)mid;
    ????????}else?if?(square?>?x)?{
    ????????????right?=?mid?-?1;
    ????????}else??{
    ????????????left?=?mid?+?1;
    ????????}
    ????}
    ????//?按要求,比如8,平方根2.82842,所求解為2.
    ????//?對于left?<=?right,在正常退出時,left?=?right?+?1
    ????// right每次更新都是mid - 1,所以直接返回right即可,或者left - 1。
    ????return?(int)right;
    }

    50 Pow(x,n)

    題目說明:

    實現 pow(x, n) ,即計算 x 的 n 次冪函數。

    示例1:
    輸入: 2.00000, 10
    輸出: 1024.00000

    示例2:
    輸入: 2.00000, -2
    輸出: 0.25000
    解釋: 2-2 = 1/22 = 1/4 = 0.25

    說明:-100.0 < x < 100.0
    n 是 32 位有符號整數,其數值范圍是 [?231, 231 ? 1]

    解題思路:

    最壞的解法,就是遍歷一遍,不斷相乘。二分法的思想在這題上,比如要算2的10次方,只要求得2的5次方即可;2的5次方只要求2的2次方,然后結果自乘即可. ?自然想到也要使用遞歸。

    可以看到,需要注意的是,當n為奇數時,中間結果還需要再乘以當前數。除此之外,還應該考慮x為負數的情況,x為負數,所求結果要被1除,即倒數。

    public?double?myPow(double?x,?int?n)?{
    ????long?N?=?n;
    ????return?N?>=?0???quickMul(x,?N)?:?1.0?/?quickMul(x,?-N);
    }

    public?double?quickMul(double?x,?long?n)?{
    ????if?(n?==?0)?{
    ????????return?x;
    ????}
    ????double?y?=?quickMul(x,?n?/?2);
    ????return?n?%?2?==?0???y?*?y?:?y?*?y?*?x;
    }

    153.尋找旋轉排序數組中的最小值

    題目描述:

    假設按照升序排序的數組在預先未知的某個點上進行了旋轉。
    ( 例如,數組 [0,1,2,4,5,6,7] 可能變為 [4,5,6,7,0,1,2] )。
    請找出其中最小的元素。你可以假設數組中不存在重復元素。

    示例:
    輸入: [4,5,6,7,0,1,2]
    輸出: 0

    解題思路

    主要思路就是通過判斷中點值與邊值的大小來縮小查找的左右區間。
    本題有3個關鍵點:

  • while條件使用 left < right
  • 中點選左中點
  • 邊值比較使用右邊值
  • 1好理解,使用left

    2.3的考慮,二分查找題目分析一定要考慮數組只有兩個元素的case,就能確定到底使用左中點合適還是右中點合適。

    此題,如果case為[1,3],如果選左中點,用左邊界比較,就會很麻煩:如果nums[mid] = num[left],left肯定不能mid+1跳過,但是如果left=mid,就死循環了。

    如果選右中點,用左邊界比較,也會有問題,正常情況nums[mid] > num[left],肯定右半邊是較小的區間,left跳過mid,但是case[1,3]來說,跳過1就有問題。

    所以,求旋轉數組最小值,因為旋轉點的右區間是小區間,所以用左中值和右邊值比較,當左中值和右值相等時,右邊值由可能是最小值,注意右邊值不能跳過mid。

    public?int?findMin(int[]?nums)?{
    ????int?left?=?0,?right?=?nums.length?-?1,?mid?=?0;
    ????while?(left?????????mid?=?(left?+?right)?>>>?1;
    ????????if?(nums[mid]?>?nums[right])?{
    ????????????left?=?mid?+?1;
    ????????}else?{
    ????????????right?=?mid;
    ????????}
    ????}
    ????return?nums[mid];
    }

    154.尋找旋轉排序數組中的最小值II

    題目描述:

    假設按照升序排序的數組在預先未知的某個點上進行了旋轉。

    ( 例如,數組?[0,1,2,4,5,6,7] 可能變為?[4,5,6,7,0,1,2]?)。

    請找出其中最小的元素。

    注意數組中可能存在重復的元素。

    示例 1:
    輸入: [3,3,1,3]
    輸出: 1

    示例 2:
    輸入: [2,2,2,0,1]
    輸出: 0

    解題思路:

    基于上題,如果沒有重復元素,如果左中點值和右值相等時nums[mid] = nums[right],右值有可能是小值,不能跳過,就更新右邊值right = mid。

    如果有重復元素后,處理也簡單,當左中值和右值相等,讓右指針往左滑動,right = right - 1, 跳過迷惑值。

    public?int?findMin(int[]?nums)?{
    ????int?left?=?0,?right?=?nums.length?-?1,?mid?=?0;
    ????while?(left?????????mid?=?(left?+?right)?>>>?1;
    ????????//?相等時,跳過右邊這個迷惑值
    ????????if?(nums[mid]?==?nums[right])?{
    ????????????right?=?right?-?1;
    ????????}
    ????????if?(nums[mid]?>?nums[right])?{
    ????????????left?=?mid?+?1;
    ????????}
    ????????if?(nums[mid]?????????????right?=?mid;
    ????????}
    ????}
    ????return?nums[left];
    }

    33 搜索旋轉排序數組

    題目描述:

    給你一個升序排列的整數數組 nums ,和一個整數 target 。

    假設按照升序排序的數組在預先未知的某個點上進行了旋轉。(例如,數組?[0,1,2,4,5,6,7]?可能變為?[4,5,6,7,0,1,2] )。

    請你在數組中搜索 target ,如果數組中存在這個目標值,則返回它的索引,否則返回?-1 。

    示例 1:
    輸入:nums=[4,5,6,7,0,1,2], target = 0
    輸出:4

    解題思路:

    和上面找最小值一樣,通過中值和邊值的比較,先確定有序的數組,然后通過判斷target是否在區間內,來更新左右邊值。

    使用左中點的話,

    • 如果nums[mid] >= nums[left], 可以證明[left,mid]左半段是有序的。當target在[left,mid)區間內,更新右邊值right = mid - 1,否則更新左邊值left = mid + 1。
      ?使用>=是因為是用的是左中點,得考慮只有兩個元素的case。
    • nums[mid] < nums[left], 右半段[mid,right]是有序的
      當target在(mid,right],更新左邊值left = mid + 1, 否則更新右邊值right = mid - 1
    public?int?search(int[]?nums,?int?target)?{
    ????int?left?=?0,?right?=?nums.length?-?1,?mid?=?0;
    ????while?(left?<=?right)?{
    ????????mid?=?(left?+?right)?>>>?1;
    ????????if?(nums[mid]?==?target)?{
    ????????????return?mid;
    ????????}
    ????????//?如果中點值大于左值,說明左半邊是有序的
    ????????if?(nums[mid]?>=?nums[left])?{
    ????????????//?如果目標值在左區間,更新右邊界
    ????????????if?(target?>=?nums[left]?&&?target?????????????????right?=?mid?-?1;
    ????????????}else?{
    ????????????????left?=?mid?+?1;
    ????????????}
    ????????}else?{
    ????????????//?右半邊是有序的,如果目標值在右半邊區間,更新左邊界????????????????????????????????????????????
    ????????????if?(target?>?nums[mid]?&&?target?<=?nums[right])?{
    ????????????????left?=?mid?+?1;
    ????????????}else?{
    ????????????????right?=?mid?-?1;
    ????????????}
    ????????}
    ????}
    ????return?-1;
    }

    81.搜索旋轉排序數組II

    題目描述:

    假設按照升序排序的數組在預先未知的某個點上進行了旋轉。
    編寫一個函數來判斷給定的目標值是否存在于數組中。若存在返回 true,否則返回 false。
    進階:
    這是 搜索旋轉排序數組 的延伸題目,本題中的 nums 可能包含重復元素。這會影響到程序的時間復雜度嗎?會有怎樣的影響,為什么?

    示例:
    輸入:nums=[2,5,6,0,0,1,2], target = 0 輸出: true

    解題思路:

    和154.尋找旋轉排序數組中的最小值II題一樣,當nums[mid] == nums[left]時,需要滑動left邊值指針,跳過迷惑項。
    如果這么處理,當待排序數組全是一樣的重復元素,時間復雜度會退化為O(n)。

    public?boolean?search(int[]?nums,?int?target)?{
    ????int?left?=?0,?right?=?nums.length?-?1,?mid?=?0;
    ????while?(left?<=?right)?{
    ????????mid?=?(left?+?right)?>>>?1;
    ????????if?(nums[mid]?==?target)?{
    ????????????return?true;
    ????????}
    ????????//?跳過迷惑值
    ????????if?(nums[mid]?==?nums[left])?{
    ????????????left?++;
    ????????}else?if?(nums[mid]?>?nums[left])?{
    ????????????if?(target?>=?nums[left]?&&?target?????????????????right?=?mid?-?1;
    ????????????}else?{
    ????????????????left?=?mid?+?1;
    ????????????}
    ????????}else?{
    ????????????if?(target?>?nums[mid]?&&?target?<=?nums[right])?{
    ????????????????left?=?mid?+?1;
    ????????????}else?{
    ????????????????right?=?mid?-?1;
    ????????????}
    ????????}
    ????}
    ????return?false;
    }

    74.搜索二維矩陣

    題目描述:

    編寫一個高效的算法來判斷 m x n 矩陣中,是否存在一個目標值。該矩陣具有如下特性:

    每行中的整數從左到右按升序排列。
    每行的第一個整數大于前一行的最后一個整數。

    示例:
    輸入:matrix = [[1,3,5,7],[10,11,16,20],[23,30,34,50]], target = 3
    輸出:true

    解題思路:

    從示例上就能看到,講該二維矩陣按行依次展開,就是一個升序的一維數組。
    然后就能按標準的二分查找來做題了。

    此題關鍵技巧,如果展開后的一維數組的下班為i。
    和m(row length) * n(col length)二維數組存在以下關系:

    • row = i / n
    • col = i % n
    public?boolean?searchMatrix(int[][]?matrix,?int?target)?{
    ????if?(matrix?==?null?||?matrix.length?<=?0)?{
    ????????return?false;
    ????}
    ????int?m?=?matrix.length;
    ????int?n?=?matrix[0].length;
    ????if?(n?<=?0)?{
    ????????return?false;
    ????}
    ????int?left?=?0,?right?=?m?*?n?-?1,?mid?=?0;
    ????while?(left?<=?right)?{
    ????????mid?=?(left?+?right)?>>>?1;
    ????????int?val?=?matrix[mid?/?n][mid?%?n];
    ????????if?(val?==?target)?{
    ????????????return?true;
    ????????}else?if?(val?>?target)?{
    ????????????right?=?mid?-?1;
    ????????}else?{
    ????????????left?=?mid?+?1;
    ????????}
    ????}
    ????return?false;
    }

    240.搜素二維矩陣II

    題目描述:

    編寫一個高效的算法來搜索 m x n 矩陣 matrix 中的一個目標值 target。該矩陣具有以下特性:
    每行的元素從左到右升序排列。
    每列的元素從上到下升序排列。?
    示例:
    現有矩陣 matrix 如下:
    [1, ? 4, ?7, 11, 15],
    [2, ? 5, ?8, 12, 19],
    [3, ? 6, ?9, 16, 22],
    [10, 13, 14, 17, 24],
    [18, 21, 23, 26, 30]
    給定 target = 5,返回 true。
    給定 target = 20,返回 false。

    解題思路:

  • 二分查找法
  • 和上題不一樣的是,下一行的起始值不一定會比上一行末尾值大。但是每一行和每一列是各自升序的。所以,對于m*n的矩陣,遍歷i次,i取m和n的最小值,分別在第i列和第i行進行二分查找。

    時間復雜度分析:
    循環次數i = min(m,n), 每次循環分別二分查找列和行,時間復雜度O(lg(m-i)) + O(lg(n-i))。近似O(2*lg(n-i)) = O(lg(n))。最壞當m==n時,總的運行時間: O(lg(n) + lg(n-1) + lg(n-2) + ... + lg(1)) = O(lg(n*(n-1)*(n-2)*...*1)) = O(lg(n!))

    ?public?boolean?searchMatrix2(int[][]?matrix,?int?target)?{
    ????if?(matrix?==?null?||?matrix.length?<=?0)?{
    ????????return?false;
    ????}
    ????int?count?=?Math.min(matrix.length,?matrix[0].length);
    ????for?(int?i?=?0;?i?????????boolean?rowSearchRes?=?bSearch(matrix,?target,?i,?true);
    ????????if?(rowSearchRes)?{
    ????????????return?true;
    ????????}
    ????????boolean?colSearchRes?=?bSearch(matrix,?target,?i,?false);
    ????????if?(colSearchRes)?{
    ????????????return?true;
    ????????}
    ????}
    ????return?false;
    }

    private?boolean?bSearch(int[][]?martix,?int?target,?int?i,?boolean?searchRow)?{
    ????int?left?=?i,?right?=?searchRow???martix[0].length?-?1?:?martix.length?-?1,?mid?=?0;
    ????while?(left?<=?right)?{
    ????????mid?=?(left?+?right)?>>>?1;
    ????????if?(searchRow)?{
    ????????????if?(target?==?martix[i][mid])?{
    ????????????????return?true;
    ????????????}
    ????????????if?(target?>?martix[i][mid])?{
    ????????????????left?=?mid?+?1;
    ????????????}
    ????????????if?(target?????????????????right?=?mid?-?1;
    ????????????}
    ????????}else?{
    ????????????if?(target?==?martix[mid][i])?{
    ????????????????return?true;
    ????????????}
    ????????????if?(target?>?martix[mid][i])?{
    ????????????????left?=?mid?+?1;
    ????????????}
    ????????????if?(target?????????????????right?=?mid?-?1;
    ????????????}
    ????????}
    ????}
    ????return?false;
    }
  • 類二叉搜索樹
  • 按照本題二維矩陣的特性,如果把左下角的值當做根節點,可以將矩陣看做一個二叉搜索樹,上一個列值比它小,下一個行值比它大。
    所以,從最下角值開始搜索,如果目標值大于當前值,后面的所有的行值也都是大于目標值的,列往上移動。
    如果目標值小于當前值,往上的所有列值都是小于目標值的,移動下一行。

    時間復雜度O(m+n),空間復雜度O(1)。

    public?boolean?searchMatrix(int[][]?martrix,?int?target)?{
    ????if?(martrix?==?null?||?martrix.length?<=?0)?{
    ????????return?false;
    ????}
    ????int?row?=?martrix.length?-?1,?col?=?0;
    ????while?(col?=?0)?{
    ????????if?(martrix[row][col]?==?target)?{
    ????????????return?true;
    ????????}?else?if?(martrix[row][col]?>?target)?{
    ????????????row?=?row?-?1;
    ????????}else?{
    ????????????col?=?col?+?1;
    ????????}
    ????}
    ????return?false;
    }

    總結
    • 二分查找題目注意在使用不同while條件時,不同的細節處理,包括左右指針的更新,退出循環后的取值考慮。

    • 記得不要忘記分析只有兩個元素的case。

    • 旋轉數組類的查找,首先要通過中值與邊值的比較,來確定哪半邊是有序的。升序數組旋轉后右半邊是小區間,求旋轉數組最小值時,使用左中值和右邊值比較。

    • 查找數組中,如果有重復元素,需要滑動指針,跳過疑惑值,但是有可能時間復雜度退化為O(n)。

    總結

    以上是生活随笔為你收集整理的problem k: 查找某一个数_quot;细节魔鬼quot; 二分查找的全部內容,希望文章能夠幫你解決所遇到的問題。

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