算法 | Leetcode 劍指offer Javascript 刷題

Leetcode終于上線劍指offer的題目了?? 記下刷題記錄

面試題03. 數(shù)組中重復(fù)的數(shù)字

簡單題
空間復(fù)雜度:O(N)
時間復(fù)雜度:O(N)

var findRepeatNumber = function(nums) {
    const arr = new Array(nums.length)
    for(let n of nums) {
        if(arr[n]) {
            return n
        }
        arr[n] = true
    }
    return false
};

面試題04. 二維數(shù)組中的查找

簡單題
空間復(fù)雜度:O(1) 輔助數(shù)據(jù)也就是坐標(biāo)i爹殊,j而已。
時間復(fù)雜度:O(N+M) 最復(fù)雜的情況就是從上到下,從右到左。

const findNumberIn2DArray = function(matrix, target) {
    // 左到右遞增
    // 上到下遞增
    // 從左上角開始或衡,item > target j--, item < target i++
    const n = matrix.length
    if(n === 0) return false
    const m = matrix[0].length
    if(m === 0) return false
    if(target < matrix[0][0]) return false
    if(target > matrix[n-1][m-1]) return false
    let i = 0, j = m-1
    while(i<n && i>=0 && j<m && j>=0) {
        if(matrix[i][j] === target) {
            return true
        }
        if(matrix[i][j] > target) {
            j--
            continue
        }
        if(matrix[i][j] < target) {
            i++
            continue
        }
    }
    return false
};

面試題05. 替換空格

簡單題
時間復(fù)雜度:O(N)
空間復(fù)雜度:O(N)

// 先復(fù)習(xí)下字符串操作API吧
// replace實(shí)現(xiàn)
const replaceSpace = function(s) {
    return s.replace(/ /g, '%20')
};
// split + join
const replaceSpace = function(s) {
    return s.split(' ').join('%20')
};
// 數(shù)組 + join做法
const replaceSpace = function(s) {
    const arr = []
    for(const c of s) {
        c === ' ' ? arr.push('%20') : arr.push(c)
    }
    return arr.join('')
};

面試題06. 從尾到頭打印鏈表

簡單題
遞歸法和循環(huán)壓棧逆序輸出法速兔,都是O(N)

const reversePrint = function(head) {
    // 用遞歸法
    const res = []
    const reverse = (node) => {
        if(!node) {
            return
        } else {
            reverse(node.next)
            res.push(node.val)
        }
    }
    reverse(head)
    return res
};
const reversePrint = function(head) {
    // 用循環(huán)法
    const res = []
    while(head) {
        res.push(head.val)
        head = head.next
    }
    return res.reverse()
};

面試題07. 重建二叉樹

中等題
時間復(fù)雜度O(N)
空間復(fù)雜度O(N) 函數(shù)調(diào)用椦憔海空間浪費(fèi)秒咐。

var buildTree = function(preorder, inorder) {
    // 重建二叉樹
    // 前序遍歷:先中后左最后右,因此第一個是中節(jié)點(diǎn)
    // 中序遍歷:先左后中最后右盈简,因此第一個是左樹
    const n = preorder.length
    if(n === 0) return null
    const build = (ps, pe, is, ie) => {
        const mid = preorder[ps]
        const midNode = new TreeNode(mid)
        if(ps === pe || is === ie) {
            return midNode
        }
        // im是中節(jié)點(diǎn)的inorder坐標(biāo)凑耻,它同時代表:左子樹的長度=im-is太示,右子樹的長度是ie-im
        let im
        for(let i=is;i<=ie;i++) {
            if(inorder[i] === mid) {
                im = i
                break
            }
        }
        let leftLen = im-is, rightLen = ie-im
        // 1,1,0,0
        midNode.left = leftLen > 0 ? build(ps+1, ps+1+leftLen-1, is, is+leftLen-1) : null
        midNode.right = rightLen > 0 ? build(ps+1+leftLen, ps+1+leftLen+rightLen-1, im+1, im+1+rightLen-1) : null
        return midNode
    }
    return build(0, n-1, 0, n-1)
};

翻了一下過去的提交,發(fā)現(xiàn)自己又踩坑了香浩,居然沒記住這個結(jié)果:就是構(gòu)建樹的過程本來就是一個先序遍歷类缤,所以每次只要考慮inorderIndex就行了。??

