偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

程序員必備的幾種常見(jiàn)排序算法和搜索算法總結(jié)

開(kāi)發(fā) 前端
我們?cè)趯W(xué)排序算法時(shí), 最容易掌握的就是冒泡排序, 因?yàn)槠鋵?shí)現(xiàn)起來(lái)非常簡(jiǎn)單,但是從運(yùn)行性能的角度來(lái)看, 它卻是性能最差的一個(gè)。

前言

最近為了鞏固一下自己的算法基礎(chǔ),又把算法書(shū)里的基本算法刷了一遍, 特地總結(jié)一下前端工程師需要了解的排序算法和搜索算法知識(shí),雖然還有很多高深算法需要了解, 但是基礎(chǔ)還是要好好鞏固一下的.本文將以圖文的形式為大家介紹如下算法知識(shí),希望在讀完之后大家能有所收獲:

  • 冒泡排序及其優(yōu)化
  • 選擇排序
  • 插入排序
  • 歸并排序
  • 快速排序
  • 順序搜索
  • 二分搜索

正文

我想對(duì)于每個(gè)前端工程師來(lái)說(shuō), 最頭疼的就是算法問(wèn)題, 但是算法往往也是衡量一個(gè)人編程能力的一個(gè)很重要的指標(biāo).目前很多主流框架和庫(kù)都應(yīng)用了大量的算法和設(shè)計(jì)模式,為了讓自己的段位更高,我們只能不斷的"打怪"(也就是刷算法)升級(jí),才能成為"最強(qiáng)王者".

其實(shí)前端發(fā)展這么多年, 越來(lái)越偏向于精細(xì)化開(kāi)發(fā), 很多超級(jí)應(yīng)用(比如淘寶,微信)都在追求極致的用戶(hù)體驗(yàn), 時(shí)間就是金錢(qián),這要求工程師們不能像以前那樣,開(kāi)發(fā)的程序只要能用就行, 我們往往還要進(jìn)行更加細(xì)致的測(cè)試(包括單元測(cè)試, 性能測(cè)試等),就拿排序來(lái)說(shuō), 對(duì)于大規(guī)模數(shù)據(jù)量的排序, 我們采用冒泡排序肯定是要被瘋狂吐槽的,因?yàn)槊芭菖判虻男阅軜O差(復(fù)雜度為O(n^2).在真實(shí)項(xiàng)目中我們往往不會(huì)采用冒泡排序,

有詳細(xì)介紹. 接下來(lái)就讓我們來(lái)一起學(xué)習(xí)如何實(shí)現(xiàn)文章開(kāi)頭的幾個(gè)常用排序和搜索算法吧.

1. 冒泡排序及其優(yōu)化

我們?cè)趯W(xué)排序算法時(shí), 最容易掌握的就是冒泡排序, 因?yàn)槠鋵?shí)現(xiàn)起來(lái)非常簡(jiǎn)單,但是從運(yùn)行性能的角度來(lái)看, 它卻是性能最差的一個(gè).

冒泡排序的實(shí)現(xiàn)思路是比較任何兩個(gè)相鄰的項(xiàng), 如果前者比后者大, 則將它們互換位置.

為了更方便的展示冒泡排序的過(guò)程和性能測(cè)試,筆者先寫(xiě)幾個(gè)工具方法,分別為動(dòng)態(tài)生成指定個(gè)數(shù)的隨機(jī)數(shù)組, 生成元素位置序列的方法,代碼如下:

// 生成指定個(gè)數(shù)的隨機(jī)數(shù)組
const generateArr = (num = 10) => {
let arr = []
for(let i = 0; i< num; i++) {
let item = Math.floor(Math.random() * (num + 1))
arr.push(item)
}
return arr
}

// 生成指定個(gè)數(shù)的元素x軸坐標(biāo)
const generateArrPosX = (n= 10, w = 6, m = 6) => {
let pos = []
for(let i = 0; i< n; i++) {
let item = (w + m) * i
pos.push(item)
}
return pos
}

