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

歡迎訪問 生活随笔!

生活随笔

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

编程问答

二分查找算法详解

發布時間:2025/3/20 编程问答 36 豆豆
生活随笔 收集整理的這篇文章主要介紹了 二分查找算法详解 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

1.什么是二分查找

二分查找針對的是一個有序的數據集合,查找思想有點類似分治思想。每次都通過跟區間的中間元素對比,將待查找的區間縮小為之前的一半,直到找到要查找的元素,或者區間被縮小為 0。

二分查找是一種非常高效的查找算法,高效到什么程度呢?我們來分析一下它的時間復雜度。

我們假設數據大小是 n,每次查找后數據都會縮小為原來的一半,也就是會除以 2。最壞情況下,直到查找區間被縮小為空,才停止。

可以看出來,這是一個等比數列。其中 n/2k=1 時,k 的值就是總共縮小的次數。而每一次縮小操作只涉及兩個數據的大小比較,所以,經過了 k 次區間縮小操作,時間復雜度就是 O(k)。通過 n/2k=1,我們可以求得 k=log2n,所以時間復雜度就是 O(logn)。

2.二分查找應用場景的局限性

二分查找的時間復雜度是 O(logn),查找數據的效率非常高。不過,并不是什么情況下都可以用二分查找,它的應用場景是有很大局限性的。那什么情況下適合用二分查找,什么情況下不適合呢?

首先,二分查找依賴的是順序表結構,簡單點說就是數組。

那二分查找能否依賴其他數據結構呢?比如鏈表。答案是不可以的,主要原因是二分查找算法需要按照下標隨機訪問元素。數組按照下標隨機訪問數據的時間復雜度是 O(1),而鏈表隨機訪問的時間復雜度是 O(n)。所以,如果數據使用鏈表存儲,二分查找的時間復雜就會變得很高。

二分查找只能用在數據是通過順序表來存儲的數據結構上。如果你的數據是通過其他數據結構存儲的,則無法應用二分查找。

其次,二分查找針對的是有序數據。

二分查找對這一點的要求比較苛刻,數據必須是有序的。如果數據沒有序,我們需要先排序。前面章節里我們講到,排序的時間復雜度最低是 O(nlogn)。所以,如果我們針對的是一組靜態的數據,沒有頻繁地插入、刪除,我們可以進行一次排序,多次二分查找。這樣排序的成本可被均攤,二分查找的邊際成本就會比較低。

但是,如果我們的數據集合有頻繁的插入和刪除操作,要想用二分查找,要么每次插入、刪除操作之后保證數據仍然有序,要么在每次二分查找之前都先進行排序。針對這種動態數據集合,無論哪種方法,維護有序的成本都是很高的。

所以,二分查找只能用在插入、刪除操作不頻繁,一次排序多次查找的場景中。針對動態變化的數據集合,二分查找將不再適用。那針對動態數據集合,如何在其中快速查找某個數據呢?別急,等到二叉樹那一節我會詳細講。

再次,數據量太小不適合二分查找。

如果要處理的數據量很小,完全沒有必要用二分查找,順序遍歷就足夠了。比如我們在一個大小為 10 的數組中查找一個元素,不管用二分查找還是順序遍歷,查找速度都差不多。只有數據量比較大的時候,二分查找的優勢才會比較明顯。

不過,這里有一個例外。如果數據之間的比較操作非常耗時,不管數據量大小,我都推薦使用二分查找。比如,數組中存儲的都是長度超過 300 的字符串,如此長的兩個字符串之間比對大小,就會非常耗時。我們需要盡可能地減少比較次數,而比較次數的減少會大大提高性能,這個時候二分查找就比順序遍歷更有優勢。

最后,數據量太大也不適合二分查找。

二分查找的底層需要依賴數組這種數據結構,而數組為了支持隨機訪問的特性,要求內存空間連續,對內存的要求比較苛刻。比如,我們有 1GB 大小的數據,如果希望用數組來存儲,那就需要 1GB 的連續內存空間。