var buildTree = function(preorder, inorder) {
    /**
     * 其實(shí) 這個build的過程本身就是一個先序遍歷邻吭,因此只需要設(shè)置一個全局自增的Index就能走先序遍歷的過程了
     * 所以 目前需要的下標(biāo)就是中序遍歷的left和right部分
     */
    let index = 0
    const inorderMap = new Map()
    for(let i=0;i<inorder.length;i++) {
        inorderMap.set(inorder[i], i)
    }
    const recBuild = (inLeft, inRight) => {
        if(inLeft === inRight) {
            return null
        }
        const rootVal = preorder[index]
        const root = new TreeNode(rootVal)
        const inorderIndex = inorderMap.get(rootVal)
        index++
        root.left = recBuild(inLeft, inorderIndex)
        root.right = recBuild(inorderIndex+1, inRight)
        return root
    }
    return recBuild(0, inorder.length)
};

面試題09. 用兩個棧實(shí)現(xiàn)隊(duì)列

簡單題

var CQueue = function() {
    this.s1 = []
    this.s2 = []
};

/** 
 * @param {number} value
 * @return {void}
 */
CQueue.prototype.appendTail = function(value) {
    this.s1.push(value)
};

/**
 * @return {number}
 */
CQueue.prototype.deleteHead = function() {
    if(this.s2.length) return this.s2.pop()
    if(!this.s1.length) return -1
    while(this.s1.length) {
        this.s2.push(this.s1.pop())
    }
    return this.s2.pop()
};

面試題10- I. 斐波那契數(shù)列

簡單題

var fib = function(n) {
    const cache = {
        0: 0n,
        1: 1n
    };
    return Fibonacci(n) % 1000000007n;

    /**
     * @param {number} n
     * @return {number}
     */
    function Fibonacci(n) {
        if (cache[n] !== undefined) {
            return cache[n];
        }

        cache[n] = Fibonacci(n - 1) + Fibonacci(n - 2);
        return cache[n];
    }
};

面試題10- II. 青蛙跳臺階問題

同上

面試題11. 旋轉(zhuǎn)數(shù)組的最小數(shù)字

簡單題
簡單題的簡單思路

var minArray = function(numbers) {
    let i
    for(i=numbers.length-1;i>0;i--) {
        if(numbers[i]<numbers[i-1]) {
            break
        }
    }
    return numbers[i]
};

但是這道題在leetcode原本的題庫是困難題餐弱,因?yàn)樗梢曰影俪觥?/p>

var minArray = function(numbers) {
    let left = 0, right = numbers.length-1
    while(left < right) {
        let mid = Math.floor((left+right)/2)
        // mid一定在數(shù)組的右邊
        if(numbers[mid]>numbers[right]) {
            left = mid+1
        // mid一定在數(shù)組的左邊
        } else if (numbers[mid]<numbers[right]) {
            right = mid
        } else {
            right = right-1
        }
    }
    return numbers[left]
};

面試題12. 矩陣中的路徑

中等題
極速地寫了一個,沒有優(yōu)化過囱晴,但是思路很明顯膏蚓,就是dfs。

const exist = function(board, word) {
    const n = board.length
    const m = board[0].length
    const len = word.length
    // 如果字母a畸写,上下左右都有下一個字母驮瞧,那就得全部都試一試?yán)玻幸粋€達(dá)成和word一樣就立馬推出枯芬。
    // travel 目前的位置i,j和目前走到word第幾個w
    const travel = (i,j,w, visit) => {
        if(w===len-1) {
            return true
        }
        // 看看左邊剧董?
        if(j-1>=0 && board[i][j-1]===word[w+1]) {
            let pos = `${i}-${j-1}`
            if(!visit[pos]) {
                if(travel(i,j-1,w+1,{[pos]:true,...visit})) return true
            }
        }
        // 看看右邊?
        if(j+1<m && board[i][j+1]===word[w+1]) {
            let pos = `${i}-${j+1}`
            if(!visit[pos]) {
                if(travel(i,j+1,w+1,{[pos]:true,...visit})) return true
            }
        }
        // 看看上邊破停?
        if(i-1>=0 && board[i-1][j]===word[w+1]) {
            let pos = `${i-1}-${j}`
            if(!visit[pos]) {
                if(travel(i-1,j,w+1,{[pos]:true,...visit})) return true
            }
        }
        // 看看下邊?
        if(i+1<n && board[i+1][j]===word[w+1]) {
            let pos = `${i+1}-${j}`
            if(!visit[pos]) {
                if(travel(i+1,j,w+1,{[pos]:true,...visit})) return true
            }
        }
        return false
    }
    for(let i=0;i<n;i++) {
        for(let j=0;j<m;j++) {
            if(board[i][j]===word[0]) {
                let pos = `${i}-${j}`
                if(travel(i,j,0, {[pos]: true})) return true
            }
        }
    }
    return false
};