有了以上兩個(gè)方法,我們就可以生成任意個(gè)數(shù)的數(shù)組以及數(shù)組項(xiàng)坐標(biāo)了,這兩個(gè)方法接下來(lái)我們會(huì)用到.

我們來(lái)直接寫(xiě)個(gè)乞丐版的冒泡排序算法:

bubbleSort(arr = []) {
let len = arr.length
for(let i = 0; i< len; i++) {
for(let j = 0; j < len - 1; j++) {
if(arr[j] > arr[j+1]) {
// 置換
[arr[j], arr[j+1]] = [arr[j+1], arr[j]]
}
}
}
return arr
}

接下來(lái)我們來(lái)測(cè)試一下, 我們用generateArr方法生成60個(gè)數(shù)組項(xiàng)的數(shù)組, 并動(dòng)態(tài)生成元素坐標(biāo):

// 生成坐標(biāo)
const pos = generateArrPosX(60)
// 生成60個(gè)項(xiàng)的數(shù)組
const arr = generateArr(60)

執(zhí)行代碼后會(huì)生成下圖隨機(jī)節(jié)點(diǎn)結(jié)構(gòu):

圖片

有關(guān)css部分這里就不介紹了,大家可以自己實(shí)現(xiàn).接下來(lái)我們就可以測(cè)試我們上面寫(xiě)的冒泡排序了,當(dāng)我們點(diǎn)擊排序時(shí),結(jié)果如下:

圖片

可以看到數(shù)組已按照順序排好了,我們可以使用console.time來(lái)測(cè)量代碼執(zhí)行所用的時(shí)間,上面"乞丐版"冒泡排序耗時(shí)為0.2890625ms.


我們深入分析代碼就可以知道兩層for循環(huán)排序?qū)е铝撕芏喽嘤嗟呐判?如果我們從內(nèi)循環(huán)減去外循環(huán)中已跑過(guò)的輪數(shù),就可以避免內(nèi)循環(huán)中不必要的比較,所以我們代碼優(yōu)化如下:

// 冒泡排序優(yōu)化版
bubbleSort(arr = []) {
let len = arr.length
// 優(yōu)化
for(let i = 0; i< len; i++) {
for(let j = 0; j < len - 1 - i; j++) {
if(arr[j] > arr[j+1]) {
// 置換
[arr[j], arr[j+1]] = [arr[j+1], arr[j]]
}
}
}
return arr
}

經(jīng)過(guò)優(yōu)化的冒泡排序耗時(shí):0.279052734375ms, 比之前稍微好了一丟丟, 但仍然不是推薦的排序算法.

2. 選擇排序

選擇排序的思路是找到數(shù)據(jù)結(jié)構(gòu)中的最小值并將其放置在第一位,接著找到第二個(gè)最小值并將其放到第二位,依次類(lèi)推.

我們還是按照之前的模式,生成一個(gè)60項(xiàng)的數(shù)組, 如下:

圖片

選擇排序代碼如下:

selectionSort(arr) {
let len = arr.length,
indexMin
for(let i = 0; i< len -1; i++) {
indexMin = i
for(let j = i; j < len; j++){
if(arr[indexMin] > arr[j]) {
indexMin = j
}
}
if(i !== indexMin) {
[arr[i], arr[indexMin]] = [arr[indexMin], arr[i]]
}
}
return arr
}

點(diǎn)擊排序時(shí), 結(jié)果如下:

圖片

說(shuō)明代碼運(yùn)行正常, 可以實(shí)現(xiàn)排序, 控制臺(tái)耗時(shí)為: 0.13720703125ms, 明顯比冒泡排序性能要好.

3. 插入排序

插入排序的思路是每次排一個(gè)數(shù)組項(xiàng),假定第一項(xiàng)已經(jīng)排序,接著它和第二項(xiàng)比較, 決定第二項(xiàng)的位置, 然后接著用同樣的方式?jīng)Q定第三項(xiàng)的位置, 依次類(lèi)推, 最終將整個(gè)數(shù)組從小到大依次排序.

