<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Go 數(shù)據(jù)結(jié)構(gòu)和算法篇(九):二分查找

          共 3690字,需瀏覽 8分鐘

           ·

          2021-03-23 23:23

          介紹完基本的線性表排序算法后,今天我們來介紹一種常見的線性表查找算法 —— 二分查找。

          一、二分查找的引入

          對于基于數(shù)字索引的數(shù)組/切片元素查找,我們可能第一反應(yīng)都是遍歷這個數(shù)組/切片,直到給定元素值和待查找的值相等時,返回索引值并退出,否則一直遍歷到最后一個元素,如果還是沒有找到則返回 -1

          這樣的查找雖然是簡單粗暴了點,但是對于規(guī)模不大的數(shù)據(jù)集,也是沒什么問題的,不過很明顯,對于 n 個元素的數(shù)組,這種查找的時間復雜度是 O(n),隨著數(shù)據(jù)規(guī)模的增加,性能會越來越差,設(shè)想如果數(shù)據(jù)集的長度是 40 億(約為 2 的 32 次方),那么最差的情況下需要遍歷數(shù)組 40 億次,簡直不敢想象需要花費多長時間!那有沒有性能更好的算法來解決這個問題呢?

          在進一步探討這個問題之前,我們先來看一個生活中的例子。我們?nèi)粘I钪校芏嗳藨?yīng)該有這種經(jīng)歷,朋友、同學或者同事淘了個寶貝,神秘兮兮的過來讓大家猜多少錢,在約定一個價格范圍之后(比如 10-100),大家會七嘴八舌的猜起價格來:

          同事 A:新淘了個寶貝,猜猜多少錢?

          同事 B:50塊。

          同事 A:高了!

          同事 C:30塊。

          同事 A:低了!

          同事 D:40塊。

          同事 A:高了!

          同事 E:36塊。

          同事 A:對了!

          如果我們用順序遍歷的邏輯,最差需要 91 次,才能猜到價格,現(xiàn)實生活中,沒人會這么干,我們采用上面這種邏輯,只需要 4 次就猜到價格了,快了幾十倍,而且數(shù)據(jù)量越大,優(yōu)勢越明顯。基于這種思路,我們的算法科學家提煉出了二分查找算法,幫助我們在給定數(shù)據(jù)集中快速定位要查找的元素。

          二、實現(xiàn)原理

          所謂二分查找,針對的是一個有序的數(shù)據(jù)集合(這點很重要),查找思想有點類似分治思想 —— 每次都通過跟區(qū)間的中間元素對比,將待查找的區(qū)間縮小為之前的一半,直到找到要查找的元素,或者區(qū)間被縮小為 0。圖示如下:

          二分查找圖示

          注意:二分查找針對的必須是已經(jīng)排序過的有序數(shù)據(jù)序列,否則不能使用該算法。

          三、示例代碼

          二分查找的思路比較簡單,我們通過 Go 代碼實現(xiàn)如下:

          package main

          import (
              "fmt"
              "sort"
          )

          // 二分查找實現(xiàn)代碼
          func binarySearch(nums []int, num int, low int, high int) int {
              // 遞歸終止條件
              if low > high {
                  return -1
              }

              // 通過中間元素進行二分查找
              mid := (low + high) / 2
              // 遞歸查找
              if num > nums[mid] {
                  // 如果待查找數(shù)據(jù)大于中間元素,則在右區(qū)間查找
                  return binarySearch(nums, num, mid + 1, high)
              } else if num < nums[mid] {
                  // 如果待查找數(shù)據(jù)小于中間元素,則在左區(qū)間查找
                  return binarySearch(nums, num, low, mid - 1)
              } else {
                  // 找到了,返回索引值
                  return mid
              }
          }

          func main() {
              nums := []int{46531827}
              sort.Ints(nums)  // 先對待排序數(shù)據(jù)序列進行排序
              fmt.Printf("Sorted nums: %v\n", nums)
              num := 5
              index := binarySearch(nums, num, 0len(nums)-1)
              if index != -1 {
                  fmt.Printf("Find num %d at index %d\n", num, index)
              } else {
                  fmt.Printf("Num %d not exists in nums\n", num)
              }
          }

          對于未排序的數(shù)據(jù)序列,需要先排序,才能使用二分查找算法,這里我們使用了 Go 內(nèi)置的 sort.Ints 對整型切片進行排序,你也可以使用前面介紹的排序算法實現(xiàn)同樣的排序效果。

          執(zhí)行上述代碼,打印結(jié)果如下:

          四、性能分析

          很顯然,二分查找的時間復雜度是 O(logn)。這是一個非常恐怖的數(shù)量級,有時候甚至比 O(1) 還要高效,比如我們要在開頭提到的 40 億個數(shù)字中查找某一個元素,也只需要32次(2 的 32 次方是 40 億數(shù)量級),這真的是非常高效了,正因如此,二分查找在線性表結(jié)構(gòu)中的應(yīng)用非常廣泛。

          但是使用二分查找需要注意一個前提,那就是針對有序數(shù)據(jù)序列,換言之,二分查找適用于變動不是很頻繁的靜態(tài)序列集,如果序列集變動很頻繁,經(jīng)常進行插入刪除操作,那么就要不斷維護這個序列集的排序,這個成本也很高,因此,這種情況下就不適用二分查找了,比如我們的數(shù)據(jù)庫查詢,增刪改查很頻繁,顯然不是通過二分查找來進行查詢的。

          對于這種動態(tài)數(shù)據(jù)集,要同時保證更新(包含插入和刪除)和查詢的高效,通常有兩種方案,一種是哈希表,一種是樹結(jié)構(gòu),比如 Redis 底層就是基于哈希表的,而 MySQL 底層則是基于 B+ 樹。關(guān)于哈希表和樹結(jié)構(gòu),我們后面會詳細介紹。

          (本文完)


          學習過程中有任何問題,可以通過下面的評論功能或加入「Go 語言研習社」與學院君討論:


          本系列教程首發(fā)在 geekr.dev,你可以點擊頁面左下角閱讀原文鏈接查看最新更新的教程。

          瀏覽 66
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  日韩大屌操 | 音影先锋一区二区 | 欧美精品一级二级A片 | 大香伊人 | 男人露大鸡巴无遮挡免费视频 |