面試題13. 機(jī)器人的運(yùn)動范圍

中等題
Leetcode題解里的BFS尉剩,比我使用DFS復(fù)雜度低了真慢。
空間復(fù)雜度O(N),時間復(fù)雜度O(N)

var movingCount = function(m, n, k) {
    let res = 0;
    const directions = [
        [1, 0],
        [0, 1]
    ];
    const queue = [[0, 0]];
    const visited = {
        "0-0": true
    }; // 標(biāo)記 (x,y) 是否被訪問過

    while (queue.length) {
        const [x, y] = queue.shift();
        //  (x, y) 的數(shù)位之和不符合要求
        // 題目要求節(jié)點(diǎn)每次只能走1格理茎,所以無法從當(dāng)前坐標(biāo)繼續(xù)出發(fā)
        if (bitSum(x) + bitSum(y) > k) {
            continue;
        }
        ++res;

        for (const direction of directions) {
            const newx = direction[0] + x;
            const newy = direction[1] + y;
            if (
                !visited[`${newx}-${newy}`] &&
                newx >= 0 &&
                newy >= 0 &&
                newx < m &&
                newy < n
            ) {
                queue.push([newx, newy]);
                visited[`${newx}-${newy}`] = true;
            }
        }
    }

    return res;
};

function bitSum (n) {
    let res = 0
    while(n) {
        res += n%10
        n = Math.floor(n/10)
    }
    return res
}

面試題16. 數(shù)值的整數(shù)次方

中等題
先寫個普通循環(huán)版本黑界,果不其然超時了。

var myPow = function(x, n) {
    let res = 1
    if(n >= 0) {
        while(n--) {
            res *= x
        }
    } else {
        while( (n++)!=0 ) {
            res /= x
        }
    }
    return res
};

想了下可以用遞歸來實(shí)現(xiàn)二分思想皂林。
如果指數(shù)是偶數(shù)的情況下:Pow(base,exponent) = Pow(base,exponent/2)*Pow(base,exponent)
指數(shù)是奇數(shù)的情況下:Pow(base,exponent) = base * Pow(base,exponent/2) * Pow(base,exponent/2)
再區(qū)分下奇數(shù)偶數(shù)的情況朗鸠,就能成了:

var myPow = function(x, n) {
    const pow = (x,n) => {
        if(n===0) {
            return 1
        }
        if(n===1) {
            return x
        }
        const exp = Math.floor(n/2)
        return n % 2 === 1 ? x*pow(x,exp)*pow(x,exp):pow(x,exp)*pow(x,exp)
    }
    return n >= 0 ? pow(x,n) : 1/pow(x,-n)
};

突然想到之前學(xué)計(jì)算機(jī)安全導(dǎo)論的時候?qū)W過一個叫快速冪的算法,這里用來試試础倍。

var myPow = function(x, n) {
    const pow = (x,n) => {
        let ans = 1
        while(n) {
            if(n & 1) {
                ans *= x
            }
            x *= x
            n = Math.floor(n/2)
        }
        return ans
    }
    return n >= 0 ? pow(x,n) : 1/pow(x,-n)
};

快速冪的思想是:


摘自百度百科

面試題17. 打印從1到最大的n位數(shù)

簡單題
直接調(diào)用API辣烛占,其實(shí)也可以用上面的快速冪計(jì)算10的n次冪

var printNumbers = function(n) {
    const res = []
    const max = 10 ** n - 1
    for(let i=1;i<=max;i++) {
        res.push(i)
    }
    return res
};

面試題18. 刪除鏈表的節(jié)點(diǎn)

var deleteNode = function(head, val) {
    if(head.val === val) {
        head = head.next
        return head
    }
    let node = head
    while(node.next) {
        if(node.next.val === val) {
            node.next = node.next.next
            break
        }
        node = node.next
    }
    return head
};

面試題19. 正則表達(dá)式匹配

簡單題
回溯法

