數據結構與算法:二分查找

常見數據結構

  • 簡單數據結構(必須理解和掌握)javascript

    • 有序數據結構:棧、隊列、鏈表。有序數據結構省空間(儲存空間小)
    • 無序數據結構:集合、字典、散列表,無序數據結構省時間(讀取時間快)
  • 複雜數據結構html

    • 樹、 堆

本系列主要內容

  • 數組和列表: 最經常使用的數據結構java

    • 與鏈表相比,數組具備更好的緩存位置。
  • 棧和隊列: 與列表相似可是更復雜數據結構
  • 鏈表: 如何經過它們克服數組的不足,git

    • 鏈表容許在迭代期間有效地從序列中的任何位置插入或刪除元素。
    • 鏈表的一個缺點是訪問時間是線性的(並且難以管道化)。(更快的訪問,如隨機訪問,是不可行的)
  • 字典: 將數據以鍵-值對的的形式儲存
  • 散列(表): 適用於快速查找和檢索
  • 集合: 適用於存儲只出現一次的元素
  • 二叉樹: 以層級的形式存儲數據
  • 圖和圖算法: 網絡建模的理想選擇
  • 算法:包括排序、搜索、圖形算法
  • 高級算法: 動態規劃、貪心算法、BF、分治、回溯等算法範式
  • 加密算法:

有序數據結構

數組

列表

隊列

鏈表

無序列數據結構

集合

字典

散列(表)

簡單算法 => 二分查找

圖片描述

二分查找是搜索算法中的一種,用來搜索有序數組github

二分查找:是一種簡單算法,其輸入是一個有序的元素列表(必須有序的緣由稍後解釋)。若是要
查找的元素包含在列表中,二分查找返回其位置;不然返回null算法

clipboard.png

Javascript ES6實現

非遞歸的

/** 
 * 函數binarySearch接受一個有序數組和一個元素。 若是指定的元素包含在數組中, 這個
 函數將返回其位置。 你將跟蹤要在其中查找的數組部分—— 開始時爲整個數組。
*/
const binarySearch = (list, item) => {
  // 數組要查找的範圍
  // low、high用於跟蹤要在其中查找的列表部分
  let low = 0  
  let high = list.length - 1

  while(low <= high) { // 只要範圍沒有縮小到只包含一個元素
    const mid = Math.floor((low + high) / 2)
    const guess = list[mid] // 找到中間的元素

    if(guess === item) { // 找到元素
      return mid
    }
    if(guess > item) { // 猜想的數大了
      high = mid - 1
    } else { // 猜想的數小了
      low = mid + 1
    }
  }

  return null
}

const myList = [1, 3, 5, 7, 9]

console.log(binarySearch(myList, 3))
console.log(binarySearch(myList, -1))

遞歸的

const binarySearch = (list, item, low, hight) => {
  let arrLength = list.length
  while (low <= high) {
    let mid = Math.floor((low + high) / 2)
    let guess = list[mid]

    if( guess === item ) {
      return mid
    } else if (guess > item) {
      high = mid - 1
      list = list.slice(0, mid)
      return binarySearch(list, item, low, high)
    } else {
      low = mid + 1
      list = list.slice(low, arrLength)
      return binarySearch(list, item, low, high)
    }
  }
  return null 
}

const createArr = (n) => Array.from({length: n}, (v, k) => k + 1)

const myList = createArr(100)
let low = 0
let high = myList.length - 1

console.log(binarySearch(myList, 3, low, high))
console.log(binarySearch(myList, -1, low, high))
找一個平衡二叉樹最後一個節點

Python實現

運行時間(時間複雜度)

圖片描述

二分查找的運行時間爲對數時間(或log時間)。
若是列表包含100個元素,最多要猜7次;若是列表包含40億個數字,最多
需猜32次。
圖片描述
即: 2的7次方 = 100segmentfault

圖片描述
簡單查找時間是 y= ax 的線性方方程
因此很容易得出結論數組

隨着元素數量的增長(x增長),二分查找須要的時間(y)並很少, 而簡單查找須要的時間(y)卻不少。
所以,隨着列表的增加,二分查找的速度比簡單查找快得多。

爲檢查長度爲n的列表,二分查找須要執行log n次操做。使用大O表示法,
這個運行時間怎麼表示呢?O(log n)。通常而言,簡單算法的大O表示法像下面這樣
圖片描述緩存

clipboard.png

大O符號

大O符號中指定的算法的增加順序網絡

圖片描述

如下是一些最經常使用的 大O標記法 列表以及它們與不一樣大小輸入數據的性能比較。

圖片描述

  • O(log n),也叫對數時間,這樣的算法包括二分查找
  • O(n),也叫線性時間,這樣的算法包括簡單查找。
  • O(n * log n),這樣的算法包括快速排序——一種速度較快的排序算法。
  • 圖片描述,這樣的算法包括選擇排序——一種速度較慢的排序算法
  • O(n!),這樣的算法包括接下來將介紹的旅行商問題的解決方案——一種很是慢的算法

圖片描述

小結

  • 算法的速度指的並不是時間,而是操做數的增速。
  • 談論算法的速度時,咱們說的是隨着輸入的增長,其運行時間將以什麼樣的速度增長。
  • 算法的運行時間用大O表示法表示。
  • O(log n)比O(n)快,當須要搜索的元素越多時,前者比後者快得越多

快速排序

快排和二分查找都基於一種叫作「分治」的算法思想,經過對數據進行分類處理,不斷下降數量級,實現O(logN)(對數級別,比O(n) 這種線性複雜度更低的一種,快排核心是二分法的O(logN) ,實際複雜度爲O(N*logN) )的複雜度。

快排大概的流程是:

  1. 隨機選擇數組中的一個數 A,以這個數爲基準
  2. 其餘數字跟這個數進行比較,比這個數小的放在其左邊,大的放到其右邊
  3. 通過一次循環以後,A 左邊爲小於 A 的,右邊爲大於 A 的
  4. 這時候將左邊和右邊的數再遞歸上面的過程

clipboard.png

旅行商問題--複雜度O(n!)的算法

簡單的講若是旅行者要去5個城市,前後順序肯定有5*4*3*2*1 = 120種排序。(這種排序想一想高中時候學到過的排序知識)

推而廣之,涉及n個城市時,須要執行n!(n的階乘)次操做才能計算出結果。所以運行時間
爲O(n!),即階乘時間。除非涉及的城市數不多,不然須要執行很是多的操做。若是涉及的城市
數超過100,根本就不能在合理的時間內計算出結果——等你計算出結果,太陽都沒了。

這種算法很糟糕!,可別無選擇。這是計算機科學領域待解的問題之一。對於這個問題,目前尚未找到更快的算法,有些很聰明的人認爲這個問題根本就沒有更巧妙的算法。
面對這個問題,咱們能作的只是去找出近似答案。

最後須要指出的一點是,高水平的讀者可研究一下二叉樹

關於二叉樹,戳這裏: 數據結構與算法:二叉樹算法

常見練習

在一個二維數組中,每一行都按照從左到右遞增的順序排序,每一列都按照從上到下遞增的順序排序。請完成一個函數,輸入這樣的一個二維數組和一個整數,判斷數組中是否含有該整數。

參考

算法圖解
JavaScript 算法與數據結構
https://github.com/egonSchiel...
【算法】時間複雜度
【算法】空間複雜度
InterviewMap 時間複雜度
https://github.com/trekhleb/j...
每週一練 之 數據結構與算法(Stack)
All Algorithms implemented in Python

相關文章
相關標籤/搜索