LeetCode 算法題刷題心得(JavaScript)

花了十幾天频轿,把《算法》看了一遍然后重新 AC 了一遍 LeetCode 的題逆日,收獲頗豐余黎。這次好好記錄下心得。
我把所有做題的代碼都放在 github 上以供參考鸟雏。
項目地址:https://github.com/violetjack/LeetCodeACByJS
題目地址:https://leetcode.com/problemset/top-interview-questions/

說來慚愧享郊,之前寫的《LeetCode 邏輯題分享》其實自己動手做的比較少,都是看解決方案孝鹊。更加關(guān)鍵的是我沒有系統(tǒng)地去學習過算法(自學的編程)炊琉。所以導致以下幾個問題:

  • 看題不懂方法論,理解他人方案困難又活。
  • 解題方法通過看別人的方案去歸納苔咪,照著抄。(其實都是有系統(tǒng)的算法寫法的)
  • 很多題目看了答案只是知其然而不知其所以然柳骄。
  • 很多答案(討論區(qū)的方案)是有錯誤的团赏,卻把它當正確答案來發(fā)。

之后耐薯,我看了《算法(第4版)》一書舔清,重新去做并且試著去 AC 題目,問題又是一堆堆的曲初。所以這次比第一次刷題時間要久不少体谒。

各類題的解決方案

話不多說,系統(tǒng)整理下解題的一些算法和解決方案

二叉樹

二叉樹大多使用遞歸的方式左右兩個元素向下遞歸臼婆。比如:

計算二叉樹最大深度

var maxDepth = function (root) {
    if (root == null) return 0
    return 1 + Math.max(maxDepth(root.left), maxDepth(root.right))
};

將二叉樹以二維數(shù)組形式表現(xiàn)

var levelOrder = function(root) {
    let ans = []
    helper(root, ans, 0)
    return ans
};

function helper(node, ans, i){
    if (node == null) return
    if (i == ans.length) ans.push([])
    ans[i].push(node.val)

    helper(node.left, ans, i + 1)
    helper(node.right, ans, i + 1)
}

都是通過遞歸方式逐層向下去查找二叉樹數(shù)據(jù)抒痒。

可能性問題

這類題一般是告訴你一組數(shù)據(jù),然后求出可能性颁褂、最小值或最大值故响。比如:

給定幾種面額的硬幣和一個總額,使用最少的硬幣湊成這個總額颁独。

var coinChange = function (coins, amount) {
    let max = amount + 1
    let dp = new Array(amount + 1)
    dp.fill(max)
    dp[0] = 0

    for (let i = 1; i < max; i++) {
        for (let j = 0; j < coins.length; j++) {
            if (coins[j] <= i) {
                dp[i] = Math.min(dp[i], dp[i - coins[j]] + 1)
            }
        }
    }
    return dp[amount] > amount ? -1 : dp[amount]
};

使用了動態(tài)規(guī)劃(DP)被去,將從 0 到目標額度所需的最小硬幣數(shù)都列出來。

求出從矩陣左上角走到右下角奖唯,且只能向右向下移動惨缆,一共有多少種可能性。

var uniquePaths = function (m, n) {
    const pos = new Array(m)
    for (let i = 0; i < m; i++) {
        pos[i] = new Array(n)
    }
    for (let i = 0; i < n; i++) {
        pos[0][i] = 1
    }
    for (let i = 0; i < m; i++) {
        pos[i][0] = 1
    }
    for (let i = 1; i < m; i++) {
        for (let j = 1; j < n; j++) {
            pos[i][j] = pos[i - 1][j] + pos[i][j - 1]
        }
    }
    return pos[m - 1][n - 1]
};

這題就是使用了動態(tài)規(guī)劃逐步列出每一格的可能性丰捷,最后返回右下角的可能性坯墨。

獲取給定數(shù)組連續(xù)元素累加最大值