注意這里的“連續”二字,也就是說,即便有 2GB 的內存空間剩余,但是如果這剩余的 2GB 內存空間都是零散的,沒有連續的 1GB 大小的內存空間,那照樣無法申請一個 1GB 大小的數組。而我們的二分查找是作用在數組這種數據結構之上的,所以太大的數據用數組存儲就比較吃力了,也就不能用二分查找了。

3.二分查找的幾個經典案例的代碼實現

尋找第一個值等于給定值的元素

? ?public int bSearchV1(int[] arr, int n, int value) {int low = 0;int high = n - 1;while (low <= high) {//這里沒有用mid=(low+high)/2 是為了防止low+high過大造成數據溢出//踩坑點:位運算的優先級低于四則運算,如果要用,需要加上大括號int mid = low + ((high - low) >> 1);if (arr[mid] > value) {high = mid - 1;} else if (arr[mid] < value) {low = mid + 1;} else {if (mid == 0 || arr[mid - 1] != value) {return mid;} else {high = mid - 1;}}}

我們分析一下第一個案例的代碼:

arr[mid]跟要查找的 value 的大小關系有三種情況:大于、小于、等于。對于 arr[mid]>value 的情況,我們需要更新 high= mid-1;對于 a[mid]<value 的情況,我們需要更新 low=mid+1。這兩點都很好理解。那當 arr[mid]=value 的時候應該如何處理呢?

如果我們查找的是任意一個值等于給定值的元素,當 a[mid]等于要查找的值時,arr[mid]就是我們要找的元素。但是,如果我們求解的是第一個值等于給定值的元素,當 a[mid]等于要查找的值時,我們就需要確認一下這個 a[mid]是不是第一個值等于給定值的元素。

如果 mid 等于 0,那這個元素已經是數組的第一個元素,那它肯定是我們要找的;如果 mid 不等于 0,但 arr[mid]的前一個元素 arr[mid-1]不等于 value,那也說明 a[mid]就是我們要找的第一個值等于給定值的元素。

如果經過檢查之后發現 arr[mid]前面的一個元素 arr[mid-1]也等于 value,那說明此時的 arr[mid]肯定不是我們要查找的第一個值等于給定值的元素。那我們就更新 high=mid-1,因為要找的元素肯定出現在[low, mid-1]之間。

尋找最后一個值等于給定值的元素

? ?public int bSearchV2(int[] arr, int n, int value) {int low = 0;int high = n - 1;while (low <= high) {int mid = low + ((high - low) >> 1);if (arr[mid] > value) {high = mid - 1;} else if (arr[mid] < value) {low = mid + 1;} else {if (mid == n - 1 || arr[mid + 1] != value) {return mid;} else {low = mid + 1;}}}return -1;}

尋找第一個大于等于給定值的元素

? ?//尋找第一個大于等于給定值的元素public int bSearchV3(int[] arr, int n, int value) {int low = 0;int high = n - 1;while (low <= high) {int mid = low + ((high - low) >> 1);if (arr[mid] < value) {low = mid + 1;} else {if (mid == 0 || arr[mid - 1] <= value) {return mid;} else {high = mid - 1;}}}return -1;}

尋找最后一個小于等于給定值的元素

? ?public int bSearchV4(int[] arr, int n, int value) {int low = 0;int high = n - 1;while (low <= high) {int mid = low + ((high - low) >> 1);if (arr[mid] >= value) {high = mid - 1;} else {if (mid == n - 1 || arr[mid + 1] > value) {return mid;} else {low = mid + 1;}}}return -1;}

?

《新程序員》:云原生和全面數字化實踐50位技術專家共同創作,文字、視頻、音頻交互閱讀

總結

以上是生活随笔為你收集整理的二分查找算法详解的全部內容,希望文章能夠幫你解決所遇到的問題。

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