「算法歸納」常用數(shù)據(jù)結構相關

  • 一個后進先出的數(shù)據(jù)結構
  • JavaScript中沒有棧马澈,使用Array代替
const stack = [];
stack.push(1);
stack.push(2);
const item1 = stack.pop();
const item2 = stack.pop(); // 1 后進先出

什么場景下用棧神帅?

場景一 十進制轉二進制

35 -> 100011

二進制

  • 后出的余數(shù)反而要排在前面
  • 把余數(shù)依次入棧,然后再依次出棧媚狰,就可以實現(xiàn)余數(shù)倒敘輸出

場景二 有效的括號

有效的括號
// 編輯器判斷代碼括號是否正確閉合
var isValid = function(s) {
    if (s.length % 2 === 1) {return false}
    const stack = []

    for(let i = 0; i < s.length; i++) {
        const c = s[i]
        if (c === '(' || c === '{' || c === '[') {
            stack.push(c)
        }
        else {
            // 棧頂
            const t = stack[stack.length - 1]
            console.log(t, c)
            if (
                (t === '(' && c === ')') ||
                (t === '{' && c === '}') ||
                (t === '[' && c === ']')
            ) {
                console.log(1)
                stack.pop()
            }
            else {
                return false
            }
        }
    }
    return stack.length === 0
};
  • 越靠后的左括號沪摄,對應的右括號越靠前
  • 左括號入棧,右括號出棧昙沦,椬镣伲空了就是合法的

場景三 函數(shù)調用堆棧

function greeting() {
    sayHi();
}
function sayHi() {
    retrun "Hi";
}
greeting()
  • 最后調用的函數(shù),最先執(zhí)行完
  • JS解釋器使用棧來控制函數(shù)的調用順序

隊列

  • 一個先進先出的數(shù)據(jù)結構
  • JavaScript中沒有隊列盾饮,但可以用Array實現(xiàn)隊列操作
// queue
const queue = [];
queue.push(1);
queue.push(2);
const item1 = queue.shift();
const item2 = queue.shift(); // 2 先進后出

什么場景用隊列

  • 需要先進先出的場景
  • 食堂打飯

場景一 JS異步中的任務隊列

  • JS是單線程采桃,無法同時處理異步中的并發(fā)任務
  • 使用任務隊列先后處理異步任務

場景三 計算最近請求次數(shù)

  • 有新請求就入隊,3000ms前發(fā)出的請求出隊
  • 隊列的長度就是最近的請求次數(shù)
var RecentCounter = function() {
    this.q = []
}

RecentCounter.prototype.ping = function(t) {
    this.q.push(t)
    while(this.q[0] < t - 3000) {
        this.q.shift()  
    }
    retrun this.q.length
}

JS異步中的任務隊列

setTimeout(() => console.log(1), 0)
setTimeout(2) // 先打印

鏈表

  • 多個元素組成的列表
  • 元素的存儲不連續(xù)丘损,用next指針連在一起
鏈表

數(shù)組 vs 鏈表

  • 數(shù)組:增刪非首尾元素時往往需要移動元素
  • 鏈表:增刪非首尾元素普办,不需要移動元素,只需要更改next指向即可

JS中的鏈表

  • Javascript中沒有鏈表
  • 可以用Object模擬鏈表
const a = {val: 'a'}
const b = {val: 'b'}
const c = {val: 'c'}
const d = {val: 'd'}

a.next = b
b.next = c
c.next = d
// 一個嵌套的object

// 遍歷鏈表
let p = a
while(p) {
    console.log(p.val)
    p = p.next // 修改指針
}

// 插入
const e = {val: 'e'}
c.next = e
e.next = d

// 刪除
c.next = d

反轉鏈表

  • 反轉兩個節(jié)點:將n+1的next指向n
  • 反轉多個節(jié)點:雙指針遍歷鏈表徘钥,重復上述操作
// 206.反轉鏈表
var reverseList = function(head) {
    let p1
    let p2
    while(p1) {
        const temp = p1.next
        p1.next = p2
        p2 = p1
        p1 = temp
    }
    return p2
}

兩數(shù)相加

  • 小學數(shù)學百位數(shù)相加衔蹲,個、十百
  • 遍歷鏈表

解題步驟:

  1. 新建一個空鏈表
  2. 遍歷被相加的兩個鏈表呈础,模擬相加操作舆驶,將個位數(shù)追加到新鏈表,將十位數(shù)留到下一位去相加
