大廠算法考點須知

什麼是時間複雜度?

通常狀況下,算法中基本操做重複執行的次數是問題規模n的某個函數,用T(n)表示,如有某個輔助函數f(n),使得當n趨近於無窮大時,T(n)/f(n)的極限值爲不等於零的常數,則稱f(n)是T(n)的同數量級函數。記做T(n)=O(f(n)),稱O(f(n))爲算法的漸進時間複雜度(O是數量級的符號 ),簡稱時間複雜度。git

如何計算時間複雜度?

當問題規模隨着處理數據的增加時,基本操做要重複執行的次數一定會隨着增加,那麼咱們須要知道執行次數是以什麼樣的數量級增加的。github

接下來咱們用大O表示法標識一下常見的時間複雜度量級:算法

O(1)

常數階的複雜度,這種複雜度不管數據規模n如何增加,計算時間是不變的。數組

一個簡單的例子:函數

const add = (a, b) => a + b
複製代碼

無論n如何增加,都不會影響到這個函數的執行時間,所以這個函數的時間複雜度爲O(1)。ui

O(n)

線性複雜度,隨着數據規模n的增加,時間複雜度也會隨着n線性增加spa

const indexOf = (arr, target) => {
  let len = arr.length
  while(len--) {
    if (arr[len] === target) {
      return arr[len]
    }
  }
}
複製代碼

O(logn)

對數複雜度,隨着問題規模n的增加,計算時間也會隨着n對數級數增加。 如數據增大1024倍時,時間只增大32倍,是比線性複雜度還要低的時間複雜度。code

典型的例子就是二分查找法。(二分查找只支持已經排好序的數組)排序

functions binarySearch(arr, target) {
  let max = arr.length - 1
  let min = 0
  while (min <= max) {
	let mid = Math.floor((max + min) / 2)
	if (target < arr[mid]) {
	  max = mid - 1
	} else if (target > arr[mid]) {
	  min = mid + 1
	} else {
	  return mid
	}
  }
  return -1
}
複製代碼

在二分查找法的代碼中,經過while循環,成 2 倍數的縮減搜索範圍,也就是說須要通過 log2^n 次便可跳出循環。遞歸

事實上在實際項目中,O(logn)是一個很是好的時間複雜度,好比當n=100的數據規模時,二分查找只須要7次,線性查找須要100次,這對於計算機而言差距不大,可是當有10億的數據規模的時候,二分查找依然只須要30次,而線性查找須要驚人的10億次,O(logn)時間複雜度的算法隨着數據規模的增大,它的優點就越明顯。

O(n²)

平方級複雜度,典型狀況是當存在雙重循環的時候,即把 O(n) 的代碼再嵌套循環一遍,它的時間複雜度就是 O(n²) 了,表明應用是冒泡排序算法。

冒泡排序

實現原理: 以最終目標爲升序排列爲例:

  1. 比較相鄰的兩個元素,若是第一個元素小於第二個,則交換位置。
  2. 從開始的兩個元素開始對比,一直對比到最後,直到最後一個數爲最大值。
  3. 不斷重複以上的步驟,獲得最終的結果
// 冒泡排序
function bubbleSort(arr) {
  for (var i = 0; i < arr.length - 1; i++) {
    for (var j = 0; j < arr.length - 1 - i; j++) {
      if (arr[j] > arr[j + 1]) {
        var temp = arr[j]
        arr[j] = arr[j + 1]
        arr[j + 1] = temp
      }
    }
  }
  return arr
}
複製代碼

冒泡排序加強版

爲了提高效率,增長最小值,和最大值,而且正反分別對比一次。減小執行的對比次數,從而達到提升效率的目的。

function bubbleSortEnhancement(arr) {
  let low = 0
  let high = arr.length - 1
  let i
  let temp
  while (low < high) {
    for (i = low; i < high; i++) {
      if (arr[i] > arr[i + 1]) {
        temp = arr[i]
        arr[i] = arr[i + 1]
        arr[i + 1] = temp
      }
    }
    high--

    for (i = high; i > low; i--) {
      if (arr[i] < arr[i - 1]) {
        temp = arr[i]
        arr[i] = arr[i - 1]
        arr[i - 1] = temp
      }
    }
    low++
  }
  return arr
}
複製代碼

O(nlogn)

當數據增大n倍時,執行時間隨着增大nlogn倍,這個複雜度高於線性複雜度,低於平方複雜度。歸併排序和快速排序就是典型的表明。

快速排序

實現原理:

  1. 將要排序的數據分割成獨立的兩部分
  2. 其中一部分的全部數據要比另一部分的全部數據要小
  3. 而後按這兩部分數據分別進行快速排序,可使用遞歸進行
  4. 以此達到整個數據變成有序序列
// 快速排序
function quickSort(arr) {
  if (arr.length <= 1) {
    return arr
  }
  var pivotIndex = Math.floor(arr.length / 2)
  var pivot = arr.splice(pivotIndex, 1)[0]
  var left = []
  var right = []
  for (var i = 0; i < arr.length; i++) {
    if (arr[i] <= pivot) {
      left.push(arr[i])
    } else {
      right.push(arr[i])
    }
  }
  return quickSort(left).concat([pivot], quickSort(right))
}
複製代碼

歸併排序

實現原理:

  1. 把一個長度爲n的數組分爲n/2長度的兩個子數組
  2. 對兩個數組分別執行歸併排序
  3. 最終合併兩個數組
function mergeSort(arr) {
  //採用自上而下的遞歸方法
  var len = arr.length
  if (len < 2) {
    return arr
  }
  var middle = Math.floor(len / 2),
    left = arr.slice(0, middle),
    right = arr.slice(middle)
  return merge(mergeSort(left), mergeSort(right))
}

function merge(left, right) {
  var result = []
  console.time('歸併排序耗時')
  while (left.length && right.length) {
    if (left[0] <= right[0]) {
      result.push(left.shift())
    } else {
      result.push(right.shift())
    }
  }

  while (left.length) result.push(left.shift())

  while (right.length) result.push(right.shift())
  console.timeEnd('歸併排序耗時')
  return result
}
複製代碼

博客地址 gitbook小冊

相關文章
相關標籤/搜索