var maxSubArray = function (nums) {
    let count = nums[0], maxCount = nums[0]
    for (let i = 1; i < nums.length; i++) {
        count = Math.max(count + nums[i], nums[i])
        maxCount = Math.max(maxCount, count)    
    }
    return maxCount
};

上面這題通過不斷對比最大值來保留并返回最大值。

其實病往,可能性問題使用動態(tài)規(guī)劃要比使用 DFS捣染、BFS 算法更加簡單而容易理解。(我使用 DFS 經(jīng)常報 TLE)

查找

一般遇到的查找問題停巷,如查找某個值一般會用到一下方法:

  • 排序算法(排序便于查找)
  • 二分查找
  • 索引移動查找(這個方法名自己想的耍攘,大概就這個意思~)

查找橫向和縱向都遞增的二維矩陣中的某個值

var searchMatrix = function (matrix, target) {
    if (matrix.length == 0) return false
    let row = 0, col = matrix[0].length - 1
    while (true) {
        if (matrix[row][col] > target && col > 0) {
            col--
        } else if (matrix[row][col] < target && row < matrix.length - 1) {
            row++
        } else if (matrix[row][col] == target) {
            return true
        } else {
            break
        }
    }
    return false
};

先將位置定位在右上角榕栏,通過改變位置坐標來找到目標值。使用了索引移動查找法來找到結(jié)果蕾各。

找到數(shù)組中最左邊和最右邊的某個數(shù)字所在位置

var searchRange = function (nums, target) {
    let targetIndex = binarySearch(nums, target, 0, nums.length - 1)
    if (targetIndex == -1) return [-1, -1]
    let l = targetIndex, r = targetIndex
    while(l > 0 && nums[l - 1] == target){
        l--
    }
    while(r < nums.length - 1 && nums[r + 1] == target){
        r++
    }
    return [l, r]
};

function binarySearch(arr, val, lo, hi) {
    if (hi < lo) return -1
    let mid = lo + parseInt((hi - lo) / 2)

    if (val < arr[mid]) {
        return binarySearch(arr, val, lo, mid - 1)
    } else if (val > arr[mid]) {
        return binarySearch(arr, val, mid + 1, hi)
    } else {
        return mid
    }
}

這題使用二分法來查找到某個目標數(shù)字的索引值扒磁,然后索引移動法分別向左和向右查找字符。獲取左右兩側(cè)的索引值返回式曲。

回文

所謂回文妨托,就是正著讀反著讀是一樣的。使用索引兩邊向中間移動的方式來判斷是否為回文吝羞。

找到給定字符串中某段最長的回文

var longestPalindrome = function (s) {
    let maxLength = 0, left = 0, right = 0
    for (let i = 0; i < s.length; i++) {
        let singleCharLength = getPalLenByCenterChar(s, i, i)
        let doubleCharLength = getPalLenByCenterChar(s, i, i + 1)
        let max = Math.max(singleCharLength, doubleCharLength)
        if (max > maxLength) {
            maxLength = max
            left = i - parseInt((max - 1) / 2)
            right = i + parseInt(max / 2)
        }
    }
    return s.slice(left, right + 1)
};

function getPalLenByCenterChar(s, left, right) {
    // 中間值為兩個字符兰伤,確保兩個字符相等
    if (s[left] != s[right]){
        return right - left
    }
    while (left > 0 && right < s.length - 1) {
        left--
        right++
        if (s[left] != s[right]){
            return right - left - 1
        }
    }
    return right - left + 1
}

路徑題

路徑題可以使用深度優(yōu)先(DFS)和廣度優(yōu)先(BFS)算法來做。我比較常用的是使用 DFS 來做钧排。通過遞歸將走過的路徑進行標記來不斷往前找到目標路徑敦腔。如:

通過給定單詞在二維字母數(shù)組中查找是否能使用鄰近字母組成這個單詞(212題)

let hasWord = false