// 2.兩數(shù)相加
var addTwoNumber = function() {
    const l3 = new ListNode(0)
    let p1 = l1
    let p2 = l2
    let p3= l3
    let carry = 0
    while(p1 || p2) {
        const v1 = p1 ? p1.val : 0
        const v2 = p2 ? p2.val : 0
        const val = v1 + v2 + carry
        carry = Math.floor(val/10)
        p3.next = new ListNode(val % 10)
        
        if (p1) p1= p1.next
        if (p2) p2 = p2.next
        p3 = p3.next
    }
    if (carry) { // carry存在而钞,就進一
        p3.next = new ListNode(carry)
    }
    return l3.next
}

刪除鏈表中的重復元素

  • 因為鏈表是有序的沙廉,所以重復元素一定相鄰
  • 遍歷鏈表,如果發(fā)現(xiàn)當前元素和下一個元素值相同臼节,就刪除下一個元素值
  • 遍歷結束后撬陵,返回原鏈表頭部
// 83.刪除排序鏈表中的重復元素
var deleteDuplicates = function(head) {
    let p = head
    while(p && p.next) {
        if (p.val === p.next.val) {
            p.next = p.next.next
        }
        else {
            p = p.next
        }
    }
    return head
}

環(huán)形鏈表

  • 兩個人在圓形操場起點同時起跑,速度快的一定會超過速度慢的人一圈
  • 用一快一慢兩指針遍歷鏈表网缝,如果指針能夠相逢巨税,那么鏈表就有圈

解題步驟:

  • 用一快一慢兩個指針遍歷鏈表,如果指針能夠相逢粉臊,就返回true
  • 遍歷結束后草添,還沒相逢就返回false
// 141.環(huán)形鏈表
var hasCycle = function () {
    let p1 = head
    let p2 = head
    while (p1 && p2 && p2.next) {
        p1 = p1.next
        p2 = p2.next.next
        if (p1 === p2) {
            return true
        }
    }
    return false
}

前端與鏈表:JS中的原型鏈

= 原型鏈的本質是鏈表

  • 原型鏈上的節(jié)點是各種原型對象,比如Function.prototype扼仲、Object.prototype...
  • 原型鏈通過proto屬性連接各種原型對象
const obj = {}
const func = () => {}
const arr = []

// obj.__proto__ === Object.prototype
// Object.__proto__ === null

// func.__proto__ === Function.prototype
// func.__proto__.__proto__ === Object.prototype

// arr.__proto__ === Array.prototype
// arr.__proto__.__proto__ === Object.prototype

知識點

  • 如果A沿著原型鏈能找到B.prototype(原型對象)远寸,那么A instanceof B為 true
    const obj = {}
    // obj.__proto__ === Object.prototype
    obj instanceof Object // true
  • 如果在A對象上沒有找到x屬性促王,那么會沿著原型鏈找x屬性
    const obj = {}
    // obj.x undefined
    Object.prototype.x = 'x'
    obj.x === 'x'

面試題一

instanceof 原理,并用代碼實現(xiàn)

  • 知識點:如果A沿著原型鏈能找到B.prototype那么A instanceof B為true
  • 解法:遍歷A原型鏈而晒,如果找到B.prototype蝇狼,返回true,否則返回false
const instanceOf = (a, b) => {
    let p = a
    while(p) {
        if (p === b.prototype) {
            return true
        }
        p = p.__proto__
    }
    retrun false
}

面試題二

var foo = {}, F = function() {}
Object.prototype.a = 'value a'
Function.prototype.b = 'value b'

console.log(foo.a) // 'value a'
console.log(foo.b) // undefined

console.log(F.a) // 'value a'
console.log(F.b) // 'value b'

使用鏈表指針獲取JSON的節(jié)點值

const json = {
    a: {
        b: {
            c: 1
        }
    },
    d: {
        e: 2
    }
}
const path = ['a', 'b', 'c']

let p = jsop
path.forEach(k => {
    p = p[k]
})

小結

  • 鏈表里的元素存儲不是連續(xù)的倡怎,之間通過next連接
  • JS中的鏈表用Object模擬
  • 鏈表常用操作
  • JS中的原型鏈也是一個鏈表

集合

  • 是一種無序且唯一的數(shù)據(jù)結構
  • ES6中有集合迅耘,名為Set
  • 集合常用操作:去重、判斷某元素是否在集合中监署、求交集
// 去重
const arr = [1,1,2,2]
const arr2 = [...new Set(arr)] // [1,2]

