本文主要记录了博主的 Leetcode 算法刷题记录,方便以后查询复习。
图论
岛屿数量
LeetCode 原题链接:200. 岛屿数量
深度优先遍历,基本思路为,遇到一个岛就把这个岛给沉没掉,然后统计数加 1。
1/**2 * @param {character[][]} grid3 * @return {number}4 */5const numIslands = (grid) => {6 const m = grid.length,7 n = grid[0].length;8 const dfs = (i, j) => {9 if (i < 0 || i > m - 1 || j < 0 || j > n - 1 || grid[i][j] === "0") {10 return;11 }12 grid[i][j] = "0";13 dfs(i + 1, j);14 dfs(i - 1, j);15 dfs(i, j + 1);16 dfs(i, j - 1);17 };18 let count = 0;19 for (let i = 0; i < m; i++) {20 for (let j = 0; j < n; j++) {21 if (grid[i][j] === "1") {22 count++;23 dfs(i, j);24 }25 }26 }27 return count;28};腐烂的橘子
LeetCode 原题链接:994. 腐烂的橘子
广度优先遍历,利用循环比较好做,这边需要注意广度优先遍历的一些条件,辅助函数用于推入队列,上下左右扩散的过程还是由循环过程去做。
1/**2 * @param {number[][]} grid3 * @return {number}4 */5const orangesRotting = (grid) => {6 const m = grid.length,7 n = grid[0].length;8 const queue = [];9
10 for (let i = 0; i < m; i++) {11 for (let j = 0; j < n; j++) {12 if (grid[i][j] === 2) {13 queue.push([i, j]);14 }15 }16 }17
18 const step = (i, j, arr) => {19 if (i < 0 || i > m - 1 || j < 0 || j > n - 1 || grid[i][j] !== 1) {20 return;21 }22 grid[i][j] = 2;23 arr.push([i, j]);24 };25
26 let count = 0;27 while (queue.length) {28 const tempArr = [];29 while (queue.length) {30 const [i, j] = queue.shift();31 step(i + 1, j, tempArr);32 step(i - 1, j, tempArr);33 step(i, j + 1, tempArr);34 step(i, j - 1, tempArr);35 }36 if (tempArr.length) {37 count++;38 queue.push(...tempArr);39 }40 }41
42 for (let i = 0; i < m; i++) {43 for (let j = 0; j < n; j++) {44 if (grid[i][j] === 1) {45 return -1;46 }47 }48 }49
50 return count;51};52
53console.log(orangesRotting([[0, 2]]));课程表
LeetCode 原题链接:207. 课程表
广度优先遍历,采用图和入度的概念进行求解。
- 构建入度数组,使用哈希表以该课程为键,依赖该课程的数组为值,建立哈希表。
- 将入度为 0 的数组推入队列。
- 迭代,入度为零的课程继续推入队列
1/**2 * @param {number} numCourses3 * @param {number[][]} prerequisites4 * @return {boolean}5 */6const canFinish = (numCourses, prerequisites) => {7 const inDegArr = new Array(numCourses).fill(0);8 const map = new Map();9 for (let i = 0; i < prerequisites.length; i++) {10 inDegArr[prerequisites[i][0]]++;11 if (map.has(prerequisites[i][1])) {12 map.get(prerequisites[i][1]).push(prerequisites[i][0]);13 } else {14 map.set(prerequisites[i][1], [prerequisites[i][0]]);15 }16 }17 const queue = [];18 for (let i = 0; i < inDegArr.length; i++) {19 if (inDegArr[i] === 0) {20 queue.push(i);21 }22 }23 let count = 0;24 while (queue.length) {25 const course = queue.shift();26 count++;27 const depArr = map.get(course);28 if (depArr) {29 for (const dep of depArr) {30 inDegArr[dep]--;31 if (inDegArr[dep] === 0) {32 queue.push(dep);33 }34 }35 }36 }37 return count === numCourses;38};实现 Trie (前缀树)
LeetCode 原题链接:208. 实现 Trie (前缀树)
采用迭代对象来求解,可以考虑使用数组的reduce方法。
1class Trie {2 constructor() {3 this.children = {};4 }5
6 insert(word) {7 let node = this.children;8 for (const ch of word) {9 if (!node[ch]) {10 node[ch] = {};11 }12 node = node[ch];13 }14 node.isEnd = true;15 }16 searchPrefix(prefix) {17 let node = this.children;18 for (const ch of prefix) {19 if (!node[ch]) {20 return false;21 }22 node = node[ch];23 }24 return node;25 }26 search(word) {27 const node = this.searchPrefix(word);28 return node !== undefined && node.isEnd !== undefined;29 }30 startsWith(prefix) {31 return this.searchPrefix(prefix);32 }33}回溯
全排列
LeetCode 原题链接:46. 全排列
无他,惟手熟尔。😊
1/**2 * @param {number[]} nums3 * @return {number[][]}4 */5const permute = (nums) => {6 const step = (arr) => {7 if (arr.length <= 1) return [arr];8 if (arr.length === 2) {9 return [10 [arr[0], arr[1]],11 [arr[1], arr[0]],12 ];13 }14 if (arr.length >= 3) {15 const res = [];16 for (let i = 0; i < arr.length; i++) {17 const tempArr = arr.filter((item) => item !== arr[i]);18 const tmpRes = step(tempArr);19 for (const temp of tmpRes) {20 res.push([arr[i]].concat(temp));21 }22 }23 return res;24 }25 };26 return step(nums);27};28
29/**30 * @param {number[]} nums31 * @return {number[][]}32 */33const permute0 = (nums) => {34 const res = [];35 const step = (first) => {36 if (first === nums.length) {37 res.push([...nums]);38 }39 for (let i = first; i < nums.length; i++) {40 [nums[first], nums[i]] = [nums[i], nums[first]];41 step(first + 1);42 [nums[first], nums[i]] = [nums[i], nums[first]];43 }44 };45 step(0);46 return res;47};48console.log(permute0([1, 2, 3]));字集
LeetCode 原题链接:78. 字集
可采用幂集算法和深度遍历解决。 对于幂集算法
- 左移
n位,循环。 - 循环数组的长度次,推入所有的有效数字。
1/**2 * @param {number[]} nums3 * @return {number[][]}4 */5const subsets = (nums) => {6 const res = [];7 for (let i = 0; i < 1 << nums.length; i++) {8 const temp = [];9 for (let j = 0; j < nums.length; j++) {10 if (i & (1 << j)) {11 temp.push(nums[j]);12 }13 }14 res.push(temp);15 }16 return res;17};18
19const subsets = function (nums) {20 const temp = [];21 const res = [];22 const dfs = (cur) => {23 if (cur === nums.length) {24 res.push([...temp]);25 return;26 }27 temp.push(nums[cur]);28 dfs(cur + 1);29 temp.pop();30 dfs(cur + 1);31 };32 dfs(0);33 return res;34};电话号码的数字组合
LeetCode 原题链接:17. 电话号码的数字组合
采用回溯算法,从前面回溯到后面
1/**2 * @param {string} digits3 * @return {string[]}4 */5const letterCombinations = (digits) => {6 if (digits.length === 0) {7 return [];8 }9 const map = new Map([10 ["2", "abc"],11 ["3", "def"],12 ["4", "ghi"],13 ["5", "jkl"],14 ["6", "mno"],15 ["7", "pqrs"],16 ["8", "tuv"],17 ["9", "wxyz"],18 ]);19 const res = [];20 const step = (str, index) => {21 if (index === digits.length) {22 res.push(str);23 return;24 }25 const charSet = map.get(digits[index]);26 for (const char of charSet) {27 step(str + char, index + 1);28 }29 };30 step("", 0);31 return res;32};组合总和
LeetCode 原题链接:39. 组合总和
排序candidates数组,递归回溯,选择当前数字或者不选择当前数字然后跳过。
1/**2 * @param {number[]} candidates3 * @param {number} target4 * @return {number[][]}5 */6const combinationSum = (candidates, target) => {7 const res = [];8 candidates.sort((a, b) => a - b);9 const step = (curVal, index, arr) => {10 if (index === candidates.length) {11 return;12 }13 if (curVal === 0) {14 res.push(arr);15 return;16 }17 if (candidates[index] < curVal) {18 step(curVal, index + 1, arr);19 }20 if (curVal - candidates[index] >= 0) {21 step(curVal - candidates[index], index, [...arr, candidates[index]]);22 }23 };24 step(target, 0, []);25 return res;26};括号总和
LeetCode 原题链接:22. 括号生成
采用深度遍历的方式进行求解,左侧括号在小于num的情况下可以一直推入,右侧括号只有在左侧括号数量大于右侧括号才能推入。非常经典的回溯加剪枝。
1/**2 * @param {number} n3 * @return {string[]}4 */5const generateParenthesis = (n) => {6 const res = [];7 const dfs = (str, left, right) => {8 if (left === n && right === n) {9 res.push(str);10 }11 if (left > right) {12 dfs(str + ")", left, right + 1);13 }14 if (left < n) {15 dfs(str + "(", left + 1, right);16 }17 };18 dfs("", 0, 0);19 return res;20};单词搜索
LeetCode 原题链接:79. 单词搜索
谨慎使用fill方法,主要利用深度遍历与标记数组。通过每个数组都尝试遍历。注意空数组不会被Map等方法遍历,需要先fill值再遍历。
1/**2 * @param {character[][]} board3 * @param {string} word4 * @return {boolean}5 */6const exist = (board, word) => {7 const rowLen = board.length;8 const colLen = board[0].length;9 const visited = new Array(rowLen)10 .fill(0)11 .map(() => new Array(colLen).fill(false));12 const dfs = (i, j, k) => {13 if (k === word.length) {14 return true;15 }16 if (i < 0 || i > rowLen - 1 || j < 0 || j > colLen - 1 || visited[i][j]) {17 return;18 }19 if (board[i][j] === word[k]) {20 visited[i][j] = true;21 const flag =22 dfs(i - 1, j, k + 1) ||23 dfs(i + 1, j, k + 1) ||24 dfs(i, j - 1, k + 1) ||25 dfs(i, j + 1, k + 1);26 visited[i][j] = false;27 return flag;28 }29 };30 for (let i = 0; i < rowLen; i++) {31 for (let j = 0; j < colLen; j++) {32 if (dfs(i, j, 0)) {33 return true;34 }35 }36 }37 return false;38};分割回文串
LeetCode 原题链接: 131. 分割回文串
深度遍历加上动态规划。我能说我其实是背下来的吗?🤧
1/**2 * @param {string} s3 * @return {string[][]}4 */5const partition = (s) => {6 const n = s.length,7 ans = [],8 res = [];9 const f = new Array(n).fill(0).map(() => new Array(n).fill(true));10 for (let i = n - 1; i >= 0; i--) {11 for (let j = i + 1; j < n; j++) {12 f[i][j] = s[i] === s[j] && f[i + 1][j - 1];13 }14 }15
16 const step = (i) => {17 if (i === n) {18 res.push([...ans]);19 return;20 }21 for (let j = i; j < n; j++) {22 if (f[i][j]) {23 ans.push(s.slice(i, j + 1));24 step(j + 1);25 ans.pop();26 }27 }28 };29
30 step(0);31 return res;32};N 皇后
LeetCode 原题链接:51. N 皇后
1/**2 * @param {number} n3 * @return {string[][]}4 */5const solveNQueens = (n) => {6 const ans = [];7 const col = new Array(n).fill(0);8 const onPath = new Array(n).fill(false);9 const diag1 = new Array(n * 2 - 1).fill(false);10 const diag2 = new Array(n * 2 - 1).fill(false);11 const dfs = (r) => {12 if (r === n) {13 ans.push(col.map((c) => ".".repeat(c) + "Q" + ".".repeat(n - 1 - c)));14 return;15 }16 for (let c = 0; c < n; c++) {17 if (!onPath[c] && !diag1[r + c] && !diag2[r - c]) {18 col[r] = c;19 onPath[c] = diag1[r + c] = diag2[r - c] = true;20 dfs(r + 1);21 onPath[c] = diag1[r + c] = diag2[r - c] = false; // 恢复现场22 }23 }24 };25 dfs(0);26 return ans;27};28
29/**30 * @param {number} n31 * @return {string[][]}32 */33const solveNQueens = (n) => {34 const arr = new Array(n);35 const res = [];36 const judge = (row) => {37 for (let i = 0; i < row; i++) {38 if (arr[i] === arr[row] || row - i === Math.abs(arr[row] - arr[i]))39 return false;40 }41 return true;42 };43 const step = (count) => {44 if (count === n) {45 const temp = [];46 for (const item of arr) {47 const strArr = new Array(n).fill(".");48 strArr[item] = "Q";49 temp.push(strArr.join(""));50 }51 res.push(temp);52 return;53 }54 for (let i = 0; i < n; i++) {55 arr[count] = i;56 if (judge(count)) {57 step(count + 1);58 }59 }60 };61 step(0);62 return res;63};64
65console.log(solveNQueens(4));二分查找
搜索插入位置
LeetCode 原题链接:35. 搜索插入位置
二分查找的变式,稍作修改,注意ans的初始值需要设置为num.length来处理越界情况。
1/**2 * @param {number[]} nums3 * @param {number} target4 * @return {number}5 */6const searchInsert = (nums, target) => {7 let low = 0,8 high = nums.length - 1,9 ans = nums.length;10 while (low <= high) {11 const mid = Math.floor((low + high) / 2);12 if (target <= nums[mid]) {13 ans = mid;14 high = mid - 1;15 } else {16 low = mid + 1;17 }18 }19 return ans;20};搜索二维矩阵
LeetCode 原题链接:74. 搜索二维矩阵
依旧是二分查找的变式,查找到大于当前数字的一行,然后对当前这一行进行二分查找。另外这里例子是Z字型查找的一个特例,也可使用Z字型查找的方式,更简单。
1/**2 * @param {number[][]} matrix3 * @param {number} target4 * @return {boolean}5 */6const searchMatrix = (matrix, target) => {7 const binarySearchRow = () => {8 let low = 0,9 high = matrix.length - 1,10 ans = 0;11 while (low <= high) {12 const mid = Math.floor((low + high) / 2);13 if (target < matrix[mid][0]) {14 high = mid - 1;15 } else if (target >= matrix[mid][0]) {16 ans = mid;17 low = mid + 1;18 }19 }20 return ans;21 };22 const binarySearchCol = (index) => {23 let low = 0,24 high = matrix[0].length - 1;25 while (low <= high) {26 const mid = Math.floor((low + high) / 2);27 if (target < matrix[index][mid]) {28 high = mid - 1;29 } else if (target > matrix[index][mid]) {30 low = mid + 1;31 } else {32 return true;33 }34 }35 return false;36 };37 const index = binarySearchRow();38 return binarySearchCol(index);39};在排序数组中查找元素的第一个和最后一个位置
LeetCode 原题链接:34. 在排序数组中查找元素的第一个和最后一个位置
二分查找的变式,注意如何查找到第一次出现的元素与最后一次出现的元素,这边采取循环的方式更好写一些。
1/**2 * @param {number[]} nums3 * @param {number} target4 * @return {number[]}5 */6const searchRange = (nums, target) => {7 const binarySearch = (low, high, flag) => {8 if (low > high) {9 return -1;10 }11 const mid = Math.floor((low + high) / 2);12 if (target > nums[mid]) {13 return binarySearch(mid + 1, high, flag);14 } else if (target < nums[mid]) {15 return binarySearch(low, mid - 1, flag);16 } else {17 if (flag) {18 if (mid === 0 || nums[mid - 1] < nums[mid]) {19 return mid;20 } else {21 return binarySearch(low, mid - 1, flag);22 }23 } else {24 if (mid === nums.length - 1 || nums[mid] < nums[mid + 1]) {25 return mid;26 } else {27 return binarySearch(mid + 1, high, flag);28 }29 }30 }31 };32 const left = binarySearch(0, nums.length - 1, true);33 const right = binarySearch(0, nums.length - 1, false);34 return [left, right];35};36
37/**38 * @param {number[]} nums39 * @param {number} target40 * @return {number[]}41 */42const searchRange = (nums, target) => {43 const binarySearch = (flag) => {44 let low = 0,45 high = nums.length - 1;46 while (low <= high) {47 const mid = Math.floor((low + high) / 2);48 if (target > nums[mid]) {49 low = mid + 1;50 } else if (target < nums[mid]) {51 high = mid - 1;52 } else {53 if (flag) {54 if (mid !== 0 && nums[mid - 1] === nums[mid]) {55 high = mid - 1;56 } else {57 return mid;58 }59 } else {60 if (mid !== nums.length - 1 && nums[mid] === nums[mid + 1]) {61 low = mid + 1;62 } else {63 return mid;64 }65 }66 }67 }68 return -1;69 };70 return [binarySearch(true), binarySearch(false)];71};搜索旋转排序数组
LeetCode 原题链接:33. 搜索旋转排序数组
来详细讲解一下这道题目的思路
考虑mid在前区间与后区间的两种情况
- 在前区间:只有满足
target >= nums[0] && target < nums[mid]的情况才有high = mid - 1其他情况都是low = mid + 1。 - 在后区间:只有满足
target > nums[mid] && target <= nums[n - 1]才有low = mid + 1其他情况都是high = mid - 1。 - 注意前区间必须是满足
>=以确定边缘情况。
1/**2 * @param {number[]} nums3 * @param {number} target4 * @return {number}5 */6const search = (nums, target) => {7 const n = nums.length;8 if (n === 0) return -1;9 if (n === 1) return nums[0] === target ? 0 : -1;10 let low = 0,11 high = n - 1;12 while (low <= high) {13 const mid = Math.floor((low + high) / 2);14 if (target === nums[mid]) return mid;15 if (nums[mid] >= nums[0]) {16 if (target >= nums[0] && target < nums[mid]) {17 high = mid - 1;18 } else {19 low = mid + 1;20 }21 } else {22 if (target > nums[mid] && target <= nums[n - 1]) {23 low = mid + 1;24 } else {25 high = mid - 1;26 }27 }28 }29 return -1;30};寻找旋转排序数组中的最小值
LeetCode 原题链接:153. 寻找旋转排序数组中的最小值
1/**2 * @param {number[]} nums3 * @return {number}4 */5const findMin = (nums) => {6 const n = nums.length;7 if (n <= 1) return nums[0];8 if (nums[0] <= nums[n - 1]) return nums[0];9 let low = 0,10 high = n - 1;11 while (low <= high) {12 const mid = Math.floor((low + high) / 2);13 if (nums[mid] < nums[mid - 1]) {14 return nums[mid];15 }16 if (nums[mid] >= nums[0]) {17 low = mid + 1;18 }19 if (nums[mid] < nums[n - 1]) {20 high = mid - 1;21 }22 }23};24
25console.log(findMin([2, 1]));寻找两个正序数组的中位数
LeetCode 原题链接:4. 寻找两个正序数组的中位数
技巧性较强的题目,菜就多练。🦀
1/**2 * @param {number[]} nums13 * @param {number[]} nums24 * @return {number}5 */6const findMedianSortedArrays = (nums1, nums2) => {7 const getKthEl = (nums1, nums2, k) => {8 let index1 = 0,9 index2 = 0;10 while (true) {11 if (index1 === nums1.length) {12 return nums2[index2 + k - 1];13 }14 if (index2 === nums2.length) {15 return nums1[index1 + k - 1];16 }17 if (k === 1) {18 return Math.min(nums1[index1], nums2[index2]);19 }20 const half = Math.floor(k / 2);21 const newIndex1 = Math.min(index1 + half, nums1.length) - 1;22 const newIndex2 = Math.min(index2 + half, nums2.length) - 1;23 if (nums1[newIndex1] <= nums2[newIndex2]) {24 k -= newIndex1 - index1 + 1;25 index1 = newIndex1 + 1;26 } else {27 k -= newIndex2 - index2 + 1;28 index2 = newIndex2 + 1;29 }30 }31 };32 const totalLength = nums1.length + nums2.length;33 if (totalLength % 2 === 1) {34 const midIndex = Math.floor(totalLength / 2);35 return getKthEl(nums1, nums2, midIndex + 1);36 } else {37 const midIndex = totalLength / 2;38 return (39 (getKthEl(nums1, nums2, midIndex) +40 getKthEl(nums1, nums2, midIndex + 1)) /41 242 );43 }44};栈
有效括号
LeetCode 原题链接:20. 有效括号
非常经典的题目,细心点就可以做出来。
1/**2 * @param {string} s3 * @return {boolean}4 */5const isValid = (s) => {6 const stack = [];7 const map = new Map([8 ["(", ")"],9 ["{", "}"],10 ["[", "]"],11 ]);12 for (const char of s) {13 if (stack.length && map.get(stack[stack.length - 1]) === char) {14 stack.pop();15 continue;16 }17 stack.push(char);18 }19 return stack.length ? false : true;20};最小栈
LeetCode 原题链接:155. 最小栈
采用辅助栈进行求解,注意考虑栈为空的边缘情况。
1class MinStack {2 constructor() {3 this.arr = [];4 this.MinStack = [];5 }6 push(el) {7 this.arr.push(el);8 if (9 !this.MinStack.length ||10 el <= this.MinStack[this.MinStack.length - 1]11 ) {12 this.MinStack.push(el);13 }14 }15
16 pop() {17 const el = this.arr.pop();18 if (19 this.MinStack.length &&20 el === this.MinStack[this.MinStack.length - 1]21 ) {22 this.MinStack.pop();23 }24 return el;25 }26
27 top() {28 return this.arr[this.arr.length - 1];29 }30
31 getMin() {32 return this.MinStack[this.MinStack.length - 1];33 }34}字符串解码
LeetCode 原题链接:394. 字符串解码
可采用正则表达式的解法与数字栈与字符栈的解法来计算,类似逆波兰表达式的计算。注意对多位数字的置零处理,和字符串栈的转换。
1const decodeString = (s) => {2 while (s.includes("[")) {3 // 正则匹配每一组编码,其中n为数字,s为字符4 s = s.replace(/(\d+)\[([a-z]+)\]/g, (_, n, s) => s.repeat(n));5 }6 return s;7};8
9const decodeString = (s) => {10 let numStack = []; // 存倍数的栈11 let strStack = []; // 存 待拼接的str 的栈12 let num = 0; // 倍数的“搬运工”13 let result = ""; // 字符串的“搬运工”14 for (const char of s) {15 // 逐字符扫描16 if (!isNaN(char)) {17 // 遇到数字18 num = num * 10 + Number(char); // 算出倍数19 } else if (char == "[") {20 // 遇到 [21 strStack.push(result); // result串入栈22 result = ""; // 入栈后清零23 numStack.push(num); // 倍数num进入栈等待24 num = 0; // 入栈后清零25 } else if (char == "]") {26 // 遇到 ],两个栈的栈顶出栈27 let repeatTimes = numStack.pop(); // 获取拷贝次数28 result = strStack.pop() + result.repeat(repeatTimes); // 构建子串29 } else {30 result += char; // 遇到字母,追加给result串31 }32 }33 return result;34};每日温度
LeetCode 原题链接:739. 每日温度
注意栈中存储的是数组的下标,单调栈一般是遇到大的元素开始做相关的操作。
1/**2 * @param {number[]} temperatures3 * @return {number[]}4 */5const dailyTemperatures = (temperatures) => {6 const stack = [];7 const arr = new Array(temperatures.length).fill(0);8 for (let i = 0; i < temperatures.length; i++) {9 while (10 stack.length &&11 temperatures[i] > temperatures[stack[stack.length - 1]]12 ) {13 const j = stack.pop();14 arr[j] = i - j;15 }16 stack.push(i);17 }18 return arr;19};堆
数组中的第 K 个最大元素
LeetCode 原题链接:215. 数组中的第 K 个最大元素
存在快排和堆两种排序算法,熟能生巧,注意快排的边界判断,菜就多练。🦀
1/**2 * @param {number[]} nums3 * @param {number} k4 * @return {number}5 */6const findKthLargest = (nums, k) => {7 const quickSelect = (l, r) => {8 if (l === r) {9 return nums[nums.length - k];10 }11 let i = l - 1,12 j = r + 1;13 const x = nums[l];14 while (i < j) {15 do {16 i++;17 } while (nums[i] < x);18 do {19 j--;20 } while (nums[j] > x);21 if (i < j) {22 [nums[i], nums[j]] = [nums[j], nums[i]];23 }24 }25 if (nums.length - k <= j) {26 return quickSelect(l, j);27 } else {28 return quickSelect(j + 1, r);29 }30 };31 return quickSelect(0, nums.length - 1);32};33
34/**35 * @param {number[]} nums36 * @param {number} k37 * @return {number}38 */39const findKthLargest = (nums, k) => {40 const buildMaxHeap = (heapSize) => {41 for (let i = Math.floor(heapSize / 2) - 1; i >= 0; i--) {42 maxHeapify(i, heapSize);43 }44 };45 const maxHeapify = (i, heapSize) => {46 const l = i * 2 + 1;47 const r = i * 2 + 2;48 let largest = i;49 if (l < heapSize && nums[l] > nums[largest]) {50 largest = l;51 }52 if (r < heapSize && nums[r] > nums[largest]) {53 largest = r;54 }55 if (i !== largest) {56 swap(i, largest);57 maxHeapify(largest, heapSize);58 }59 };60 const swap = (i, j) => {61 const temp = nums[i];62 nums[i] = nums[j];63 nums[j] = temp;64 };65 let heapSize = nums.length;66 buildMaxHeap(heapSize);67 for (let i = nums.length - 1; i >= nums.length - k + 1; i--) {68 swap(0, i);69 heapSize--;70 maxHeapify(0, heapSize);71 }72 return nums[0];73};贪心
买卖股票的最佳时机
LeetCode 原题链接:121. 买卖股票的最佳时机
贪心算法,每次都找到前一个的最优条件
1/**2 * @param {number[]} prices3 * @return {number}4 */5const maxProfit = (prices) => {6 let maxIn = 0,7 minPrice = prices[0];8 for (let i = 1; i < prices.length; i++) {9 if (minPrice < prices[i]) {10 maxIn = Math.max(maxIn, prices[i] - minPrice);11 } else {12 minPrice = prices[i];13 }14 }15 return maxIn;16};跳跃游戏
LeetCode 原题链接:55. 跳跃游戏
贪心算法,每次在符合条件情况下更新最长的长度。
1/**2 * @param {number[]} nums3 * @return {boolean}4 */5const canJump = (nums) => {6 let head = nums[0];7 for (let i = 1; i < nums.length; i++) {8 if (i <= head) {9 head = Math.max(head, i + nums[i]);10 }11 if (head >= nums.length - 1) {12 return true;13 }14 }15 return head >= nums.length - 1;16};动态规划
爬楼梯
LeetCode 原题链接:70. 爬楼梯
经典问题,甚至让我觉得有点像斐波那契数列。🤔
1/**2 * @param {number} n3 * @return {number}4 */5const climbStairs = (n) => {6 let pre = 1,7 cur = 1;8 for (let i = 1; i < n; i++) {9 const temp = pre + cur;10 pre = cur;11 cur = temp;12 }13 return cur;14};打家劫舍
LeetCode 原题链接:70. 爬楼梯
常规的动态规划题目,注意状态转移方程的书写,注意初始条件的设置,并不影响整体的判断。
1/**2 * @param {number[]} nums3 * @return {number}4 */5const rob = (nums) => {6 if (!nums || nums.length === 0) {7 return 0;8 }9 if (nums.length === 1) {10 return nums[0];11 }12 let first = nums[0],13 second = Math.max(nums[0], nums[1]);14 for (let i = 2; i < nums.length; i++) {15 const temp = second;16 second = Math.max(second, first + nums[i]);17 first = temp;18 }19 return second;20};21
22/**23 * @param {number[]} nums24 * @return {boolean}25 */26const rob = (nums) => {27 if (!nums || nums.length === 0) {28 return 0;29 }30 if (nums.length === 1) {31 return nums[0];32 }33 const sumArr = new Array(nums.length);34 sumArr[0] = nums[0];35 sumArr[1] = Math.max(nums[0], nums[1]);36 for (let i = 2; i < nums.length; i++) {37 sumArr[i] = Math.max(sumArr[i - 1], sumArr[i - 2] + nums[i]);38 }39 return sumArr[sumArr.length - 1];40};单词拆分
LeetCode 原题链接:139. 单词拆分
这边还是有些技巧,首先dp数组的长度为n + 1并且第一项为true,然后状态转移方程如下,值得仔细理解。
1/**2 * @param {string} s3 * @param {string[]} wordDict4 * @return {boolean}5 */6const wordBreak = (s, wordDict) => {7 const set = new Set(wordDict);8 const dp = new Array(s.length + 1).fill(false);9 dp[0] = true;10 for (let i = 1; i < s.length + 1; i++) {11 for (let j = 0; j < i; j++) {12 if (dp[j] && set.has(s.slice(j, i))) {13 dp[i] = true;14 break;15 }16 }17 }18 return dp[dp.length - 1];19};最长递增子序列
LeetCode 原题链接:300. 最长递增子序列
维护一个dp数组,数组中的每个值为当前索引的最长子序列长度,然后动态规划求解。
1/**2 * @param {number[]} nums3 * @return {number}4 */5const lengthOfLIS = (nums) => {6 const dp = new Array(nums.length).fill(1);7 let maxLen = 1;8 for (let i = 1; i < nums.length; i++) {9 let max = 1;10 for (let j = 0; j < i; j++) {11 if (nums[i] > nums[j]) {12 max = Math.max(max, dp[j] + 1);13 }14 }15 dp[i] = max;16 maxLen = Math.max(maxLen, max);17 }18 return maxLen;19};多维动态规划
不同路径
LeetCode 原题链接:62. 不同路径
二维动态规划,也可直接使用一维数组来模仿,重点在于当前节点的次数只会来源于左上。
1/**2 * @param {number} m3 * @param {number} n4 * @return {number}5 */6const uniquePaths = (m, n) => {7 const f = new Array(m).fill(0).map(() => new Array(n).fill(0));8 for (let i = 0; i < m; i++) {9 f[i][0] = 1;10 }11 for (let j = 0; j < n; j++) {12 f[0][j] = 1;13 }14 for (let i = 1; i < m; i++) {15 for (let j = 1; j < n; j++) {16 f[i][j] = f[i - 1][j] + f[i][j - 1];17 }18 }19 return f[m - 1][n - 1];20};21
22/**23 * @param {number} m24 * @param {number} n25 * @return {number}26 */27const uniquePaths = (m, n) => {28 const newArr = new Array(n).fill(1);29 for (let i = 1; i < m; i++) {30 for (let j = 1; j < n; j++) {31 newArr[j] += newArr[j - 1];32 }33 }34 return newArr[n - 1];35};最小路径和
LeetCode 原题链接:64. 最小路径和
相同的动态规划思路,左边和上面哪个更小取哪个,注意需要初始化边缘的行与列。
1/**2 * @param {number[][]} grid3 * @return {number}4 */5const minPathSum = (grid) => {6 const rowLen = grid.length,7 colLen = grid[0].length;8 for (let i = 1; i < colLen; i++) {9 grid[0][i] += grid[0][i - 1];10 }11 for (let i = 1; i < rowLen; i++) {12 grid[i][0] += grid[i - 1][0];13 }14 for (let i = 1; i < rowLen; i++) {15 for (let j = 1; j < colLen; j++) {16 grid[i][j] += Math.min(grid[i - 1][j], grid[i][j - 1]);17 }18 }19 return grid[rowLen - 1][colLen - 1];20};最长回文子串
LeetCode 原题链接:5. 最长回文子串
中心扩散法直接秒,经典无需多言。
1/**2 * @param {string} s3 * @return {string}4 */5const longestPalindrome = (s) => {6 if (s.length <= 1) {7 return s;8 }9 let res = "";10 const helper = (m, n) => {11 while (m >= 0 && n < s.length && s[m] === s[n]) {12 m--;13 n++;14 }15 const temp = s.slice(m + 1, n);16 if (temp.length > res.length) {17 res = temp;18 }19 };20 for (let i = 0; i < s.length; i++) {21 helper(i, i);22 helper(i, i + 1);23 }24 return res;25};技巧
多数元素
LeetCode 原题链接:169. 多数元素
哈希表求解
1/**2 * @param {number[]} nums3 * @return {number}4 */5const majorityElement = (nums) => {6 if (nums.length <= 1) return nums;7 const map = new Map();8 for (const num of nums) {9 if (map.has(num)) {10 const count = map.get(num);11 if (count === Math.floor(nums.length / 2)) {12 return num;13 }14 map.set(num, count + 1);15 } else {16 map.set(num, 1);17 }18 }19};下一个排列
LeetCode 原题链接:31. 下一个排列
从右往左找到较小的一个数,然后找到较大的一个数然后需要尽量靠右,交换后然后反转。
1/**2 * @param {number[]} nums3 * @return {void} Do not return anything, modify nums in-place instead.4 */5const nextPermutation = (nums) => {6 const swap = (i, j) => {7 const temp = nums[i];8 nums[i] = nums[j];9 nums[j] = temp;10 };11
12 const reverse = (l, r) => {13 while (l < r) {14 swap(l++, r--);15 }16 };17
18 let i = nums.length - 2;19 while (i >= 0 && nums[i] >= nums[i + 1]) {20 i--;21 }22 if (i >= 0) {23 let j = nums.length - 1;24 while (j > i && nums[j] <= nums[i]) {25 j--;26 }27 swap(i, j);28 }29 reverse(i + 1, nums.length - 1);30};- 文章作者:
- 希亚的西红柿
- 版权声明:
- 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自希亚的西红柿のBlog!
相关推荐
评论
评论加载中…