var isMatch = function(s, p) {
    const _isMatch = (i, j) => {
        if(i === s.length && j===p.length) {
            return true
        }
        if(i !== s.length && j === p.length) {
            return false
        }
        if(p[j+1] === '*') {
            if(s[i]===p[j] || (p[j]==='.' && i !== s.length)) {
                return _isMatch(i+1,j) || _isMatch(i,j+2)
            } else {
                return _isMatch(i,j+2)
            }
        }
        if (s[i]===p[j]  || (p[j]==='.' && i !== s.length)){
            return _isMatch(i+1, j+1)
        } else {
            return false
        }
    }
    return _isMatch(0,0)
};

322. 零錢兌換

回溯法剪枝依然超時,所以應(yīng)該用動態(tài)規(guī)劃

/**
 * @param {number[]} coins
 * @param {number} amount
 * @return {number}
 */
var coinChange = function(coins, amount) {
    /**
     * 最優(yōu)化問題
     * 可以用回溯法和動態(tài)規(guī)劃法解決
     * 優(yōu)化目標(biāo):硬幣數(shù)量最少
     * 先用回溯法莽一波
     * 動態(tài)規(guī)劃也可以減少解空間
     */
    /**
     * rest: 剩余金額
     * count: 使用硬幣數(shù)
     */
    let res = Infinity
    const sub = (rest, count) => {
        // 剪枝
        if(count > res) {
            return
        }

        if(rest === 0) {
            res = Math.min(count, res)
            return
        } else if(rest < Math.min(coins)) {
            return
        }

        // 剩余情況
        for(const coin of coins) {
            sub(rest-coin, count+1)
        }
    }
    sub(amount, 0)
    return res === Infinity ? -1 : res
};
var coinChange = function(coins, amount) {
    /**
     * 最優(yōu)化問題
     * 可以用回溯法和動態(tài)規(guī)劃法解決
     * 優(yōu)化目標(biāo):硬幣數(shù)量最少
     * 先用回溯法莽一波
     * 動態(tài)規(guī)劃也可以減少解空間
     */
    // dp是此金額花費(fèi)的硬幣數(shù)
    const dp = Array(amount+1).fill(Infinity)
    dp[0] = 0
    for(let i=1;i<=amount;i++) {
        for(let coin of coins) {
            if(i-coin>=0) {
                dp[i] = Math.min(dp[i], dp[i-coin]+1)
            }
        }
    }
    return dp[amount] === Infinity ? -1 : dp[amount]
};

面試題37. 序列化二叉樹

var serialize = function(root) {
    // 用層次遍歷來讀入
    if(!root) return '[]'
    let res = '['
    const queue = [root]
    while(queue.length) {
        const t = queue.shift()
        if(t === null) {
            res+='null,'
        }else {
            res+=`${t.val},`
        }
        // null節(jié)點(diǎn)不會再加入它們的子節(jié)點(diǎn)而導(dǎo)致出錯
        if(!t) continue
        queue.push(t.left)
        queue.push(t.right)
    }
    return res.slice(0, res.length-1) + ']'
};

/**
 * Decodes your encoded data to tree.
 *
 * @param {string} data
 * @return {TreeNode}
 */
var deserialize = function(data) {
    if(data === '[]' || !data) return null
    data = data.slice(1,data.length-1).split(',')
    let index = 0
    const head = getTreeNode(data[index++])
    const queue = [head]
    while(queue.length) {
        let node = queue.shift()
        node.left = getTreeNode(data[index++])
        node.right = getTreeNode(data[index++])
        node.left && queue.push(node.left)
        node.right && queue.push(node.right)
    }
    return head
};

const getTreeNode = (value) => {
    if(value === 'null') return null
    else return new TreeNode(Number(value))
}

/**
 * Your functions will be called as such:
 * deserialize(serialize(root));
 */

面試題38. 字符串的排列

/**
 * @param {string} s
 * @return {string[]}
 */
var permutation = function(s) {
    if (!s.length) {
        return [];
    }
    const result = [];
    _permutation(s.split(""), 0, result);
    return result
};

/**
 * 回溯遍歷得到所有的結(jié)果
 *
 * @param {string[]} strs
 * @param {number} start
 * @param {string[]} result
 */
function _permutation(strs, start, result) {
    if (start === strs.length) {
        result.push(strs.join(""));
        return;
    }
    const map = {}
    for (let i = start; i < strs.length; ++i) {
        if (map[strs[i]]) {
            // 進(jìn)行剪枝
            continue;
        }
        map[strs[i]] = true;
        [strs[i], strs[start]] = [strs[start], strs[i]];
        _permutation(strs, start + 1, result);
        [strs[i], strs[start]] = [strs[start], strs[i]];
    }
}