// 判斷元素是否在集合中
const set = new Set(arr)
const has = set.has(1)

// 求交集
const set2 = new Set([2,3])
const set3 = new Set([...set].filter(item => set2.has(item)))

兩個數(shù)組的交集

// 349.兩個數(shù)組的交集
const set1 = new Set(nums1)
const set2 = new Set(nums2)

return [...set1].filter(i => set2.has(i))

使用ES6中的Set

  • 使用Set對象:new颤专、add、delete钠乏、has栖秕、size
  • 迭代Set:多種迭代方法、Set與Array互轉晓避、求交集/差集
// add
let mySet = new Set() // 實例化set對象
mySet.add(1)
mySet.add(5)
mySet.add(5) // [1,5]
mySet.add('some text')
let o = {a: 1, b: 2}
mySet.add(o)
mySet.add({a: 1, b: 2}) // 兩個對象內存中位置不一樣簇捍,都被加進來了

// has
const has1 = mySet.has(1) // true
const has2 = mySet.has(o) // true

// delete
mySet.delete(5) // 5被刪除

// 遍歷
for(let i of mySet) // console.log(i)
for(let i of mySet.keys()) // console.log(i)
for(let i of mySet.values()) // console.log(i)
for(let [key, value] of mySet.entries()) // console.log(key, value)

// set -> array
const myArr1 = [...mySet]
const myArr2 = Array.from(mySet)

// array -> set
const mySet1 = new Set([1,2,3,4])

// 交集
const intersection = new Set([...mySet1].filter(i => mySet2.has(i)))

// 差集
const difference = new Set([...mySet1].filter(i => !mySet2.has(i)))

字典

  • 與集合類似,字典也是一種存儲唯一值的數(shù)據(jù)結構俏拱,但它是易鍵值對的形式來存儲
  • ES6中有字典暑塑,名為Map
  • 字典常用操作:鍵值對的增刪改查
const m = new Map()

// 增
m.set('a', 'aa')
m.set('b', 'bb')

m.get('a') // 'aa'

// 刪
m.delete('b') 
m.get('b') // undefined
m.clear()
m.get('a') // undefined

// 改
m.set('a', 'aa')
m.set('a', 'aaa')
m.get('a') // 'aaa'

// 查
m.get('a') // 'aaa'

兩個數(shù)組的交集

思路:

  • 求nums1和nums2都有的值
  • 用字典建立一個映射關系,記錄nums1里的值
  • 遍歷nums2找出nums1里的值

解題步驟:

  • 新建一個字典锅必,遍歷nums1事格,填充字典
  • 遍歷nums2,遇到字典里的值就從字典中刪除
// 349.兩個數(shù)組的交集
const map = new Map()

nums1.forEach(n => map.set(n, true))

const result = []
nums2.forEach(n => {
    if(map.get(n)) {
        result.push(n)
        map.delete(n)
    }
})
return result

有效的括號

// 20.有效的括號
var isValid = function(s) {
    if (s.length % 2 === 1) {return false}
    const stack = []
    const map = new Map()
    map.set('(', ')')
    map.set('{', '}')
    map.set('[', ']')

    for(let i = 0; i < s.length; i++) {
        const c = s[i]
        if (map.get(c)) {
            stack.push(c)
        }
        else {
            const t = stack[stack.length - 1]
            if (map.get(t) === c) {
                stack.pop()
            }
            else {
                return false
            }
        }
    }
    return stack.length === 0
};

兩數(shù)之和

// 1.兩數(shù)之和
var twoSum = function(nums, target) {
    const map = new Map()
    for (let i = 0; i < nums.length; i++) {
        const current = nums[i]
        const need = target - current
        if (map.has(need)) {
            return [map.get(need), i]
        }
        else {
            map.set(current, i)
        }
    }
};

無重復字符的最長子串

思路:

  • 找出所有的不包含重復字符的子串
  • 找出長度最大的那個子串搞隐,返回其長度
    解題步驟:
  • 用雙指針維護一個滑動窗口驹愚,用來剪切子串
  • 不斷移動右指針,遇到重復字符劣纲,就把左指針移動到重復字符的下一位
  • 過程中逢捺,記錄所有窗口長度,并返回最大值
// 3.無重復字符的最長子串
var lengthOfLongestSubstring = function(s) {
    let l = 0
    let res = 0
    const map = new Map()
    for (let r = 0; r < s.length; r++) {
        if (map.has(s[r]) && map.get(s[r]) >= l) {
            l = map.get(s[r]) + 1
        }
        res = Math.max(res, r - l + 1)
        map.set(s[r], r)
    }
    return res
};

