LeetCode 算法题解集锦 这是一个算法学习笔记,包含了各种常见的算法题解和实现思路。主要涵盖数组、字符串、链表、树结构和动态规划等经典算法类型。
目录
数组算法 数组是最基础的数据结构之一,这里收集了一些常见的数组操作算法题目。
题目列表 旋转图像 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 function rotate (matrix ) { const n = matrix.length ; for (let i = 0 ; i < n; i++) { for (let j = i; j < n; j++) { [matrix[i][j], matrix[j][i]] = [matrix[j][i], matrix[i][j]]; } } for (let i = 0 ; i < n; i++) { for (let j = 0 ; j < n / 2 ; j++) { [matrix[i][j], matrix[i][n - 1 - j]] = [ matrix[i][n - 1 - j], matrix[i][j], ]; } } return matrix; } console .log ( rotate ([ [1 , 2 , 3 ], [4 , 5 , 6 ], [7 , 8 , 9 ], ]) );
寻找最大值 1 2 3 4 5 6 7 8 9 10 11 12 function findMax (arr ) { let max = arr[0 ]; for (let i = 1 ; i < arr.length ; i++) { if (arr[i] > max) { max = arr[i]; } } return max; } console .log (findMax ([1 , 3 , 5 , 7 , 9 ]));
反转数组 1 2 3 4 5 6 7 8 9 10 11 12 13 function reverseArray (arr ) { let left = 0 ; let right = arr.length - 1 ; while (left < right) { [arr[left], arr[right]] = [arr[right], arr[left]]; left++; right--; } return arr; } console .log (reverseArray ([1 , 2 , 3 , 4 , 5 ]));
两数之和 1 2 3 4 5 6 7 8 9 10 11 12 13 14 function twoSum (nums, target ) { const map = new Map (); for (let i = 0 ; i < nums.length ; i++) { const complement = target - nums[i]; if (map.has (complement)) { return [map.get (complement), i]; } map.set (nums[i], i); } return []; } console .log (twoSum ([2 , 7 , 11 , 15 ], 9 ));
检查数组是否包含重复元素 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 var containsDuplicate = function (nums ) { if (nums.length === 1 ) { return false ; } let dp = new Set (); for (let num of nums) { if (dp.has (num)) { return true ; } dp.add (num); } return false ; }; console .log (containsDuplicate ([0 , 4 , 5 , 0 , 3 , 6 ]));
移动零 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 function moveZeroes (nums ) { for (let i = 0 ; i < nums.length - 1 ; i++) { for (let j = i + 1 ; j < nums.length ; j++) { if (nums[i] === 0 && nums[j] !== 0 ) { [nums[i], nums[j]] = [nums[j], nums[i]]; } } } return nums; } console .log (moveZeroes ([0 , 1 , 0 , 3 , 12 ]));
寻找旋转排序数组中的最小值 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 var findMin = function (nums ) { let left = 0 let right = nums.length - 1 while (left < right) { let mid = left + Math .floor ((right - left) / 2 ) if (nums[mid] > nums[right]) { left = mid + 1 } else { right = mid } } return nums[left]; }; console .log (findMin ([3 , 4 , 5 , 1 , 2 ]))
字符串算法 字符串操作是编程中最常见的任务之一,掌握字符串算法对提高编程能力很有帮助。
题目列表 反转字符串 1 2 3 4 5 6 function reverseString (str ) { return str.split ("" ).reverse ().join ("" ); } console .log (reverseString ("hello" ));
判断回文 1 2 3 4 5 6 7 function isPalindrome (str ) { const cleanedStr = str.replace (/[^a-zA-Z0-9]/g , "" ).toLowerCase (); return cleanedStr === cleanedStr.split ("" ).reverse ().join ("" ); } console .log (isPalindrome ("A man, a plan, a canal: Panama" ));
字符串包含 1 2 3 4 5 6 function contains (s, t ) { return s.includes (t); } console .log (contains ("hello world" , "world" ));
链表算法 链表是一种重要的线性数据结构,它的特点是动态性强,插入和删除操作高效。
题目列表 反转链表 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 var reverseList = function (head ) { let prev = null ; while (head) { let next = head.next ; head.next = prev; prev = head; head = next; } return prev; }; console .log ( reverseList ({ val : 1 , next : { val : 2 , next : { val : 3 , next : { val : 4 , next : { val : 5 , next : null } } }, }, }) );
合并两个有序链表 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 function mergeTwoLists (l1, l2 ) { const dummy = new ListNode (0 ); let current = dummy; while (l1 && l2) { if (l1.val < l2.val ) { current.next = l1; l1 = l1.next ; } else { current.next = l2; l2 = l2.next ; } current = current.next ; } current.next = l1 || l2; return dummy.next ; }
删除链表的倒数第 N 个节点 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 var removeNthFromEnd = function (head, n ) { let left = head; let right = head; for (let i = 0 ; i < n; i++) { right = right.next ; } if (!right) return head.next ; while (right.next ) { left = left.next ; right = right.next ; } left.next = left.next .next ; return head; }; console .log ( removeNthFromEnd ( { val : 1 , next : { val : 2 , next : { val : 3 , next : { val : 4 , next : { val : 5 , next : null , }, }, }, }, }, 2 ) );
树结构算法 树是一种非线性数据结构,在解决层次关系问题时非常有用。
题目列表 二叉树的前序遍历 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 function TreeNode (val ) { this .val = val; this .left = this .right = null ; } function preorderTraversal (root ) { const result = []; function traverse (node ) { if (!node) return ; result.push (node.val ); traverse (node.left ); traverse (node.right ); } traverse (root); return result; } const root = new TreeNode (1 );root.right = new TreeNode (2 ); root.right .left = new TreeNode (3 ); console .log (preorderTraversal (root));
二叉树的层序遍历 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 function levelOrder (root ) { const result = []; if (!root) return result; const queue = [root]; while (queue.length ) { const level = []; const size = queue.length ; for (let i = 0 ; i < size; i++) { const node = queue.shift (); level.push (node.val ); if (node.left ) queue.push (node.left ); if (node.right ) queue.push (node.right ); } result.push (level); } return result; }
动态规划 动态规划是解决复杂问题的有效方法,通过将大问题分解为小问题来求解。
爬楼梯 1 2 3 4 5 6 7 8 9 10 11 12 13 14 function climbStairs (n ) { if (n <= 2 ) return n; let first = 1 , second = 2 ; for (let i = 3 ; i <= n; i++) { const temp = first + second; first = second; second = temp; } return second; } console .log (climbStairs (5 ));
最长公共子序列 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 function longestCommonSubsequence (text1, text2 ) { const m = text1.length ; const n = text2.length ; const dp = Array (m + 1 ) .fill (0 ) .map (() => Array (n + 1 ).fill (0 )); for (let i = 1 ; i <= m; i++) { for (let j = 1 ; j <= n; j++) { if (text1[i - 1 ] === text2[j - 1 ]) { dp[i][j] = dp[i - 1 ][j - 1 ] + 1 ; } else { dp[i][j] = Math .max (dp[i - 1 ][j], dp[i][j - 1 ]); } } } return dp[m][n]; } console .log (longestCommonSubsequence ("abcde" , "ace" ));
买卖股票的最佳时机 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 var maxProfit = function (prices ) { let dp = new Array (prices.length ).fill ().map (() => new Array (2 ).fill (0 )); dp[0 ][0 ] = 0 ; dp[0 ][1 ] = -prices[0 ]; for (let i = 1 ; i < prices.length ; i++) { dp[i][0 ] = Math .max (dp[i - 1 ][0 ], dp[i - 1 ][1 ] + prices[i]); dp[i][1 ] = Math .max (dp[i - 1 ][1 ], -prices[i]); } return dp[prices.length - 1 ][0 ]; }; console .log (maxProfit ([7 , 1 , 5 , 3 , 6 , 4 ]));
最大子序和 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 var maxSubArray = function (nums ) { let dp = new Array (nums.length ).fill (0 ); dp[0 ] = nums[0 ]; let max = nums[0 ]; for (let i = 1 ; i < nums.length ; i++) { dp[i] = Math .max (dp[i - 1 ] + nums[i], nums[i]); max = Math .max (max, dp[i]); } return max; }; console .log (maxSubArray ([-2 , 1 , -3 , 4 , -1 , 2 , 1 , -5 , 4 ]));
背包问题 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 function knapspacking (weights, values, capacity ) { let n = weights.length ; let dp = Array (n + 1 ) .fill () .map (() => Array (capacity + 1 ).fill (0 )); for (let i = 1 ; i <= n; i++) { for (let w = 0 ; w <= capacity; w++) { dp[i][w] = dp[i - 1 ][w]; if (w >= weights[i - 1 ]) { dp[i][w] = Math .max ( dp[i][w], dp[i - 1 ][w - weights[i - 1 ]] + values[i - 1 ] ); } } } return dp[n][capacity]; } console .log (knapspacking ([1 , 6 , 10 ], [1 , 2 , 3 ], 10 ));
验证二叉搜索树 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 var isValidBST = function (root ) { if (!root) return true ; if (root.val === null ) return true ; const dfs = (node, min, max ) => { if (!node) return true ; if (min != null && node.val <= min) return false ; if (max != null && node.val >= max) return false ; return dfs (node.left , min, node.val ) && dfs (node.right , node.val , max); }; return dfs (root, null , null ); }; console .log ( isValidBST ({ val : 5 , left : { val : 4 , left : 3 , right : null }, right : { val : 6 , left : 3 , right : 7 }, }) );
给你一个二叉树的根节点 root , 检查它是否轴对称。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 const dfs = (left, right ) => { if (left == null || right == null ) return left == right; if (left.val != right.val ) return false ; return dfs (left.left , right.right ) && dfs (left.right , right.left ); }; console .log ( dfs ( { val : 1 , left : { val : 2 , left : 3 , right : null }, right : { val : 2 , left : null , right : 3 }, }, { val : 1 , left : { val : 2 , left : 3 , right : null }, right : { val : 2 , left : null , right : 3 }, } ) );
有效的括号 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 var isValid = function (s ) { let queue = []; for (let i = 0 ; i < s.length ; i++) { if (s[i] === "(" || s[i] === "[" || s[i] === "{" ) { queue.push (s[i]); } else { let last = queue.pop (); if ( (last === "(" && s[i] === ")" ) || (last === "[" && s[i] === "]" ) || (last === "{" && s[i] === "}" ) ) { continue ; } else { return false ; } } } return queue.length === 0 ; }; console .log (isValid ("()[]{}" ));
打家劫舍 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 var rob = function (nums ) { if (nums.length === 1 ) { return nums[0 ]; } let dp = new Array (nums.length ).fill (0 ); dp[0 ] = nums[0 ]; dp[1 ] = Math .max (nums[0 ], nums[1 ]); for (let i = 2 ; i < nums.length ; i++) { dp[i] = Math .max (dp[i - 1 ], dp[i - 2 ] + nums[i]); } return dp[nums.length - 1 ]; }; console .log (rob ([1 , 2 , 3 , 1 ]));
将有序数组转换为二叉搜索树 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 var sortedArrayToBST = function (nums ) { const buildBST = (left, right ) => { if (left > right) return null ; const mid = Math .floor ((left + right) / 2 ); const root = new TreeNode (nums[mid]); root.left = buildBST (left, mid - 1 ); root.right = buildBST (mid + 1 , right); return root; }; function TreeNode (val, left, right ) { this .val = val === undefined ? 0 : val; this .left = left === undefined ? null : left; this .right = right === undefined ? null : right; } return buildBST (0 , nums.length - 1 ); }; console .log (sortedArrayToBST ([-10 , -3 , 0 , 5 , 9 ]));
洗牌算法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 const ShuffleArray = function (nums ) { this .original = nums; }; ShuffleArray .prototype .shuffle = function ( ) { let nums = this .original .slice (); for (let i = nums.length - 1 ; i > 0 ; i--) { const j = Math .floor (Math .random () * (i + 1 )); [nums[i], nums[j]] = [nums[j], nums[i]]; } return nums; }; const shuffleArray = new ShuffleArray ([1 , 2 , 3 , 4 , 5 ]);console .log (shuffleArray.shuffle ());
归并排序 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 const mergeSort = function (arr ) { if (arr.length <= 1 ) { return arr; } let mid = Math .floor (arr.length /2 ); let left = arr.slice (0 , mid); let right = arr.slice (mid); return merge (mergeSort (left), mergeSort (right)); } const merge = function (left, right ) { let result = []; let leftIndex = 0 ; let rightIndex = 0 ; while (leftIndex < left.length && rightIndex < right.length ) { if (left[leftIndex] <= right[rightIndex]) { result.push (left[leftIndex]); leftIndex++; } else { result.push (right[rightIndex]); rightIndex++; } } return result.concat (left.slice (leftIndex)).concat (right.slice (rightIndex)); }
快速排序 const quickSort = function(arr, left = 0, right = arr.length - 1) {
if (left < right) {
const pivotIndex = partition(arr, left, right);
quickSort(arr, left, pivotIndex - 1);
quickSort(arr, pivotIndex + 1, right);
}
return arr;
}
const partition = function(arr, left, right) {
const pivot = arr[right]; // 选择最右边的元素作为基准
let i = left - 1; // 小于pivot的元素的边界
for (let j = left; j < right; j++) {
if (arr[j] <= pivot) {
i++;
[arr[i], arr[j]] = [arr[j], arr[i]]; // 交换元素
}
}
// 将pivot放到正确的位置
[arr[i + 1], arr[right]] = [arr[right], arr[i + 1]];
return i + 1;
}