代碼如下:

insertionSort(arr) {
let len = arr.length,
j,
temp;
for(let i = 1; i< len; i++) {
j = i
temp = arr[i]
while(j > 0 && arr[j-1] > temp) {
arr[j] = arr[j-1]
j--
}
arr[j] = temp;
}
}

執(zhí)行結(jié)果如下:

圖片

控制臺(tái)打印耗時(shí)為:0.09912109375ms.

4. 歸并排序

歸并排序算法性能比以上三者都好, 可以在實(shí)際項(xiàng)目中投入使用,但實(shí)現(xiàn)方式相對(duì)復(fù)雜.

歸并排序是一種分治算法,其思想是將原始數(shù)組切分成較小的數(shù)組,直到每個(gè)小數(shù)組只有一個(gè)元素,接著將小數(shù)組歸并成較大的數(shù)組,最后變成一個(gè)排序完成的大數(shù)組。

其實(shí)現(xiàn)過(guò)程如下圖所示:

圖片

為了實(shí)現(xiàn)該方法我們需要準(zhǔn)備一個(gè)合并函數(shù)和一個(gè)遞歸函數(shù),具體實(shí)現(xiàn)如下代碼:

// 歸并排序
mergeSortRec(arr) {
let len = arr.length
if(len === 1) {
return arr
}
let mid = Math.floor(len / 2),
left = arr.slice(0, mid),
right = arr.slice(mid, len)
return merge(mergeSortRec(left), mergeSortRec(right))
}
// 合并方法
merge(left, right) {
let result = []
l = 0,
r = 0;
while(l < left.length && r < right) {
if(left[l] < right(r)) {
result.push(left[l++])
}else {
result.push(right[r++])
}
}
while(l < left.length) {
result.push(left[l++])
}
while(r < right.length) {
result.push(right[r++])
}
return result
}

以上代碼中的遞歸作用是將一個(gè)大數(shù)組劃分為多個(gè)小數(shù)組直到只有一項(xiàng),然后再逐層進(jìn)行合并排序。如果有不理解的可以和筆者交流或者結(jié)合筆者畫(huà)的草圖進(jìn)行理解。??

圖片

5. 快速排序

快速排序是目前比較常用的排序算法,它的復(fù)雜度為O(nlog^n),并且它的性能比其他復(fù)雜度為O(nlog^n)的好,也是采用分治的思想,將原始數(shù)組進(jìn)行劃分,由于快速排序?qū)崿F(xiàn)起來(lái)比較復(fù)雜,這里講一下思路:

  1. 從數(shù)組中選擇中間項(xiàng)作為主元
  2. 創(chuàng)建兩個(gè)指針,左邊一個(gè)指向數(shù)組第一項(xiàng),右邊一個(gè)指向數(shù)組最后一項(xiàng),移動(dòng)左指針直到我們找到一個(gè)比主元大的元素,移動(dòng)右指針直到找到一個(gè)比主元小的元素,然后交換它們的位置,重復(fù)此過(guò)程直到左指針超過(guò)了右指針
  3. 算法對(duì)劃分后的小數(shù)組重復(fù)1,2步驟,直到數(shù)組完全排序完成。

代碼如下:

// 快速排序
quickSort(arr, left, right) {
let index
if(arr.length > 1) {
index = partition(arr, left, right)
if(left < index - 1) {
quickSort(arr, left, index -1)
}
if(index < right) {
quickSort(arr, index, right)
}
}
}
// 劃分流程
partition(arr, left, right) {
let part = arr[Math,floor((right + left) / 2)],
i = left,
j = right
while(i <= j) {
while(arr[i] < part) {
i++
}
while(arr[j] > part) {
j--
}
if(i <= j) {
// 置換
[arr[i], arr[j]] = [arr[j], arr[i]]
i++
j--
}
}
return i
}

7. 順序搜索