var findWords = function (board, words) {
    var ans = []
    for (let word of words) {
        for (let j = 0; j < board.length; j++) {
            for (let i = 0; i < board[0].length; i++) {
                if (board[j][i] == word[0]) {
                    hasWord = false
                    DFS(word, board, 0, j, i, "")
                    if (hasWord) {
                        if (!ans.includes(word))
                            ans.push(word)
                    }
                }
            }
        }
    }
    return ans
};

function DFS(word, board, index, j, i, subStr) {
    if (word[index] == board[j][i]) {
        subStr += board[j][i]
        board[j][i] = "*"
        if (j < board.length - 1)
            DFS(word, board, index + 1, j + 1, i, subStr)
        if (j > 0)
            DFS(word, board, index + 1, j - 1, i, subStr)
        if (i < board[0].length - 1)
            DFS(word, board, index + 1, j, i + 1, subStr)
        if (i > 0)
            DFS(word, board, index + 1, j, i - 1, subStr)
        board[j][i] = word[index]
    }
    if (index >= word.length || subStr == word) {
        hasWord = true
    }
}

由于 DFS 是一條路走到黑,如果每個元素都去使用 DFS 來找會出現(xiàn)超時的情況恨溜。如果條件允許(如查找遞增數(shù)組)可以通過設(shè)置緩存來優(yōu)化 DFS 查找超時問題会烙。

獲取二維矩陣中最大相鄰遞增數(shù)組長度。

const dirs = [[0, 1], [1, 0], [0, -1], [-1, 0]]

var longestIncreasingPath = function (matrix) {
    if (matrix.length == 0) return 0
    const m = matrix.length, n = matrix[0].length
    let max = 1

    let cache = new Array(m)
    for (let i = 0; i < m; i++){
        let child = new Array(n)
        child.fill(0)
        cache[i] = child
    }

    for (let i = 0; i < m; i++) {
        for (let j = 0; j < n; j++) {
            let len = dfs(matrix, i, j, m, n, cache)
            max = Math.max(max, len)
        }
    }
    return max
}

function dfs(matrix, i, j, m, n, cache){
    if (cache[i][j] != 0) return cache[i][j]
    let max = 1
    for (let dir of dirs){
        let x = i + dir[0], y = j + dir[1]
        if(x < 0 || x >= m || y < 0 || y >= n || matrix[x][y] <= matrix[i][j]) continue;
        let len = 1 + dfs(matrix, x, y, m, n, cache)
        max = Math.max(max, len)
    }
    cache[i][j] = max
    return max
}

將已使用 DFS 查找過的長度放入緩存筒捺,如果有其他元素走 DFS 走到當前值,直接返回緩存最大值即可纸厉。

鏈表

鏈表從 JS 的角度來說就是一串對象使用指針連接的數(shù)據(jù)結(jié)構(gòu)系吭。合理使用 next 指針改變指向來完成對鏈表的一系列操作。如:

鏈表的排序:

var sortList = function (head) {
    if (head == null || head.next == null) return head

    let prev = null, slow = head, fast = head
    while (fast != null && fast.next != null) {
        prev = slow
        slow = slow.next
        fast = fast.next.next
    }

    prev.next = null;

    let l1 = sortList(head)
    let l2 = sortList(slow)

    return merge(l1, l2)
};

function merge(l1, l2) {
    let l = new ListNode(0), p = l;

    while (l1 != null && l2 != null) {
        if (l1.val < l2.val) {
            p.next = l1;
            l1 = l1.next;
        } else {
            p.next = l2;
            l2 = l2.next;
        }
        p = p.next;
    }

    if (l1 != null)
        p.next = l1;

    if (l2 != null)
        p.next = l2;

    return l.next;
}

使用了自上而下的歸并排序方法對鏈表進行了排序颗品。使用 slow.nextfast.next.next 兩種速度獲取鏈表節(jié)點肯尺,從而獲取中間值。

鏈表的倒序