最小覆蓋子串

思路:

  • 先找出所有的包含T的子串
  • 找出長度最小的那個子串味廊,返回即可
    解題步驟:
  • 用雙指針維護一個滑動窗口
  • 移動右指針蒸甜,找到包含T的子串棠耕,移動左指針余佛,盡量減少包含T的子串的長度
// 76.最小覆蓋子串 
var minWindow = function(s, t) {
    let l = 0
    let r = 0
    const need = new Map()
    
    for(let c of t) {
        need.set(c, need.has(c) ? need.get(c) + 1 : 1)
    }
    let needType = need.size
    let res = ''
    while (r < s.length) {
        const c = s[r]
        if (need.has(c)) {
            need.set(c, need.get(c) - 1)
            if (need.get(c) === 0) needType -= 1
        }
        while (needType === 0) {
            const newRes = s.substring(l, r + 1)
            if (!res || newRes.length < res.length) res = newRes
            const c2 = s[l]
            if (need.has(c2)) {
                need.set(c2, need.get(c2) + 1)
                if (need.get(c2) === 1) needType += 1
            }
            l += 1
        }
        r += 1
    }
    return res
};

小結

  • 與集合類似,字典也是一種存儲唯一值的數(shù)據(jù)結構窍荧,但它是以鍵值對的形式來存儲
  • ES6中有字典辉巡,名為Map

二叉樹

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市蕊退,隨后出現(xiàn)的幾起案子郊楣,更是在濱河造成了極大的恐慌憔恳,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,188評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件净蚤,死亡現(xiàn)場離奇詭異钥组,居然都是意外死亡,警方通過查閱死者的電腦和手機今瀑,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,464評論 3 395
  • 文/潘曉璐 我一進店門程梦,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人橘荠,你說我怎么就攤上這事屿附。” “怎么了哥童?”我有些...
    開封第一講書人閱讀 165,562評論 0 356
  • 文/不壞的土叔 我叫張陵挺份,是天一觀的道長。 經常有香客問我贮懈,道長匀泊,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,893評論 1 295
  • 正文 為了忘掉前任朵你,我火速辦了婚禮探赫,結果婚禮上,老公的妹妹穿的比我還像新娘撬呢。我一直安慰自己伦吠,他們只是感情好,可當我...
    茶點故事閱讀 67,917評論 6 392
  • 文/花漫 我一把揭開白布魂拦。 她就那樣靜靜地躺著毛仪,像睡著了一般。 火紅的嫁衣襯著肌膚如雪芯勘。 梳的紋絲不亂的頭發(fā)上箱靴,一...
    開封第一講書人閱讀 51,708評論 1 305
  • 那天,我揣著相機與錄音荷愕,去河邊找鬼衡怀。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的敛纲。 我是一名探鬼主播彤避,決...
    沈念sama閱讀 40,430評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼怖现!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 39,342評論 0 276
  • 序言:老撾萬榮一對情侶失蹤屈嗤,失蹤者是張志新(化名)和其女友劉穎潘拨,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體饶号,經...
    沈念sama閱讀 45,801評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡铁追,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,976評論 3 337
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了茫船。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片脂信。...
    茶點故事閱讀 40,115評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖透硝,靈堂內的尸體忽然破棺而出狰闪,到底是詐尸還是另有隱情,我是刑警寧澤濒生,帶...
    沈念sama閱讀 35,804評論 5 346
  • 正文 年R本政府宣布埋泵,位于F島的核電站,受9級特大地震影響罪治,放射性物質發(fā)生泄漏丽声。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,458評論 3 331
  • 文/蒙蒙 一觉义、第九天 我趴在偏房一處隱蔽的房頂上張望雁社。 院中可真熱鬧,春花似錦晒骇、人聲如沸霉撵。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,008評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽徒坡。三九已至,卻和暖如春瘤缩,著一層夾襖步出監(jiān)牢的瞬間喇完,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,135評論 1 272
  • 我被黑心中介騙來泰國打工剥啤, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留锦溪,地道東北人。 一個月前我還...
    沈念sama閱讀 48,365評論 3 373
  • 正文 我出身青樓府怯,卻偏偏與公主長得像刻诊,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子富腊,可洞房花燭夜當晚...
    茶點故事閱讀 45,055評論 2 355

推薦閱讀更多精彩內容