面試題39. 數(shù)組中出現(xiàn)次數(shù)超過一半的數(shù)字

var majorityElement = function(nums) {
    /**
     * 摩爾投票法
     */
    let count = 1
    let res = nums[0]
    for(let n of nums) {
        if(n !== res) {
            count--
            if(count === 0) {
                res = n
                count = 1
            }
        } else {
            count++
        }
    }
    return res
};
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末沟启,一起剝皮案震驚了整個濱河市忆家,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌德迹,老刑警劉巖芽卿,帶你破解...
    沈念sama閱讀 219,188評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異胳搞,居然都是意外死亡卸例,警方通過查閱死者的電腦和手機(jī)称杨,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,464評論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來筷转,“玉大人姑原,你說我怎么就攤上這事〉┳埃” “怎么了页衙?”我有些...
    開封第一講書人閱讀 165,562評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長阴绢。 經(jīng)常有香客問我店乐,道長,這世上最難降的妖魔是什么呻袭? 我笑而不...
    開封第一講書人閱讀 58,893評論 1 295
  • 正文 為了忘掉前任眨八,我火速辦了婚禮,結(jié)果婚禮上左电,老公的妹妹穿的比我還像新娘廉侧。我一直安慰自己,他們只是感情好篓足,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,917評論 6 392
  • 文/花漫 我一把揭開白布段誊。 她就那樣靜靜地躺著,像睡著了一般栈拖。 火紅的嫁衣襯著肌膚如雪连舍。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,708評論 1 305
  • 那天涩哟,我揣著相機(jī)與錄音索赏,去河邊找鬼。 笑死贴彼,一個胖子當(dāng)著我的面吹牛潜腻,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播器仗,決...
    沈念sama閱讀 40,430評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼融涣,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了青灼?” 一聲冷哼從身側(cè)響起暴心,我...
    開封第一講書人閱讀 39,342評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎杂拨,沒想到半個月后专普,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,801評論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡弹沽,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,976評論 3 337
  • 正文 我和宋清朗相戀三年檀夹,在試婚紗的時候發(fā)現(xiàn)自己被綠了筋粗。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,115評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡炸渡,死狀恐怖娜亿,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情蚌堵,我是刑警寧澤买决,帶...
    沈念sama閱讀 35,804評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站吼畏,受9級特大地震影響督赤,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜泻蚊,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,458評論 3 331
  • 文/蒙蒙 一躲舌、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧性雄,春花似錦没卸、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,008評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至迁筛,卻和暖如春病蛉,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背瑰煎。 一陣腳步聲響...
    開封第一講書人閱讀 33,135評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留俗孝,地道東北人酒甸。 一個月前我還...
    沈念sama閱讀 48,365評論 3 373
  • 正文 我出身青樓,卻偏偏與公主長得像赋铝,于是被迫代替她去往敵國和親插勤。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,055評論 2 355

推薦閱讀更多精彩內(nèi)容

  • 總結(jié) 想清楚再編碼 分析方法:舉例子革骨、畫圖 第1節(jié):畫圖分析方法 對于二叉樹农尖、二維數(shù)組、鏈表等問題良哲,都可以采用畫圖...
    M_巴拉巴拉閱讀 1,211評論 0 7
  • 數(shù)組 記錄《劍指offer》中所有關(guān)于數(shù)組的題目盛卡,以及LeetCode中的相似題目 相關(guān)題目列表 說明 由于簡書...
    wenmingxing閱讀 1,518評論 1 12
  • 前言 這是用來記錄在刷LeetCode時遇到的一些問題的技巧,因只記錄一些技巧或優(yōu)化方案故不一定全部記錄筑凫,自認(rèn)為的...
    Cesarean閱讀 831評論 0 0
  • 高級鉗工應(yīng)知鑒定題庫(858題) ***單選題*** 1. 000003難易程度:較難知識范圍:相關(guān)4 01答案:...
    開源時代閱讀 5,783評論 1 9
  • 大美無言滑沧,能動萬言而按兵不動并村,其氣如虹,其場如戰(zhàn) 我與海洋別離很久了滓技,我不會想念它哩牍,它始終在我的心里 故鄉(xiāng)漸行漸遠(yuǎn)...
    有讀閱讀 618評論 0 0