數(shù)組
作為最簡(jiǎn)單诗宣,最基礎(chǔ)的一個(gè)數(shù)據(jù)結(jié)構(gòu),大多數(shù)語(yǔ)言都天然地對(duì)數(shù)組有著原生的表達(dá)想诅,Javascript亦然召庞。
“開(kāi)箱即用”,而不必自行實(shí)現(xiàn)来破,非常方便篮灼。
首先我們需要知道: JS 數(shù)組未必是真正的數(shù)組
在大多數(shù)的計(jì)算機(jī)語(yǔ)言中,數(shù)組都對(duì)應(yīng)著一段連續(xù)的內(nèi)存徘禁。如果我們想要在任意位置刪除一個(gè)元素诅诱,那么該位置往后的所有元素,都需要往前挪一個(gè)位置晌坤;相應(yīng)地逢艘,如果要在任意位置新增一個(gè)元素旦袋,那么該位置往后的所有元素也都要往后挪一個(gè)位置。
我們假設(shè)數(shù)組的長(zhǎng)度是 n它改,那么因增加/刪除操作導(dǎo)致需要移動(dòng)的元素?cái)?shù)量疤孕,就會(huì)隨著數(shù)組長(zhǎng)度 n 的增大而增大,呈一個(gè)線性關(guān)系央拖。所以說(shuō)數(shù)組增加/刪除操作對(duì)應(yīng)的復(fù)雜度就是 O(n)祭阀。
但 JS 中不一定是。
JS比較特別鲜戒。如果我們?cè)谝粋€(gè)數(shù)組中只定義了一種類(lèi)型的元素专控,比如:
const arr = [1,2,3,4]
它是一個(gè)純數(shù)字?jǐn)?shù)組,那么對(duì)應(yīng)的確實(shí)是連續(xù)內(nèi)存遏餐。
但如果我們定義了不同類(lèi)型的元素:
const arr = ['haha', 1, {a:1}]
它對(duì)應(yīng)的就是一段非連續(xù)的內(nèi)存伦腐。此時(shí),JS 數(shù)組不再具有數(shù)組的特征失都,其底層使用哈希映射分配內(nèi)存空間柏蘑,是由對(duì)象鏈表來(lái)實(shí)現(xiàn)的。
所以說(shuō)“JS 數(shù)組未必是真正的數(shù)組”粹庞。
在各大教材(包括百科詞條)對(duì)數(shù)組的定義中咳焚,都有一個(gè)“存儲(chǔ)在連續(xù)的內(nèi)存空間里”這樣的必要條件。
這同時(shí)是前端面試題中庞溜,數(shù)組和鏈表辨析的答案革半,我們要意識(shí)到?JS 數(shù)組和常規(guī)數(shù)組的不同,相對(duì)于數(shù)組來(lái)說(shuō)流码,鏈表有個(gè)明顯的有點(diǎn)又官,就是添加和刪除元素都不需要挪動(dòng)多余的元素。
Tips:要對(duì)數(shù)組格外走心漫试,畢竟后面需要它幫忙的地方會(huì)非常多赏胚。
數(shù)組的創(chuàng)建:
最多的創(chuàng)建方式:
const arr = [ 1, 2, 3 ]
但是在算法題中,很多時(shí)候我們初始化一個(gè)數(shù)組時(shí)商虐,并不知道它內(nèi)部元素的情況觉阅,這種場(chǎng)景下,推薦大家使用new的方式初始化數(shù)組
const arr = new Array()
// 等價(jià)于
const arr = []
不過(guò)我們使用構(gòu)造函數(shù)秘车,可不是為了創(chuàng)建空數(shù)組這么無(wú)聊典勇。
我們需要它的時(shí)候,往往是因?yàn)槲覀冇?i>創(chuàng)造指定長(zhǎng)度的空數(shù)組這樣的需求叮趴。需要多長(zhǎng)的數(shù)組就給它傳多大的參數(shù)割笙。
const arr = new Array(7)
// 創(chuàng)建一個(gè)指定長(zhǎng)度為7的數(shù)組
在一些場(chǎng)景還需要我們創(chuàng)建一個(gè)指定長(zhǎng)度,同時(shí)每一個(gè)元素的值也都確定的數(shù)組。這時(shí)我們可以調(diào)用fill方法伤溉,假設(shè)需求是每一個(gè)坑里都填上一個(gè)1般码,只需給它fill一個(gè)1:
const arr = (new Array(7)).fill(1)
// [1,1,1,1,1,1,1]
數(shù)組的訪問(wèn)和遍歷:
訪問(wèn)數(shù)組中的元素,我們直接在中括號(hào)中指定其索引即可:
arr[0]? ? // 訪問(wèn)索引下標(biāo)為0的元素
而遍歷數(shù)組乱顾,這個(gè)方法就多了板祝,不過(guò)目的往往都是一致的(訪問(wèn)數(shù)組中的每一個(gè)元素,并且知道當(dāng)前元素的索引)走净。這里我們介紹三個(gè)方法:
1.for循環(huán)
這是最基礎(chǔ)的操作券时。我們可以通過(guò)循環(huán)數(shù)組的下標(biāo),來(lái)依次訪問(wèn)每個(gè)值:
// 獲取數(shù)組的長(zhǎng)度
const len = arr.length
for(let i=0;i<len;i++) {
????// 輸出數(shù)組的元素值伏伯,輸出當(dāng)前索引 console.log(arr[i], i)
}
2.forEach方法
通過(guò)區(qū)forEach方法中傳入函數(shù)的第一個(gè)入?yún)⒑偷诙€(gè)入?yún)㈤俣矗覀円部梢匀〉綌?shù)組每個(gè)元素的值及其對(duì)應(yīng)索引:
arr.forEach((item, index)=> {
????// 輸出數(shù)組的元素值,輸出當(dāng)前索引
????console.log(item, index)
})
3.map方法
map方法在調(diào)用形式上與forEach無(wú)異说搅,區(qū)別于map方法會(huì)根據(jù)你傳入的函數(shù)邏輯對(duì)數(shù)組的每一個(gè)元素進(jìn)行處理炸枣,進(jìn)而返回一個(gè)全新的數(shù)組。
所以其實(shí)map做的事情不僅僅是遍歷弄唧,而是在遍歷的基礎(chǔ)上再加工抛虏。當(dāng)我們需要對(duì)數(shù)組內(nèi)容做批量修改,同時(shí)修改邏輯又高度一致時(shí)套才,就可以調(diào)用map來(lái)達(dá)到我們的目的:
const newArr = arr.map((item, index)=> {
????// 輸出數(shù)組的元素值,輸出當(dāng)前索引
????console.log(item, index)
????// 在當(dāng)前元素值的基礎(chǔ)上加1
????return item+1
})
小建議:沒(méi)有特殊的需求慕淡,統(tǒng)一使用for循環(huán)來(lái)實(shí)現(xiàn)遍歷背伴。因?yàn)?b>從性能上看,for循環(huán)遍歷起來(lái)是最快的峰髓。
二維數(shù)組
二維數(shù)組的特點(diǎn):從形狀上看傻寂,相對(duì)于一維數(shù)組一條“線”一般的布局,二維數(shù)組更像是一個(gè)“面”携兵。拿咱們這個(gè)例子來(lái)說(shuō)疾掰,這里的二維數(shù)組邏輯分布圖就宛如一個(gè)正方形。當(dāng)然啦徐紧,如果我們稍微延長(zhǎng)一下其中的一邊静檬,它也可以是一個(gè)矩形。
在數(shù)學(xué)中并级,形如這樣長(zhǎng)方陣列排列的復(fù)數(shù)或?qū)崝?shù)集合拂檩,被稱(chēng)為“矩陣”。因此二維數(shù)組的別名就叫“矩陣”嘲碧。
必須要記住“矩陣”和“二維數(shù)組”之間的等價(jià)關(guān)系稻励。在算法題目中,見(jiàn)到“矩陣”時(shí)愈涩,能夠立刻反射出它說(shuō)的是二維數(shù)組,不被別名整懵逼,這就夠了习劫。
二維數(shù)組的初始化:
const arr =(new Array(7)).fill([])
以上的方式對(duì)嗎萤彩?乍一看,沒(méi)什么毛病亦镶。
但是當(dāng)你想修改某一個(gè)坑里的數(shù)組的值的時(shí)候,你就會(huì)發(fā)現(xiàn)問(wèn)題。
正確的初始化一個(gè)二維數(shù)組的方式:
const len = arr.length
for(let i=0;i<len;i++) {
????// 將數(shù)組的每一個(gè)坑位初始化為數(shù)組 arr[i] = []
}
二維數(shù)組的訪問(wèn):
訪問(wèn)二維數(shù)組和訪問(wèn)一維數(shù)組差別不大趣惠,區(qū)別在于我們現(xiàn)在需要的是兩層循環(huán)。
一維數(shù)組用 for 循環(huán)遍歷只需一層循環(huán)身害,二維數(shù)組是兩層味悄,三維數(shù)組就是三層。依次類(lèi)推塌鸯,N 維數(shù)組需要 N 層循環(huán)來(lái)完成遍歷侍瑟。
這遠(yuǎn)不是數(shù)組的全部,目前僅圍繞數(shù)組最基本的操作進(jìn)行介紹丙猬,在javascript數(shù)據(jù)結(jié)構(gòu)中涨颜,數(shù)組幾乎是基石一般的存在。
棧和隊(duì)列
在javascript中茧球,棧和隊(duì)列的實(shí)現(xiàn)一般都要依賴于數(shù)組庭瑰,“特別的數(shù)組”。
(注:實(shí)際上抢埋,棧和隊(duì)列作為兩種運(yùn)算受限的線性表弹灭,用鏈表來(lái)實(shí)現(xiàn)也是沒(méi)問(wèn)題的。只是從前端面試做題的角度來(lái)說(shuō)揪垄,基于鏈表來(lái)實(shí)現(xiàn)棧和隊(duì)列約等于脫褲子放屁(鏈表實(shí)現(xiàn)起來(lái)會(huì)比數(shù)組麻煩得多穷吮,做不到開(kāi)箱即用),基本沒(méi)人會(huì)這么干饥努。這里大家按照數(shù)組的思路往下走就行了)
作為有經(jīng)驗(yàn)的前端捡鱼,大家對(duì)javascript數(shù)組的操作恐怕都不陌生。
但我們今天講的棧和隊(duì)列酷愧,兩者的區(qū)別在于驾诈,它們各自對(duì)數(shù)組的增刪操作有著不一樣的限制。因此我們先熟悉一下javascript數(shù)組中的增刪操作具有什么樣的特性溶浴,對(duì)應(yīng)的方法有哪些翘鸭?
增加:
unshift 方法-添加元素到數(shù)組的頭部
push 方法-添加元素到數(shù)組的尾部
splice 方法-添加元素到數(shù)組的任何位置
刪除:
shift 方法-刪除數(shù)組頭部的元素
pop 方法-刪除數(shù)組尾部的元素
splice 方法-刪除數(shù)組任意位置的元素
棧(Stack)——只用 pop 和 push 完成增刪的“數(shù)組”
棧是一種后進(jìn)先出(LIFO,Last In First Out)的數(shù)據(jù)結(jié)構(gòu)戳葵。
隊(duì)列(Queue)——只用 push 和 shift 完成增刪的“數(shù)組”
隊(duì)列是一種先進(jìn)先出(FIFO就乓,F(xiàn)irst In First Out)的數(shù)據(jù)結(jié)構(gòu)。
閱讀掘金小冊(cè)的《前端算法與數(shù)據(jù)結(jié)構(gòu)面試:底層邏輯解讀與大廠真題訓(xùn)練》所獲。富有的朋友們值得購(gòu)買(mǎi)一讀生蚁。