搜索算法也是我們經(jīng)常用到的算法之一,比如我們需要查找某個(gè)用戶(hù)或者某條數(shù)據(jù),不管是在前端還是在后端,都會(huì)使用搜索算法。我們先來(lái)介紹最簡(jiǎn)單也是效率最低的順序搜索,其主要思想是將每一個(gè)數(shù)據(jù)結(jié)構(gòu)中的元素和我們要查詢(xún)的元素做比較,然后返回指定元素的索引。

圖片

之所以說(shuō)順序搜索效率低是因?yàn)槊看味家獜臄?shù)組的頭部開(kāi)始查詢(xún),直到查找到要搜索的值,整體查詢(xún)不夠靈活和動(dòng)態(tài)性。順序搜索代碼實(shí)現(xiàn)如下:

sequentialSearch(arr, item) {
for(let i = 0; i< arr.length; i++) {
if(item === arr[i]) {
return i
}
}
return -1
}

接下來(lái)我們看下面一種比較常用和靈活的搜索算法——二分搜索。

8. 二分搜索

二分搜索的思想有點(diǎn)“投機(jī)學(xué)”的意思,但是它是一種有理論依據(jù)的“投機(jī)學(xué)”。首先它要求被搜索的數(shù)據(jù)結(jié)構(gòu)已排序,其次進(jìn)行如下步驟:

  1. 找出數(shù)組的中間值
  2. 如果中間值是待搜索的值,那么直接返回中間值的索引
  3. 如果待搜索的值比中間值小,則返回步驟1,將區(qū)間范圍縮小,在中間值左邊的子數(shù)組中繼續(xù)搜索
  4. 如果待搜索的值比選中的值大,則返回步驟1,將區(qū)間范圍縮小,在中間值右邊的子數(shù)組中繼續(xù)搜索
  5. 如果沒(méi)有搜到,則返回-1

為了方便理解筆者畫(huà)了如下草圖:

圖片

由上圖大家可以很容易的理解二分搜索的實(shí)現(xiàn)過(guò)程,接下來(lái)我們看下代碼實(shí)現(xiàn):

binarySearch(arr, item) {
// 調(diào)用排序算法先對(duì)數(shù)據(jù)進(jìn)行排序
this.quickSort(arr)

let min = 0,
max = arr.length - 1,
mid,
el
while(min <= max) {
mid = Math.floor((min + max) / 2)
el = arr[mid]
if(el < item) {
min = mid + 1
}else if(el > item) {
max = mid -1
}else {
return mid
}
}
return -1
}

其實(shí)還有很多搜索算法,筆者在js基本搜索算法實(shí)現(xiàn)與170萬(wàn)條數(shù)據(jù)下的性能測(cè)試有具體介紹。

參考文獻(xiàn):Learning JavaScript Data Structures and Algorithms

責(zé)任編輯:武曉燕 來(lái)源: 趣談前端
相關(guān)推薦

2021-09-04 23:40:53

算法程序員前端

2022-09-24 09:03:55

前端單元測(cè)試冒泡排序

2021-11-10 09:17:18

程序員排序算法搜索算法

2011-01-04 11:02:08

程序員

2019-03-29 09:40:38

數(shù)據(jù)結(jié)構(gòu)算法前端

2012-08-20 09:26:17

程序員算法排列算法

2020-10-14 08:32:08

算法遞歸面試

2011-02-17 09:11:40

JavaScript算法

2014-07-01 09:43:55

程序員算法

2009-01-07 21:00:05

2018-06-04 12:41:50

程序員貪心算法分析

2023-05-30 07:58:01

谷歌搜索算法

2023-11-30 08:09:05

2025-02-26 05:00:00

DFS算法遞歸

2020-12-08 05:52:28

js前端算法

2018-10-12 15:15:45

電商搜索算法

2022-12-07 10:21:19

谷歌搜索技巧

2012-02-29 13:32:28

Java

2019-01-21 14:13:51

程序員技能開(kāi)發(fā)者

2021-01-19 15:59:14

程序員算法書(shū)
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)