var reverseList = function(head) {
    let ans = null,cur = head
    while (cur != null) {
        let nextTmp = cur.next
        cur.next = ans
        ans = cur
        cur = nextTmp
    }
    return ans
};

排序

排序和查找算是算法中最重要的問題了躯枢。常用的排序算法有:

  • 插入排序
  • 選擇排序
  • 快速排序
  • 歸并排序
  • 計數(shù)排序

更多排序算法的知識點可參考《JS家的排序算法》则吟,文章作者圖文并茂的講解了各種排序算法,很容易理解锄蹂。
舉幾個排序算法的栗子:

求數(shù)組中第K大的值

/**
 * @param {number[]} nums
 * @param {number} k
 * @return {number}
 */
var findKthLargest = function (nums, k) {
    for (let i = 0; i <= k; i++) {
        let max = i
        for (let j = i; j < nums.length; j++) {
            if (nums[j] > nums[max]) max = j
        }
        swap(nums, i, max)
    }
    return nums[k - 1]
};

function swap(arr, a, b) {
    let tmp = arr[a]
    arr[a] = arr[b]
    arr[b] = tmp
}

使用了選擇排序排列了前 K 個值得到結(jié)果氓仲。

對有重復值的數(shù)組 [2,0,2,1,1,0] 排序

var sortColors = function (nums) {
    sort(nums, 0, nums.length - 1)
};

function sort(arr, lo, hi) {
    if (hi <= lo) return
    let lt = lo, i = lo + 1, gt = hi;
    let v = arr[lo]
    while (i <= gt) {
        if (arr[i] < v) swap(arr, lt++, i++)
        else if (arr[i] > v) swap(arr, i, gt--)
        else i++
    }
    sort(arr, lo, lt - 1)
    sort(arr, gt + 1, hi)
}

function swap(arr, a, b) {
    let x = arr[a]
    arr[a] = arr[b]
    arr[b] = x
}

這種有重復值的使用三向切分的快速排序是非常好的解決方案。當然得糜,計數(shù)排序法可是不錯的選擇敬扛。
還有之前提到的鏈表的排序使用的是歸并排序

算術(shù)題

算術(shù)題看似簡單朝抖,但是遇到最大的問題就是:如果使用累加啥箭、累成這種常熟級別的增長,遇到很大的數(shù)字會出現(xiàn) TLE (超出時間限制)治宣。所以急侥,我們要用指數(shù)級別的增長來找到結(jié)果砌滞。如:

計算 x 的 n 次方

var myPow = function (x, n) {
    if (n == 0) return 1
    if (n < 0) {
        n = -n
        x = 1 / x
    }
    return (n % 2 == 0) ? myPow(x * x, parseInt(n / 2)) : x * myPow(x * x, parseInt(n / 2));
};

一開始我使用了 x*x 這么乘上 n 次,但是遇到 n 太大就直接超時了坏怪。使用以上方案:29 = 2 * 44 = 2 * 82 = 2 * 64 = 128
直接從常熟級變化變?yōu)橹笖?shù)級變化贝润,這一點在數(shù)學運算中是需要注意的。

求 x 的平方根

var mySqrt = function (x) {
    let l = 0, r = x
    while (true) {
        let mid = parseInt(l + (r - l) / 2)
        if (mid * mid > x) {
            r = mid - 1
        } else if (mid * mid < x) {
            if ((mid + 1) * (mid + 1) > x) {
                return mid
            }
            l = mid + 1
        } else {
            return mid
        }
    }
};

這題使用二分法來找到結(jié)果陕悬。

二進制問題

二進制問題题暖,一般使用按位運算符和二進制轉(zhuǎn)換 Number.parseInt()Number.prototype.toString()來解決。

將一個32位數(shù)字的二進制進行倒序

var reverseBits = function(n) {
    var t = n.toString(2).split("");
    while(t.length < 32) t.unshift("0"); // 插入足夠的 0
    return parseInt(t.reverse().join(""), 2);
};

