雖然不少人都以爲前端算法弱,但其實 JavaScript 也能夠刷題啊!最近兩個月斷斷續續刷完了 leetcode 前 200 的 middle + hard ,總結了一些刷題經常使用的模板代碼。走過路過發現 bug 請指出,拯救一個辣雞(但很帥)的少年就靠您啦!javascript
包括打印函數和一些數學函數。html
const _max = Math.max.bind(Math); const _min = Math.min.bind(Math); const _pow = Math.pow.bind(Math); const _floor = Math.floor.bind(Math); const _round = Math.round.bind(Math); const _ceil = Math.ceil.bind(Math); const log = console.log.bind(console); //const log = _ => {}
log 在提交的代碼中固然是用不到的,不過在調試時十分有用。可是當代碼裏面加了不少 log 的時候,提交時還須要一個個註釋掉就至關麻煩了,只要將 log 賦值爲空函數就能夠了。前端
舉一個簡單的例子,下面的代碼是能夠直接提交的。java
// 計算 1+2+...+n // const log = console.log.bind(console); const log = _ => {} function sumOneToN(n) { let sum = 0; for (let i = 1; i <= n; i++) { sum += i; log(`i=${i}: sum=${sum}`); } return sum; } sumOneToN(10);
判斷一個整數 x
的奇偶性: x & 1 = 1 (奇數) , x & 1 = 0 (偶數)
node
求一個浮點數 x
的整數部分: ~~x ,對於正數至關於 floor(x) 對於負數至關於 ceil(-x) git
計算 2 ^ n : 1 << n 至關於 pow(2, n) github
計算一個數 x
除以 2 的 n 倍: x >> n 至關於 ~~(x / pow(2, n)) 面試
判斷一個數 x
是 2 的整數冪(即 x = 2 ^ n ): x & (x - 1) = 0 算法
※注意※:上面的位運算只對32位帶符號的整數有效,若是使用的話,必定要注意數!據!範!圍!數組
記住這些技巧的做用:
提高運行速度 ❌
提高逼格 ✅
舉一個實用的例子,快速冪(原理自行google)
// 計算x^n n爲整數 function qPow(x, n) { let result = 1; while (n) { if (n & 1) result *= x; // 同 if(n%2) x = x * x; n >>= 1; // 同 n=floor(n/2) } return result; }
剛開始作 LeetCode 的題就遇到了不少鏈表的題。噁心心。最麻煩的不是寫題,是調試啊!!因而總結了一些鏈表的輔助函數。
/** * 鏈表節點 * @param {*} val * @param {ListNode} next */ function ListNode(val, next = null) { this.val = val; this.next = next; } /** * 將一個數組轉爲鏈表 * @param {array} a * @return {ListNode} */ const getListFromArray = (a) => { let dummy = new ListNode() let pre = dummy; a.forEach(x => pre = pre.next = new ListNode(x)); return dummy.next; } /** * 將一個鏈表轉爲數組 * @param {ListNode} node * @return {array} */ const getArrayFromList = (node) => { let a = []; while (node) { a.push(node.val); node = node.next; } return a; } /** * 打印一個鏈表 * @param {ListNode} node */ const logList = (node) => { let str = 'list: '; while (node) { str += node.val + '->'; node = node.next; } str += 'end'; log(str); }
還有一個經常使用小技巧,每次寫鏈表的操做,都要注意判斷表頭,若是建立一個空表頭來進行操做會方便不少。
let dummy = new ListNode(); // 返回 return dummy.next;
使用起來超爽噠~舉個例子。@leetcode 82。題意就是刪除鏈表中連續相同值的節點。
/* * @lc app=leetcode id=82 lang=javascript * * [82] Remove Duplicates from Sorted List II */ /** * @param {ListNode} head * @return {ListNode} */ var deleteDuplicates = function(head) { // 空指針或者只有一個節點不須要處理 if (head === null || head.next === null) return head; let dummy = new ListNode(); let oldLinkCurrent = head; let newLinkCurrent = dummy; while (oldLinkCurrent) { let next = oldLinkCurrent.next; // 若是當前節點和下一個節點的值相同 就要一直向前直到出現不一樣的值 if (next && oldLinkCurrent.val === next.val) { while (next && oldLinkCurrent.val === next.val) { next = next.next; } oldLinkCurrent = next; } else { newLinkCurrent = newLinkCurrent.next = oldLinkCurrent; oldLinkCurrent = oldLinkCurrent.next; } } newLinkCurrent.next = null; // 記得結尾置空~ logList(dummy.next); return dummy.next; }; deleteDuplicates(getListFromArray([1,2,3,3,4,4,5])); deleteDuplicates(getListFromArray([1,1,2,2,3,3,4,4,5])); deleteDuplicates(getListFromArray([1,1])); deleteDuplicates(getListFromArray([1,2,2,3,3]));
本地運行結果
list: 1->2->5->end list: 5->end list: end list: 1->end
是否是很方便!
矩陣的題目也有不少,基本每個須要用到二維數組的題,都涉及到初始化,求行數列數,遍歷的代碼。因而簡單提取出來幾個函數。
/** * 初始化一個二維數組 * @param {number} r 行數 * @param {number} c 列數 * @param {*} init 初始值 */ const initMatrix = (r, c, init = 0) => new Array(r).fill().map(_ => new Array(c).fill(init)); /** * 獲取一個二維數組的行數和列數 * @param {any[][]} matrix * @return [row, col] */ const getMatrixRowAndCol = (matrix) => matrix.length === 0 ? [0, 0] : [matrix.length, matrix[0].length]; /** * 遍歷一個二維數組 * @param {any[][]} matrix * @param {Function} func */ const matrixFor = (matrix, func) => { matrix.forEach((row, i) => { row.forEach((item, j) => { func(item, i, j, row, matrix); }); }) } /** * 獲取矩陣第index個元素 從0開始 * @param {any[][]} matrix * @param {number} index */ function getMatrix(matrix, index) { let col = matrix[0].length; let i = ~~(index / col); let j = index - i * col; return matrix[i][j]; } /** * 設置矩陣第index個元素 從0開始 * @param {any[][]} matrix * @param {number} index */ function setMatrix(matrix, index, value) { let col = matrix[0].length; let i = ~~(index / col); let j = index - i * col; return matrix[i][j] = value; }
找一個簡單的矩陣的題示範一下用法。@leetcode 566。題意就是將一個矩陣從新排列爲r行c列。
/* * @lc app=leetcode id=566 lang=javascript * * [566] Reshape the Matrix */ /** * @param {number[][]} nums * @param {number} r * @param {number} c * @return {number[][]} */ var matrixReshape = function(nums, r, c) { // 將一個矩陣從新排列爲r行c列 // 首先獲取原來的行數和列數 let [r1, c1] = getMatrixRowAndCol(nums); log(r1, c1); // 不合法的話就返回原矩陣 if (!r1 || r1 * c1 !== r * c) return nums; // 初始化新矩陣 let matrix = initMatrix(r, c); // 遍歷原矩陣生成新矩陣 matrixFor(nums, (val, i, j) => { let index = i * c1 + j; // 計算是第幾個元素 log(index); setMatrix(matrix, index, val); // 在新矩陣的對應位置賦值 }); return matrix; }; let x = matrixReshape([[1],[2],[3],[4]], 2, 2); log(x)
當我作到二叉樹相關的題目,我發現,我錯怪鏈表了,嗚嗚嗚這個更噁心。
固然對於二叉樹,只要你掌握先序遍歷,後序遍歷,中序遍歷,層序遍歷,遞歸以及非遞歸版,先序中序求二叉樹,先序後序求二叉樹,基本就能AC大部分二叉樹的題目了(我瞎說的)。
二叉樹的題目 input 通常都是層序遍歷的數組,因此寫了層序遍歷數組和二叉樹的轉換,方便調試。
function TreeNode(val, left = null, right = null) { this.val = val; this.left = left; this.right = right; } /** * 經過一個層次遍歷的數組生成一棵二叉樹 * @param {any[]} array * @return {TreeNode} */ function getTreeFromLayerOrderArray(array) { let n = array.length; if (!n) return null; let index = 0; let root = new TreeNode(array[index++]); let queue = [root]; while(index < n) { let top = queue.shift(); let v = array[index++]; top.left = v == null ? null : new TreeNode(v); if (index < n) { let v = array[index++]; top.right = v == null ? null : new TreeNode(v); } if (top.left) queue.push(top.left); if (top.right) queue.push(top.right); } return root; } /** * 層序遍歷一棵二叉樹 生成一個數組 * @param {TreeNode} root * @return {any[]} */ function getLayerOrderArrayFromTree(root) { let res = []; let que = [root]; while (que.length) { let len = que.length; for (let i = 0; i < len; i++) { let cur = que.shift(); if (cur) { res.push(cur.val); que.push(cur.left, cur.right); } else { res.push(null); } } } while (res.length > 1 && res[res.length - 1] == null) res.pop(); // 刪掉結尾的 null return res; }
一個例子,@leetcode 110,判斷一棵二叉樹是否是平衡二叉樹。
/** * @param {TreeNode} root * @return {boolean} */ var isBalanced = function(root) { if (!root) return true; // 認爲空指針也是平衡樹吧 // 獲取一個二叉樹的深度 const d = (root) => { if (!root) return 0; return _max(d(root.left), d(root.right)) + 1; } let leftDepth = d(root.left); let rightDepth = d(root.right); // 深度差不超過 1 且子樹都是平衡樹 if (_min(leftDepth, rightDepth) + 1 >= _max(leftDepth, rightDepth) && isBalanced(root.left) && isBalanced(root.right)) return true; return false; }; log(isBalanced(getTreeFromLayerOrderArray([3,9,20,null,null,15,7]))); log(isBalanced(getTreeFromLayerOrderArray([1,2,2,3,3,null,null,4,4])));
參考 C++ STL 中的 lower_bound 和 upper_bound 。這兩個函數真的很好用的!
/** * 尋找>=target的最小下標 * @param {number[]} nums * @param {number} target * @return {number} */ function lower_bound(nums, target) { let first = 0; let len = nums.length; while (len > 0) { let half = len >> 1; let middle = first + half; if (nums[middle] < target) { first = middle + 1; len = len - half - 1; } else { len = half; } } return first; } /** * 尋找>target的最小下標 * @param {number[]} nums * @param {number} target * @return {number} */ function upper_bound(nums, target) { let first = 0; let len = nums.length; while (len > 0) { let half = len >> 1; let middle = first + half; if (nums[middle] > target) { len = half; } else { first = middle + 1; len = len - half - 1; } } return first; }
照例,舉個例子,@leetcode 34。題意是給一個排好序的數組和一個目標數字,求數組中等於目標數字的元素最小下標和最大下標。不存在就返回 -1。
/* * @lc app=leetcode id=34 lang=javascript * * [34] Find First and Last Position of Element in Sorted Array */ /** * @param {number[]} nums * @param {number} target * @return {number[]} */ var searchRange = function(nums, target) { let lower = lower_bound(nums, target); let upper = upper_bound(nums, target); let size = nums.length; // 不存在返回 [-1, -1] if (lower >= size || nums[lower] !== target) return [-1, -1]; return [lower, upper - 1]; };
前面說的那些模板,難道每一次打開新的一道題都要複製一遍麼?固然不用啦。
首先配置代碼片斷 選擇 Code -> Preferences -> User Snippets ,而後選擇 JavaScript
而後把文件替換爲下面的代碼:
{ "leetcode template": { "prefix": "@lc", "body": [ "const _max = Math.max.bind(Math);","const _min = Math.min.bind(Math);","const _pow = Math.pow.bind(Math);","const _floor = Math.floor.bind(Math);","const _round = Math.round.bind(Math);","const _ceil = Math.ceil.bind(Math);","const log = console.log.bind(console);","// const log = _ => {}","/**************** 鏈表 ****************/","/**"," * 鏈表節點"," * @param {*} val"," * @param {ListNode} next"," */","function ListNode(val, next = null) {"," this.val = val;"," this.next = next;","}","/**"," * 將一個數組轉爲鏈表"," * @param {array} array"," * @return {ListNode}"," */","const getListFromArray = (array) => {"," let dummy = new ListNode()"," let pre = dummy;"," array.forEach(x => pre = pre.next = new ListNode(x));"," return dummy.next;","}","/**"," * 將一個鏈表轉爲數組"," * @param {ListNode} list"," * @return {array}"," */","const getArrayFromList = (list) => {"," let a = [];"," while (list) {"," a.push(list.val);"," list = list.next;"," }"," return a;","}","/**"," * 打印一個鏈表"," * @param {ListNode} list "," */","const logList = (list) => {"," let str = 'list: ';"," while (list) {"," str += list.val + '->';"," list = list.next;"," }"," str += 'end';"," log(str);","}","/**************** 矩陣(二維數組) ****************/","/**"," * 初始化一個二維數組"," * @param {number} r 行數"," * @param {number} c 列數"," * @param {*} init 初始值"," */","const initMatrix = (r, c, init = 0) => new Array(r).fill().map(_ => new Array(c).fill(init));","/**"," * 獲取一個二維數組的行數和列數"," * @param {any[][]} matrix"," * @return [row, col]"," */","const getMatrixRowAndCol = (matrix) => matrix.length === 0 ? [0, 0] : [matrix.length, matrix[0].length];","/**"," * 遍歷一個二維數組"," * @param {any[][]} matrix "," * @param {Function} func "," */","const matrixFor = (matrix, func) => {"," matrix.forEach((row, i) => {"," row.forEach((item, j) => {"," func(item, i, j, row, matrix);"," });"," })","}","/**"," * 獲取矩陣第index個元素 從0開始"," * @param {any[][]} matrix "," * @param {number} index "," */","function getMatrix(matrix, index) {"," let col = matrix[0].length;"," let i = ~~(index / col);"," let j = index - i * col;"," return matrix[i][j];","}","/**"," * 設置矩陣第index個元素 從0開始"," * @param {any[][]} matrix "," * @param {number} index "," */","function setMatrix(matrix, index, value) {"," let col = matrix[0].length;"," let i = ~~(index / col);"," let j = index - i * col;"," return matrix[i][j] = value;","}","/**************** 二叉樹 ****************/","/**"," * 二叉樹節點"," * @param {*} val"," * @param {TreeNode} left"," * @param {TreeNode} right"," */","function TreeNode(val, left = null, right = null) {"," this.val = val;"," this.left = left;"," this.right = right;","}","/**"," * 經過一個層次遍歷的數組生成一棵二叉樹"," * @param {any[]} array"," * @return {TreeNode}"," */","function getTreeFromLayerOrderArray(array) {"," let n = array.length;"," if (!n) return null;"," let index = 0;"," let root = new TreeNode(array[index++]);"," let queue = [root];"," while(index < n) {"," let top = queue.shift();"," let v = array[index++];"," top.left = v == null ? null : new TreeNode(v);"," if (index < n) {"," let v = array[index++];"," top.right = v == null ? null : new TreeNode(v);"," }"," if (top.left) queue.push(top.left);"," if (top.right) queue.push(top.right);"," }"," return root;","}","/**"," * 層序遍歷一棵二叉樹 生成一個數組"," * @param {TreeNode} root "," * @return {any[]}"," */","function getLayerOrderArrayFromTree(root) {"," let res = [];"," let que = [root];"," while (que.length) {"," let len = que.length;"," for (let i = 0; i < len; i++) {"," let cur = que.shift();"," if (cur) {"," res.push(cur.val);"," que.push(cur.left, cur.right);"," } else {"," res.push(null);"," }"," }"," }"," while (res.length > 1 && res[res.length - 1] == null) res.pop(); // 刪掉結尾的 null"," return res;","}","/**************** 二分查找 ****************/","/**"," * 尋找>=target的最小下標"," * @param {number[]} nums"," * @param {number} target"," * @return {number}"," */","function lower_bound(nums, target) {"," let first = 0;"," let len = nums.length;",""," while (len > 0) {"," let half = len >> 1;"," let middle = first + half;"," if (nums[middle] < target) {"," first = middle + 1;"," len = len - half - 1;"," } else {"," len = half;"," }"," }"," return first;","}","","/**"," * 尋找>target的最小下標"," * @param {number[]} nums"," * @param {number} target"," * @return {number}"," */","function upper_bound(nums, target) {"," let first = 0;"," let len = nums.length;",""," while (len > 0) {"," let half = len >> 1;"," let middle = first + half;"," if (nums[middle] > target) {"," len = half;"," } else {"," first = middle + 1;"," len = len - half - 1;"," }"," }"," return first;","}", "$1" ], "description": "LeetCode經常使用代碼模板" } }
之後每一次寫題以前,鍵入 @lc
就會出現提示,輕鬆加入代碼模板。
固然,必須推薦刷題神器,vscode 中的一款插件 vscode-leetcode
最後我要大聲說,前端真的有機會用到算法的(不僅面試)!來一塊兒快樂刷題!
原文出處:https://www.cnblogs.com/wenruo/p/11100537.html