LeetCode《算法入门》刷题笔记(31 题全)
LeetCode《算法入门》刷题笔记(31 题全)
- 二分查找
- 1. 二分查找
- _解法1:二分搜索(迭代)
- 解法2:二分搜索(递归)
- 2. 第一个错误的版本
- _解法1:二分
- 3. 搜索插入位置
- _解法1:二分
- 双指针
- 4. 有序数组的平方
- _解法1:map + sort
- 解法2:双指针
- 5. 轮转数组
- _解法1:额外空间
- 解法2:JS API
- 解法3:反转 3 次数组
- 6. 移动零
- 解法1:双指针
- 7. 两数之和 II - 输入有序数组
- _解法1:map
- 解法2:双指针
- _解法3:暴力 + 二分
- 8. 反转字符串
- _解法1:双指针
- 解法2:递归
- 9. 反转字符串中的单词 III
- _解法1:高阶函数 API
- 10. 链表的中间结点
- _解法1:哈希
- _解法2:快慢指针
- 11. 删除链表的倒数第 N 个结点
- _解法1:双指针
- 解法2:递归
- 滑动窗口
- 12. 无重复的最长子串
- 解法1:滑动窗口
- 13. 字符串的排列
- 解法1:滑动窗口 + 字典
- DFS / BFS
- 14. 图像渲染
- 解法1:DFS
- 解法2:BFS
- 15. 岛屿的最大面积
- _解法1:DFS
- 16. 合并二叉树
- _解法1:递归
- 17. 填充每个节点的下一个右侧节点指针
- _解法1:递归
- 18. 0 1 矩阵**
- 解法1:BFS
- 解法2:DFS
- 19. 腐烂的橘子**
- 解法1:BFS
- 递归 / 回溯
- 20. 合并两个有序链表
- _解法1:递归
- 解法2:迭代
- 21. 反转链表
- 解法1:递归
- 解法2:迭代
- 22. 组合**
- 解法1:回溯
- 23. 全排列**
- _解法1:回溯
- 24. 字母大小写全排列*
- 解法1:回溯
- 动态规划
- 25.爬楼梯
- 我的题解:递归、记忆化递归 、动态规划
- 26. 打家劫舍
- 我的题解
- 27. 三角形的最小路径和
- 我的题解:动态规范、递归
- 位运算
- 28. 2的幂
- 我的题解
- 29. 位1的个数
- 我的题解
- 30. 颠倒二进制位
- 我的题解:API、位运算
- 31. 只出现一次的数字
- 我的题解:位运算
二分查找
1. 二分查找
题目:704. 二分查找
_解法1:二分搜索(迭代)
/*** @param {number[]} nums* @param {number} target* @return {number}*/
var search = function (nums, target) {return binarySearch(nums, target, 0, nums.length - 1)
};/*** 在 nums 的 [left, right] 中搜索 target, 返回搜索到值的索引* @param {number[]} nums 执行搜索的目标数组* @param {number} target 搜索的目标数* @param {number} left 搜索的左边界* @param {number} right 搜索的右边界* @return {number} 目标数的索引, 不存在返回 -1*/
var binarySearch = function (nums, target, left, right) {while (left <= right) {let mid = (left + right) >> 1if (nums[mid] > target)right = mid - 1else if (nums[mid] < target)left = mid + 1else return mid}return -1
}
解法2:二分搜索(递归)
var search = function (nums, target) {return binarySearch(nums, target, 0, nums.length - 1)
};var binarySearch = function (nums, target, left, right) {if (left > right) return -1let mid = (left + right) >> 1if (nums[mid] == target) return mid;return nums[mid] < target? binarySearch(nums, target, mid + 1, right): binarySearch(nums, target, left, mid - 1)
}
2. 第一个错误的版本
题目:278. 第一个错误的版本
_解法1:二分
~~
是 js 中 浮点数 转 整数 最快的方法:console.log(~~6.95) // 最快
/*** @param {function} isBadVersion()* @return {function}*/
var solution = function (isBadVersion) {/*** @param {integer} n Total versions* @return {integer} The first bad version*/return function (n) {let left = 1, right = nwhile (left < right) {// 浮点数 --> 整数let mid = ~~(left + (right - left) / 2)isBadVersion(mid) ? right = mid : left = mid + 1}return left};
};
3. 搜索插入位置
题目:35. 搜索插入位置
_解法1:二分
/*** @param {function} isBadVersion()* @return {function}*/
var solution = function (isBadVersion) {/*** @param {integer} n Total versions* @return {integer} The first bad version*/return function (n) {let left = 1, right = nwhile (left < right) {let mid = ~~(left + (right - left) / 2)isBadVersion(mid) ? right = mid : left = mid + 1}return left};
};
双指针
4. 有序数组的平方
题目:977. 有序数组的平方
_解法1:map + sort
/*** https://leetcode-cn.com/problems/squares-of-a-sorted-array/* 有序数组的平方* @param {number[]} nums* @return {number[]}*/
var sortedSquares = function (nums) {return nums.map(n => n * n).sort((a, b) => a - b)
};
解法2:双指针
var sortedSquares = function (nums) {let left = 0, right = nums.length - 1, idx = rightlet res = new Array(nums.length)while (left <= right) {if (Math.pow(nums[left], 2) > Math.pow(nums[right], 2))res[idx--] = Math.pow(nums[left++], 2)elseres[idx--] = Math.pow(nums[right--], 2)}return res
};
5. 轮转数组
题目:189. 轮转数组
_解法1:额外空间
/*** https://leetcode-cn.com/problems/rotate-array/* 轮转数组* @param {number[]} nums* @param {number} k* @return {void} Do not return anything, modify nums in-place instead.*/
var rotate = function (nums, k) {let len = nums.lengthlet res = new Array(len)// 计算好索引, 放到新数组for (let i = 0; i < len; i++)res[(i + k) % len] = nums[i]// 修改传入的数组for (let i = 0; i < len; i++)nums[i] = res[i]
};
以上写法中,“修改传入的数组” 这步可以利用 JS API 简化:
var rotate = function (nums, k) {let len = nums.lengthlet res = new Array(len)for (let i = 0; i < len; i++)res[(i + k) % len] = nums[i]nums.splice(0, len, ...res)
};
解法2:JS API
var rotate = function (nums, k) {nums.splice(0, 0, ...nums.splice(-(k % nums.length)))
};var rotate = function (nums, k) {const len = nums.lengthnums.unshift(...nums.splice(len - k % len))
};
解法3:反转 3 次数组
思路:
nums = "----->-->"; k =3
result = "-->----->";reverse "----->-->" we can get "<--<-----"
reverse "<--" we can get "--><-----"
reverse "<-----" we can get "-->----->"
/*** 反转 3 次数组*/
var rotate = function (nums, k) {k %= nums.lengthreverse(nums, 0, nums.length - 1)reverse(nums, 0, k - 1)reverse(nums, k, nums.length - 1)
}/*** 反转数组*/
let reverse = (nums, start, end) => {while (start < end)[nums[start++], nums[end--]] = [nums[end], nums[start]]
}
6. 移动零
题目:283. 移动零 - 力扣(LeetCode) (leetcode-cn.com)
解法1:双指针
常规思路:
- 遍历时将整数全部移动到前面, 剩下的补 0
- left 记录不为 0 的数字个数
var moveZeroes = function (nums) {let left = 0, right = 0while (right < nums.length) {if (nums[right] != 0)nums[left++] = nums[right]right++}while (left < nums.length)nums[left++] = 0
};
优化成一轮循环:
- 遍历时 right 遇到不为 0 的数字,就和前面为 0 的 left 交换
- 遇到为 0 的数字,left 不动,right 继续往前
var moveZeroes = function (nums) {let left = 0, right = 0while (right < nums.length) {if (nums[right] != 0) {if (nums[left] == 0) {[nums[left], nums[right]] = [nums[right], nums[left]]}left++}right++}
};
7. 两数之和 II - 输入有序数组
题目:两数之和 II - 输入有序数组
提示:
2 <= numbers.length <= 3 * 104
-1000 <= numbers[i] <= 1000
numbers
按 非递减顺序 排列-1000 <= target <= 1000
- 仅存在一个有效答案
_解法1:map
题目要求常量级的额外空间,因此这种做法不符合题意
思路:
- 通过一次遍历,建立 numbers 中元素 和 对应索引 的 映射
- 再次遍历,寻找
target - numbers[i]
是否在 map 的 key 中
/*** @param {number[]} numbers* @param {number} target* @return {number[]}*/
var twoSum = function (numbers, target) {let map = new Map()numbers.forEach((val, i) => map.set(val, i))for (let i = 0; i < numbers.length; i++)if (map.has(target - numbers[i]))return [i + 1, map.get(target - numbers[i]) + 1]return []
};
解法2:双指针
思路:
- 关键在于输入的是有序数组,可以定义两个指针,分别指向首尾
- 计算两个指针指向元素和,大了就
right--
,小了就left++
var twoSum = function (numbers, target) {let left = 0, right = numbers.length - 1while (left < right) {let sum = numbers[left] + numbers[right]if (sum == target) return [left + 1, right + 1]else if (sum > target) right--;else left++;}return []
};
_解法3:暴力 + 二分
/*** 二分*/
var twoSum = function (numbers, target) {for (let i = 0; i < numbers.length; i++) {let idx = leftBoundBinarySearch(numbers, target - numbers[i], i + 1, numbers.length - 1)if (idx !== -1) return [i + 1, idx + 1]}return []
};/*** 寻找左边界的二分搜索*/
const leftBoundBinarySearch = (nums, target, left, right) => {while (left <= right) {let mid = (left + right) >> 1if (nums[mid] >= target)right = mid - 1else if (nums[mid] < target)left = mid + 1}if (left >= nums.length || nums[left] != target)return -1return left
}
8. 反转字符串
题目:344. 反转字符串
正常都是使用库函数:
var reverseString = function (s) {s.reverse() };
_解法1:双指针
/*** @param {character[]} s* @return {void} Do not return anything, modify s in-place instead.*/
var reverseString = function (s) {let left = 0, right = s.length - 1while (left <= right)[s[left++], s[right--]] = [s[right], s[left]]
}
解法2:递归
var reverseString = function (s) {var help = (s, left, right) => {if (left >= right) return[s[left], s[right]] = [s[right], s[left]]help(s, ++left, --right)}help(s, 0, s.length - 1)
};
9. 反转字符串中的单词 III
题目:557. 反转字符串中的单词 III
_解法1:高阶函数 API
/*** @param {string} s* @return {string}*/
var reverseWords = function (s) {return s.split(" ").map(s => s.split("").reverse().join("")).join(" ")
};
10. 链表的中间结点
题目:876. 链表的中间结点
_解法1:哈希
var middleNode = function (head) {let map = new Map(), idx = 0while (head) {map.set(idx++, head)head = head.next}return map.get(Math.ceil(--idx / 2))
};
_解法2:快慢指针
/*** 快慢指针*/
var middleNode = function (head) {let slow = head, fast = headwhile (fast.next) {slow = slow.nextfast = fast.nextfast.next && (fast = fast.next)}return slow
};/*** 快慢指针*/
var middleNode = function (head) {let slow = head, fast = headwhile (fast && fast.next) {slow = slow.nextfast = fast.next.next}return slow
};
11. 删除链表的倒数第 N 个结点
题目:19. 删除链表的倒数第 N 个结点
提示:
- 链表中结点的数目为
sz
1 <= sz <= 30
0 <= Node.val <= 100
1 <= n <= sz
_解法1:双指针
*/
var removeNthFromEnd = function (head, n) {if (!head.next) return nulllet slow = head, fast = headwhile (n--)fast = fast.nextif (!fast) return head.nextwhile (fast && fast.next) {slow = slow.nextfast = fast.next}slow.next = slow.next.nextreturn head
};
/*** @param {ListNode} head* @param {number} n* @return {ListNode}*/
var removeNthFromEnd = function (head, n) {if (!head.next) return nulllet slow = head, fast = headwhile (n--)fast = fast.nextif (!fast) return head.nextwhile (fast && fast.next) {slow = slow.nextfast = fast.next}slow.next = slow.next.nextreturn head
};
解法2:递归
Java 可以跑通,JS 就不能跑通,有点奇怪。。
class Solution {int cur = 0;public ListNode removeNthFromEnd(ListNode head, int n) {if (head == null) return null;head.next = removeNthFromEnd(head.next, n);cur++;if (cur == n) return head.next;return head;}
}
滑动窗口
12. 无重复的最长子串
题目:3. 无重复字符的最长子串
解法1:滑动窗口
var lengthOfLongestSubstring = function (s) {let set = new Set(), max = 0let left = 0, right = 0while (right < s.length) {let c = s[right]while (set.has(c))set.delete(s[left++])set.add(s[right])max = Math.max(max, right - left + 1)right++}return max
};
13. 字符串的排列
题目:567. 字符串的排列
解法1:滑动窗口 + 字典
class Solution {public boolean checkInclusion(String s1, String s2) {// 不可能的情况int m = s1.length(), n = s2.length();if (m > n) return false;// cnt 数组: 记录 s1 中字母出现的次数// cur 数组: 记录滑动的窗口中的字母出现的次数int[] cnt = new int[26], cur = new int[26];for (char c : s1.toCharArray())cnt[c - 'a']++;for (int i = 0; i < m; i++)cur[s2.charAt(i) - 'a']++;for (int i = m; i < n; i++) {cur[s2.charAt(i) - 'a']++;cur[s2.charAt(i - m) - 'a']--;if (Arrays.equals(cnt, cur)) return true;}return false;}
}
var checkInclusion = function (s1, s2) {let m = s1.length, n = s2.lengthif (m > n) return falselet cnt1 = new Array(26).fill(0), cnt2 = new Array(26).fill(0)for (let c of s1)cnt1[c.charCodeAt() - 'a'.charCodeAt()]++for (let i = 0; i < m; i++)cnt2[s2[i].charCodeAt() - 'a'.charCodeAt()]++if (check(cnt1, cnt2)) return truefor (let i = m; i < n; i++) {cnt2[s2[i].charCodeAt() - 'a'.charCodeAt()]++cnt2[s2[i - m].charCodeAt() - 'a'.charCodeAt()]--if (check(cnt1, cnt2)) return true}return false
}let check = (nums1, nums2) => {for (let i = 0; i < nums1.length; i++)if (nums1[i] != nums2[i])return falsereturn true
}
DFS / BFS
14. 图像渲染
题目:733. 图像渲染
提示:
m == image.length
n == image[i].length
1 <= m, n <= 50
0 <= image[i][j], newColor < 216
0 <= sr < m
0 <= sc < n
解法1:DFS
class Solution {public int[][] floodFill(int[][] image, int sr, int sc, int newColor) {dfs(image, sr, sc, newColor, image[sr][sc]);return image;}/*** @param image 二维数组* @param sr 当前行* @param sc 当前列* @param newColor 新颜色* @param originColor 旧颜色*/void dfs(int[][] image, int sr, int sc, int newColor, int originColor) {// 越界, 停止遍历if (sr < 0 || sr >= image.length || sc < 0 || sc >= image[0].length) return;// 不满足上色要求, 或是已经上色过, 停止遍历if (image[sr][sc] != originColor || image[sr][sc] == newColor) return;// 上色image[sr][sc] = newColor;dfs(image, sr - 1, sc, newColor, originColor);dfs(image, sr, sc + 1, newColor, originColor);dfs(image, sr + 1, sc, newColor, originColor);dfs(image, sr, sc - 1, newColor, originColor);}
}
const floodFill = function (image, sr, sc, newColor) {dfs(image, sr, sc, newColor, image[sr][sc])return image
};const dfs = (image, sr, sc, newColor, originColor) => {// 越界if (sr < 0 || sr >= image.length || sc < 0 || sc >= image[0].length) return// 不满足上色要求, 或已经上色过if (image[sr][sc] != originColor || image[sr][sc] == newColor) return;// 上色image[sr][sc] = newColordfs(image, sr - 1, sc, newColor, originColor)dfs(image, sr, sc + 1, newColor, originColor)dfs(image, sr + 1, sc, newColor, originColor)dfs(image, sr, sc - 1, newColor, originColor)
}
解法2:BFS
class Solution {int[] dx = { 1, 0, 0, -1 };int[] dy = { 0, 1, -1, 0 };public int[][] floodFill(int[][] image, int sr, int sc, int newColor) {// 保存初始颜色int originColor = image[sr][sc];// 如果初始颜色和新上的颜色相同, 直接返回原数组if (originColor == newColor) return image;// 初始位置入队Queue<int[]> queue = new LinkedList<>() {{ offer(new int[] { sr, sc }); }};// 上色image[sr][sc] = newColor;while (!queue.isEmpty()) {int[] cell = queue.poll();int x = cell[0], y = cell[1];for (int i = 0; i < 4; i++) {int mx = x + dx[i], my = y + dy[i];// 没有越界, 并且还未上过色, 才继续搜索if (mx >= 0 && mx < image.length && my >= 0 && my < image[0].length&& image[mx][my] == originColor) {queue.offer(new int[] { mx, my });image[mx][my] = newColor;}}}return image;}
}
15. 岛屿的最大面积
题目:695. 岛屿的最大面积
提示:
m == grid.length
n == grid[i].length
1 <= m, n <= 50
grid[i][j]
为0
或1
_解法1:DFS
使用全局变量:
let area = 0
var maxAreaOfIsland = function (grid) {let max = -1for (let i = 0; i < grid.length; i++) {for (let j = 0; j < grid[0].length; j++) {area = 0dfs(grid, i, j)max = Math.max(max, area)}}return max
};const dfs = (grid, cr, cc) => {// 越界if (cr < 0 || cr >= grid.length || cc < 0 || cc >= grid[0].length) return// 已访问过, 或者不是岛屿if (grid[cr][cc] === -1 || grid[cr][cc] === 0) return// 面积+1, 并标记已经访问area++grid[cr][cc] = -1dfs(grid, cr - 1, cc) // 上dfs(grid, cr, cc + 1) // 右dfs(grid, cr + 1, cc) // 下dfs(grid, cr, cc - 1) // 左
}
不使用全局变量:
var maxAreaOfIsland = function (grid) {let max = -1for (let i = 0; i < grid.length; i++)for (let j = 0; j < grid[0].length; j++)max = Math.max(max, dfs(grid, i, j))return max
};const dfs = (grid, cr, cc) => {// 越界if (cr < 0 || cr >= grid.length || cc < 0 || cc >= grid[0].length) return 0// 已访问过, 或者不是岛屿if (grid[cr][cc] === -1 || grid[cr][cc] === 0) return 0// 面积+1, 并标记已经访问grid[cr][cc] = -1let area = 1area += dfs(grid, cr - 1, cc) // 上area += dfs(grid, cr, cc + 1) // 右area += dfs(grid, cr + 1, cc) // 下area += dfs(grid, cr, cc - 1) // 左return area
}
16. 合并二叉树
题目:617. 合并二叉树
_解法1:递归
/*** @param {TreeNode} root1* @param {TreeNode} root2* @return {TreeNode}*/
var mergeTrees = function (root1, root2) {if (root1 && !root2) return root1if (!root1 && root2) return root2if (!root1 && !root2) return nulllet root = new TreeNode(root1.val + root2.val)root.left = mergeTrees(root1.left, root2.left)root.right = mergeTrees(root1.right, root2.right)return root
}
17. 填充每个节点的下一个右侧节点指针
题目:116. 填充每个节点的下一个右侧节点指针
提示:
- 树中节点的数量在 [0, 212 - 1] 范围内
-1000 <= node.val <= 1000
进阶:
- 你只能使用常量级额外空间。
- 使用递归解题也符合要求,本题中递归程序占用的栈空间不算做额外的空间复杂度。
_解法1:递归
var connect = function (root) {if (!root) return nullif (root.left) {// 处理下一层root.left.next = root.right// 循环处理后面的层let tmp1 = root.left, tmp2 = root.rightwhile (tmp1.right) {tmp1.right.next = tmp2.lefttmp1 = tmp1.righttmp2 = tmp2.left}}connect(root.left)connect(root.right)return root
}
评论区看到更简洁的递归:
var connect = function (root) {if (!root) return nullif (root.left) {root.left.next = root.rightif (root.next)root.right.next = root.next.left}connect(root.left)connect(root.right)return root
}
18. 0 1 矩阵**
题目:542. 01 矩阵
解法1:BFS
题解:2种BFS,详解DP,
LeetCode《算法入门》刷题笔记(31 题全)相关推荐
- 《LeetCode力扣练习》第31题 下一个排列 Java
<LeetCode力扣练习>第31题 下一个排列 Java 一.资源 题目: 整数数组的一个 排列 就是将其所有成员以序列或线性顺序排列. 例如,arr = [1,2,3] ,以下这些都可 ...
- LeetCode算法入门- Longest Valid Parentheses -day12
LeetCode算法入门- Longest Valid Parentheses -day12 Given a string containing just the characters '(' and ...
- LeetCode算法入门- 3Sum -day9
LeetCode算法入门- 3Sum -day9 题目描述: Given an array nums of n integers, are there elements a, b, c in nums ...
- LeetCode算法入门- String to Integer (atoi)-day7
LeetCode算法入门- String to Integer (atoi)-day7 String to Integer (atoi): Implement atoi which converts ...
- LeetCode算法入门- Implement strStr() -day22
LeetCode算法入门- Implement strStr() -day22 题目描述 Implement strStr(). Return the index of the first occur ...
- LeetCode算法入门- Remove Duplicates from Sorted Array -day21
LeetCode算法入门- Remove Duplicates from Sorted Array -day21 题目描述 Given a sorted array nums, remove the ...
- LeetCode算法入门- Remove Element -day20
LeetCode算法入门- Remove Element -day20 1. 题目描述 Given an array nums and a value val, remove all instance ...
- LeetCode算法入门- Search Insert Position -day19
LeetCode算法入门- Search Insert Position -day19 题目描述 Given a sorted array and a target value, return the ...
- LeetCode算法入门- Multiply Strings -day18
LeetCode算法入门- Multiply Strings -day18 题目介绍 Given two non-negative integers num1 and num2 represented ...
- LeetCode算法入门- Remove Nth Node From End of List -day17
LeetCode算法入门- Remove Nth Node From End of List -day17 题目解释: Given a linked list, remove the n-th nod ...
最新文章
- ASP绕过防注入的新思路
- 以太坊地址算法php,以太坊ETH源码分析(1):地址生成过程
- 1、数列求值 - 2019年第十届蓝桥杯大赛软件类省赛
- Golang zip 压缩与解压
- mysql安装教程_mysql8.0.20安装教程,mysql下载安装教程8.0.20
- Atitit. camel分词器 分词引擎 camel拆分 的实现设计
- 切换IP软件,切换电脑手机IP如此简单
- 《非暴力沟通》-马歇尔·卢森堡
- 039.有符号数除法
- 当编程语言都变成女孩子
- Web Service简单demo
- springboot基于微信小程序“智慧校园” 一体式的设计与实现毕业设计源码091634
- 关于蜗牛星际的升级问题!
- MBTI职业性格测试小程序
- 使用Persimmon UI Builder 开发的一些技巧和注意事项 -- (RT-thread 柿饼UI)
- OpenCV读取、保存视频
- Linux报错: terminate called after throwing an instanc
- Type-c引脚定义
- mysql 1701,MySQL ERROR 1701 (42000)
- 共享单车项目数据可视化+需求策略分析