常用算法

講了這么多捉超,其實除了常用的排序胧卤、搜索,其他最常用的就是 DP拼岳、DFS枝誊、BFS 這三個算法了∠е剑可以這么說:掌握了排序和這三個算法叶撒,可以 AC 大多數(shù)的算法問題。這么牛逼的算法了解一下耐版?

簡單說說幾種排序和查找

  • 冒泡排序:遍歷數(shù)組祠够,對比元素和后面相鄰元素,如果當前元素大于后面元素粪牲,調(diào)換位置古瓤。這樣從頭遍歷到尾,獲取最后一位排序玩的元素腺阳。然后在 1 到 n - 1 中再次重復以上步驟落君。直到最后第一和第二個元素對比大小。是一種從后往前的排序亭引。
  • 選擇排序:遍歷數(shù)組绎速,找到最小的元素位置,與第一個元素調(diào)換位置焙蚓,然后縮小范圍從第二個元素開始遍歷纹冤,如此重復到最后一個元素」汗可以從后往前也可以從前往后排序赵哲。
function sort(arr) {
    const len = arr.length
    for (let i = 0; i < len; i++) {
        let min = i
        for (let j = i + 1; j < len; j++) {
            if (arr[j] < arr[min]) min = j
        }
        swap(arr, i, min)
        console.log(arr)
    }
    return arr
}
  • 插入排序:遍歷數(shù)組,選中某一個元素君丁,與前面相鄰元素對比枫夺,如果當前元素小于之前元素,調(diào)換位置绘闷,繼續(xù)對比直到當前元素前的元素小于當前元素(或者到最前面)橡庞,如此對所有元素排序一遍较坛。是一種從前往后的排序。
function sort(arr) {
    const len = arr.length
    for (let i = 1; i < len; i++) {
        for (let j = i; j > 0 && arr[j] < arr[j - 1]; j--) {
            swap(arr, j, j - 1)
            console.log(arr)
        }
    }
    return arr
}
  • 希爾排序:類似于插入排序扒最,選中一個元素與元素前 n 個元素進行比大小和調(diào)換位置丑勤。之后再縮小 n 的值。這種方法可以減少插入排序中最小值在最后面吧趣,然后需要一個一個調(diào)換位置知道最前面這類問題法竞。減少調(diào)換次數(shù)。是一種從前往后的排序强挫。
  • 歸并排序:在《算法》中提到了兩種歸并排序:一種是自上而下的歸并排序岔霸。將數(shù)組不斷二分到最小單位(1到2個元素)將他們進行排序,之后將前兩個和后兩個元素對比俯渤,如此往上最后完成整個數(shù)組的排序呆细。還有一種自下而上的歸并排序是直接將數(shù)組分割為若干個子數(shù)組進行排序然后合并。
let aux = new Array(arr.length)
function sort(arr, lo, hi) {
    if (hi <= lo) return
    let mid = lo + (parseInt((hi - lo) / 2))

    sort(arr, lo, mid)
    sort(arr, mid + 1, hi)
    merge(arr, lo, mid, hi)
}

function merge(arr, lo, mid, hi) {
    let i = lo, j = mid + 1
    for (let k = lo; k <= hi; k++) {
        aux[k] = arr[k]
    }
    for (let k = lo; k <= hi; k++) {
        if (i > mid) arr[k] = aux[j++]
        else if (j > hi) arr[k] = aux[i++]
        else if (aux[j] < aux[i]) arr[k] = aux[j++]
        else arr[k] = aux[i++]
    }
    console.log(arr)
}
  • 快速排序:選定第一個值為中間值八匠,然后將小于中間值的元素放在中間值的左側(cè)而大于中間值的元素放在中間值右側(cè)絮爷,然后對兩側(cè)的元素分別再次切割,直到最小單位梨树。
