append 降低数组位数_4.有序数组
果然我在這個(gè)專欄的更新上沒有壓力,哈哈,這挺好的,不用在乎別人是不是看,反正自己總結(jié)出來(lái),鞏固了知識(shí)就好。
有序數(shù)組其實(shí)就是數(shù)組的進(jìn)化版,和數(shù)組唯一的區(qū)別就是“有序”這兩個(gè)字,就是指在這個(gè)數(shù)組當(dāng)中的所有數(shù)據(jù),必須按照一定的順序來(lái)排列,即便是插入新數(shù)據(jù)或者刪除原有數(shù)據(jù),仍然要按照既定規(guī)則來(lái)排序;而普通的數(shù)組是不考慮順序的,新添加的數(shù)據(jù)可以在末尾(JavaScript:Array.push;Python:list.append)也可以在開頭(JavaScript:Array.unshift;Python:list.insert(0, element)),當(dāng)然這里不考慮語(yǔ)言的差異,括號(hào)里面僅僅是兩種常見語(yǔ)言的樣例。
1 插入
比如有一個(gè)數(shù)組 [3, 17, 80, 202],它如果是個(gè)普通數(shù)組,當(dāng)我們想插入新的數(shù)值 75 時(shí),可以直接放在數(shù)組最后:
那么,這里只需要 1 步就能完成操作了。
可如果是個(gè)有序數(shù)組,并且按照數(shù)字升序排列,就必須要找到一個(gè)合適的位置來(lái)插入新的數(shù)值,從而使得整個(gè)數(shù)組依舊保持有序:
那么這個(gè)步驟就不可能只需 1 步完成了,計(jì)算機(jī)需要先找到合適的位置,然后還需要把應(yīng)該屬于其后面的值后移,給插入騰出位置,最后再插入 75。
(1)檢查索引 0 的值,并對(duì)比其是否大于 75:
(2)因?yàn)?3 小于 75,按照升序的規(guī)則,75 應(yīng)該在它后面的位置,所以再檢查下一個(gè)索引 1 的值:
(3)因?yàn)?17 小于 75,繼續(xù)檢查下一個(gè)索引:
(4)這次的 80 明顯大于 75,所以可以確定把 75 放置在索引 2 這個(gè)位置,并把之后的所有值后移即可,首先后移最后一個(gè)值:
(5)接著后移 80:
(6)終于可以把 75 插入到正確的位置上了:
如上所述,我們可以看到,如果在有序數(shù)組當(dāng)中插入新值,首先就要遍歷以此確定需要插入的位置,這也就比普通數(shù)組多了很多步驟,意味著在插入數(shù)據(jù)方面,其性能比不上普通的數(shù)組;但是有序數(shù)組在查找方面,有其特殊的優(yōu)勢(shì)。
2 查找
普通的數(shù)組查找其實(shí)就是標(biāo)準(zhǔn)的遍歷比較,從頭至尾逐個(gè)檢查,直到獲取到了完全一致(或者根據(jù)正則表達(dá)式得到相對(duì)一致)的結(jié)果,這種方式被稱為線性查找。
比如這里有個(gè)數(shù)組 [3, 17, 75, 80, 202],如果我們想要查找 22 是否存在,是普通數(shù)組的話,就需要逐個(gè)元素檢查對(duì)比,直到確定不存在;而有序數(shù)組則不必遍歷整個(gè)數(shù)組,只要查找到 75 就可以停止了,因?yàn)?75 > 22,而后面的元素一定比 75 大,所以 22 并不存在。
這里可以用 JavaScript 來(lái)嘗試實(shí)現(xiàn)一個(gè)有序數(shù)組的線性查找(用的是比較基礎(chǔ)的方式,沒什么騷操作):
// 定義一個(gè)函數(shù) linearSearch function linearSearch(array, value) {// 遍歷數(shù)組的每一個(gè)元素for (let i = 0; i < array.length; i++) {// 如果該元素等于我們要尋找的值,則記錄在控制臺(tái),并退出循環(huán)if (array[i] === value) {console.log(`Found at index ${i}!`);break;// 如果該元素大于要尋找的值,則退出循環(huán)} else if (array[i] > value) {console.log('Not found!');break;}} }所以,在大多數(shù)情況下,有序數(shù)組的線性查找都要快于普通數(shù)組,除非要找的值大于等于最后一個(gè),那就沒什么區(qū)別了。
到這里,其實(shí)我們?nèi)匀豢床怀鲇行驍?shù)組比普通數(shù)組的優(yōu)勢(shì)有多大,這是因?yàn)槲覀円恢痹谑褂镁€性查找,當(dāng)然這僅僅是查找算法當(dāng)中的一種,而且是最基礎(chǔ)的一種;而改變算法,我們就可以看出巨大的不同了,比如二分查找。
3 二分查找
其實(shí)二分查找的原理很簡(jiǎn)單,就是我們經(jīng)常玩的那種猜數(shù)字的游戲,從 1 到 100 的數(shù)字當(dāng)中,出題人確定了一個(gè),其他人則猜測(cè),出題人可以根據(jù)猜測(cè)結(jié)果說(shuō)數(shù)字猜大了還是小了;大多數(shù)人都會(huì)先猜 50,而不是 1 ——因?yàn)椴还芙酉聛?lái)是更大還是更小,我們能可以排除掉一半的錯(cuò)誤答案,大幅度的降低下一輪猜測(cè)的范圍。
這里有個(gè)小范例,為了偷懶,我們就以數(shù)字 1 到 10 為例:
其實(shí)這就是二分查找的通俗描述,我也不用像其他算法書上面寫一堆概念什么的了,哈哈;從這里可以看出來(lái),普通數(shù)組因?yàn)闆]有排序,所以不可能使用二分查找,也就是說(shuō),有序數(shù)組有這個(gè)優(yōu)勢(shì)。
我們可以假設(shè)有包含 9 個(gè)元素的有序數(shù)組,計(jì)算機(jī)目前還不知道每個(gè)索引的值:
然后我們嘗試用二分法來(lái)查找 7 這個(gè)值:
(1)首先檢查正中間的索引 4,因?yàn)閿?shù)組長(zhǎng)度已知,所以將長(zhǎng)度除以2就可以精確的判斷正中間的內(nèi)存地址,然后檢查了:
這里的值是 9,大于 7,所以可以推測(cè)出 7應(yīng)該在其之前的索引當(dāng)中,我們也同時(shí)排除了從 9 開始的后面的索引地址:
(2)檢查 9 前面的格子,同樣從中間開始,由于是復(fù)數(shù)所以索引 1 或者 2 都可以,這里隨機(jī)挑選了索引 1:
這里值是 4,同理可以排除 4 以及之前的值了:
(3)剩下兩個(gè)索引,隨機(jī)選擇索引 2:
(4)只剩下一個(gè)了,再檢查(如果還沒有,就說(shuō)明整個(gè)有序數(shù)組中不存在該值):
這樣我們找到了 7,共使用了 4 步;雖然在這個(gè)特例當(dāng)中使用線性查找也是 4 步(因?yàn)?7 在索引 3 的位置上),但是如果是在 100 個(gè)值當(dāng)中查找呢?我們后面可以看看二分查找的優(yōu)勢(shì)。
下面是二分查找的 JavaScript 實(shí)現(xiàn):
function binarySearch(array, value) {// 首先設(shè)置上下界,以索引 0 為下界,最后一個(gè)索引為上界let lowerBound = 0;let upperBound = array.length - 1;// 循環(huán)檢查上下界之間的最中間元素while (lowerBound <= upperBound) {// 找出最中間的索引,并使用 Math.floor 函數(shù)向下轉(zhuǎn)化為整數(shù)let midpoint = Math.floor((upperBound + lowerBound) / 2);// 獲取最中間索引的值valueOfMidpoint = array[midpoint];// 如果等于 value,搞定收工// 否則就需要根據(jù)該值大于還是小于查找值,來(lái)調(diào)整上下界if (value < valueOfMidpoint) {upperBound = midpoint - 1;} else if (value > valueOfMidpoint) {lowerBound = midpoint + 1;} else if (value === valueOfMidpoint) {return midpoint;break;}} }4 二分查找和線性查找的比較
上面提及的 100 個(gè)值的有序數(shù)組,兩種查找算法的最多步數(shù)如下:
- 線性查找:100步;如果要找的值大于等于最后一個(gè)值,就必需100步
- 二分查找:7步;因?yàn)槊看尾聹y(cè)后會(huì)排除掉一半的元素,所以我們最大步數(shù)就是:
- 第一步,剩50
- 第二步,剩25
- 第三步,剩13
- 第四步,剩7
- 第五步,剩3
- 第六步,剩1
- 第七步,確定
換個(gè)角度我們可以發(fā)現(xiàn)一個(gè)規(guī)律,每次有序數(shù)組長(zhǎng)度乘以 2,二分查找所需的最多步數(shù)只會(huì)加 1,這就很能體現(xiàn)二分查找的優(yōu)勢(shì)了,兩者的復(fù)雜度對(duì)比如下圖:
如果數(shù)組變得更大,比如說(shuō)有 10,000 個(gè)元素,線性查找最多要 10,000 步,而二分查找最多只需要14步;這個(gè)數(shù)量級(jí)來(lái)到 1,000,000 的話,則差距就更大了,二分查找僅僅最多 20 步就可以完成,線性查找則最多需要 1,000,000。
這里就可以看出合適的算法可以對(duì)操作有多么巨大的性能影響了。
當(dāng)然有序數(shù)組并不是所有的操作都要比普通數(shù)組快,譬如插入操作;所以我們需要區(qū)分應(yīng)用場(chǎng)景來(lái)選擇合適的數(shù)據(jù)結(jié)構(gòu)。
總結(jié)
以上是生活随笔為你收集整理的append 降低数组位数_4.有序数组的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: wps出现安装installer_判断本
- 下一篇: 新三板的股票能买吗 还是要看大家的风险承