function sort(arr, lo, hi) {
    if (hi <= lo + 1) return
    let mid = partition(arr, lo, hi) // 切分方法
    sort(arr, lo, mid)
    sort(arr, mid + 1, hi)
}

function partition(arr, lo, hi) {
    let i = lo, j = hi + 1
    let v = arr[lo]
    while(true) {
        while(arr[++i] < v) if (i == hi) break
        while(v < arr[--j]) if (j == lo) break
        if ((i >= j)) break
        swap(arr, i, j)
        console.log(arr)
    }
    swap(arr, lo, j)
    console.log(arr)
    return j
}
  • 三向切分的快速排序:類似于快速排序坑夯,優(yōu)化點在于如果某個元素等于切分元素,元素位置不變抡四。最后小于切分元素的到左邊柜蜈,等于切分元素的根據(jù)數(shù)量放在中間,大于切分元素的放在右邊床嫌。適用于有大量相同大小元素的數(shù)組。
function sort(arr, lo, hi) {
    if (hi <= lo) return
    let lt = lo, i = lo + 1, gt = hi;
    let v = arr[lo]
    while (i <= gt) {
        if (arr[i] < v) swap(arr, lt++, i++)
        else if (arr[i] > v) swap(arr, i, gt--)
        else i++
        console.log(arr)
    }
    sort(arr, lo, lt - 1)
    sort(arr, gt + 1, hi)
}
  • 堆排序:堆排序可以說是一種利用堆的概念來排序的選擇排序胸私。使用優(yōu)先隊列返回最大值的特性逐個返回當前堆的最大值厌处。
  • 計數(shù)排序:就是將數(shù)組中所有元素的出現(xiàn)次數(shù)保存在一個數(shù)組中,然后按照從小到大返回排序后的數(shù)組岁疼。
  • 桶排序:其實就是字符串排序的 LSD 和 MSD 排序阔涉。LSD 使用索引計數(shù)法從字符串右邊向左邊移動,根據(jù)當前值進行排序捷绒。而 MSD 是從左到右使用索引計數(shù)法來排序瑰排,在字符串第一個字符后,將字符串數(shù)組分為若干個相同首字符串的數(shù)組各自進行第二暖侨、第三次的 MSD 排序椭住。
  • 二分查找: 對有序數(shù)組去中間值與目標值相比對。如果目標值小于中間值字逗,取前一半數(shù)組繼續(xù)二分京郑。如果目標值大于中間值宅广,取后一半數(shù)組繼續(xù)二分。如果目標值等于中間值些举,命中跟狱!

DP

關(guān)于動態(tài)規(guī)劃,可以看下詳解動態(tài)規(guī)劃——鄒博講動態(tài)規(guī)劃一文户魏,其中講了路徑驶臊、硬幣、最長子序列叼丑。都是 LeetCode 中有的題目关翎。
我的理解:動態(tài)規(guī)劃就是下一狀態(tài)可以根據(jù)上一狀態(tài),或之前幾個狀態(tài)獲取到的一種推理過程幢码。

DFS

深度優(yōu)先搜索(DFS)就是選中某條從條件1到條件2的某條可能性進行搜索笤休,之后返回搜索其他一條可能性,如此一條條升入症副。舉個栗子店雅,如果有5條路,那么 DFS 算法就是只排出一個斥候先走一條路走到底去偵察贞铣,如果走不通那么返回走下一條路徑闹啦。

DFS(頂點v)
{
  標記v為已遍歷;
  for(對于每一個鄰接v且未標記遍歷的點u)
      DFS(u);
}

DFS 使用的是遞歸的方式進行搜索的辕坝。

示例:在二維字母矩陣中查找是否能夠使用相鄰字母組成目標單詞窍奋。

var exist = function (board, word) {
    for (let y = 0; y < board.length; y++) {
        for (let x = 0; x < board[0].length; x++) {
            if (find(board, word, y, x, 0)) return true
        }
    }
    return false
};

function find(board, word, y, x, d) {
    if (d == word.length) return true
    if (y < 0 || x < 0 || y == board.length || x == board[y].length) return false;
    if (board[y][x] != word[d]) return false
    let tmp = board[y][x]
    board[y][x] = "*"
    let exist = find(board, word, y, x + 1, d + 1)
        || find(board, word, y, x - 1, d + 1)
        || find(board, word, y + 1, x, d + 1)
        || find(board, word, y - 1, x, d + 1)
    board[y][x] = tmp
    return exist
}

BFS

廣度優(yōu)先搜索(BFS)就是將從條件1到條件2的所有可能性都列出來同步搜索的過程。適用于查找最短路徑酱畅。舉個栗子琳袄,如果有5條路,那么 BFS 算法就是分別向5條路排出斥候去偵察纺酸。

BFS()
{
  輸入起始點窖逗;
  初始化所有頂點標記為未遍歷;
  初始化一個隊列queue并將起始點放入隊列餐蔬;

  while(queue不為空)
  {

    從隊列中刪除一個頂點s并標記為已遍歷碎紊; 
    將s鄰接的所有還沒遍歷的點加入隊列;
  }
}

BFS是使用數(shù)組存儲下一頂點的方式樊诺。

示例:每次改變一次字母仗考,通過給定數(shù)組中的單詞,從單詞 A 變?yōu)閱卧~ B词爬。(127題

/**
 * @param {string} beginWord
 * @param {string} endWord
 * @param {string[]} wordList
 * @return {number}
 */
var ladderLength = function (beginWord, endWord, wordList) {
    if (!wordList.includes(endWord)) return 0
    let set = new Set(),
        visited = new Set(),
        len = 1

    set.add(beginWord)
    visited.add(beginWord)
    while (set.size != 0) {
        let tmp = new Set([...set])

        for (let w of tmp) {
            visited.add(w)
            set.delete(w)

            if (changeOneChar(w, endWord))
                return len + 1
            
            for (let word of wordList){
                if (changeOneChar(w, word) && !visited.has(word)){
                    set.add(word)
                }
            }
        }
        len++
    }
    return 0
};

function changeOneChar(a, b) {
    let count = 0
    for (let i = 0; i < a.length; i++)
        if (a[i] != b[i])
            count++
    return count == 1
}

最后

寫下 AC 一遍題目之后的收獲秃嗜。

  • 知道了方法論,做起題來輕松了不少。
  • 遇到問題多找輪子痪寻,一定有某種方法論可以用螺句。
  • 不要耍小聰明用一些奇巧淫技,思路不對再怎么繞都是浪費時間橡类。
  • 不要想著自己造輪子(特別是算法方面)蛇尚,絕大多數(shù)問題前輩一定有更好更完善的方案在。自己造輪子費時費事又沒太大意義顾画。
  • 看答案和自己做是兩回事取劫,自己動手實現(xiàn)了才能算是會了。
  • 算法之所以存在研侣,就是用來適應(yīng)某些場景谱邪、解決某類問題的。在對的場景選擇對的算法才能體現(xiàn)算法的價值庶诡,不要濫用算法惦银。
  • 沒必要把所有算法都精通,但起碼在遇到問題時可以找到最優(yōu)算法解決問題末誓。即知道算法的存在及其用途扯俱,按需深入學習。

其實刷算法題還是很有趣的事情喇澡,之后計劃把 LeetCode 題庫中的所有問題都刷一遍~

PS:本文以及相關(guān)項目中有任何錯誤或者可以改進的地方迅栅,還請?zhí)岢觥9餐M步~

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末晴玖,一起剝皮案震驚了整個濱河市读存,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌呕屎,老刑警劉巖颁湖,帶你破解...
    沈念sama閱讀 216,591評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件聪廉,死亡現(xiàn)場離奇詭異园细,居然都是意外死亡棍郎,警方通過查閱死者的電腦和手機屋休,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,448評論 3 392
  • 文/潘曉璐 我一進店門逆甜,熙熙樓的掌柜王于貴愁眉苦臉地迎上來与倡,“玉大人滩愁,你說我怎么就攤上這事虫给√俾眨” “怎么了?”我有些...
    開封第一講書人閱讀 162,823評論 0 353
  • 文/不壞的土叔 我叫張陵抹估,是天一觀的道長缠黍。 經(jīng)常有香客問我,道長药蜻,這世上最難降的妖魔是什么瓷式? 我笑而不...
    開封第一講書人閱讀 58,204評論 1 292
  • 正文 為了忘掉前任替饿,我火速辦了婚禮,結(jié)果婚禮上贸典,老公的妹妹穿的比我還像新娘视卢。我一直安慰自己,他們只是感情好廊驼,可當我...
    茶點故事閱讀 67,228評論 6 388
  • 文/花漫 我一把揭開白布据过。 她就那樣靜靜地躺著,像睡著了一般妒挎。 火紅的嫁衣襯著肌膚如雪绳锅。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,190評論 1 299
  • 那天酝掩,我揣著相機與錄音鳞芙,去河邊找鬼。 笑死期虾,一個胖子當著我的面吹牛原朝,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播彻消,決...
    沈念sama閱讀 40,078評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼竿拆,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了宾尚?” 一聲冷哼從身側(cè)響起丙笋,我...
    開封第一講書人閱讀 38,923評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎煌贴,沒想到半個月后御板,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,334評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡牛郑,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,550評論 2 333
  • 正文 我和宋清朗相戀三年怠肋,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片淹朋。...
    茶點故事閱讀 39,727評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡笙各,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出础芍,到底是詐尸還是另有隱情杈抢,我是刑警寧澤,帶...
    沈念sama閱讀 35,428評論 5 343
  • 正文 年R本政府宣布仑性,位于F島的核電站惶楼,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜歼捐,卻給世界環(huán)境...
    茶點故事閱讀 41,022評論 3 326
  • 文/蒙蒙 一何陆、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧豹储,春花似錦贷盲、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,672評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至朦乏,卻和暖如春球及,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背呻疹。 一陣腳步聲響...
    開封第一講書人閱讀 32,826評論 1 269
  • 我被黑心中介騙來泰國打工吃引, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人刽锤。 一個月前我還...
    沈念sama閱讀 47,734評論 2 368
  • 正文 我出身青樓镊尺,卻偏偏與公主長得像,于是被迫代替她去往敵國和親并思。 傳聞我的和親對象是個殘疾皇子庐氮,可洞房花燭夜當晚...
    茶點故事閱讀 44,619評論 2 354

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

  • 因為之前就復習完數(shù)據(jù)結(jié)構(gòu)了,所以為了保持記憶宋彼,整理了一份復習綱要弄砍,復習的時候可以看著綱要想具體內(nèi)容。 樹 樹的基本...
    牛富貴兒閱讀 6,870評論 3 10
  • 背景 一年多以前我在知乎上答了有關(guān)LeetCode的問題, 分享了一些自己做題目的經(jīng)驗输涕。 張土汪:刷leetcod...
    土汪閱讀 12,743評論 0 33
  • 1 初級排序算法 排序算法關(guān)注的主要是重新排列數(shù)組元素音婶,其中每個元素都有一個主鍵。排序算法是將所有元素主鍵按某種方...
    深度沉迷學習閱讀 1,404評論 0 1
  • 張愛玲在很早以前就說過:出名要趁早莱坎。在信息更新速度極快的今天衣式,大家都很努力的想要出名,想要被關(guān)注檐什,很多明星都整容成...
    黎落清音閱讀 175評論 0 3
  • 2017年11月12日 晴 十一月的路燈側(cè)耳聽 楓樹下嚶嚶的女聲 顫抖的枝椏祈求太平 孤影握住這夜色 柔弱的明朗和...
    鮮栗子閱讀 138評論 0 0