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]01

_解法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 题全)相关推荐

  1. 《LeetCode力扣练习》第31题 下一个排列 Java

    <LeetCode力扣练习>第31题 下一个排列 Java 一.资源 题目: 整数数组的一个 排列 就是将其所有成员以序列或线性顺序排列. 例如,arr = [1,2,3] ,以下这些都可 ...

  2. LeetCode算法入门- Longest Valid Parentheses -day12

    LeetCode算法入门- Longest Valid Parentheses -day12 Given a string containing just the characters '(' and ...

  3. LeetCode算法入门- 3Sum -day9

    LeetCode算法入门- 3Sum -day9 题目描述: Given an array nums of n integers, are there elements a, b, c in nums ...

  4. LeetCode算法入门- String to Integer (atoi)-day7

    LeetCode算法入门- String to Integer (atoi)-day7 String to Integer (atoi): Implement atoi which converts ...

  5. LeetCode算法入门- Implement strStr() -day22

    LeetCode算法入门- Implement strStr() -day22 题目描述 Implement strStr(). Return the index of the first occur ...

  6. LeetCode算法入门- Remove Duplicates from Sorted Array -day21

    LeetCode算法入门- Remove Duplicates from Sorted Array -day21 题目描述 Given a sorted array nums, remove the ...

  7. LeetCode算法入门- Remove Element -day20

    LeetCode算法入门- Remove Element -day20 1. 题目描述 Given an array nums and a value val, remove all instance ...

  8. LeetCode算法入门- Search Insert Position -day19

    LeetCode算法入门- Search Insert Position -day19 题目描述 Given a sorted array and a target value, return the ...

  9. LeetCode算法入门- Multiply Strings -day18

    LeetCode算法入门- Multiply Strings -day18 题目介绍 Given two non-negative integers num1 and num2 represented ...

  10. 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 ...

最新文章

  1. ASP绕过防注入的新思路
  2. 以太坊地址算法php,以太坊ETH源码分析(1):地址生成过程
  3. 1、数列求值 - 2019年第十届蓝桥杯大赛软件类省赛
  4. Golang zip 压缩与解压
  5. mysql安装教程_mysql8.0.20安装教程,mysql下载安装教程8.0.20
  6. Atitit. camel分词器 分词引擎 camel拆分 的实现设计
  7. 切换IP软件,切换电脑手机IP如此简单
  8. 《非暴力沟通》-马歇尔·卢森堡
  9. 039.有符号数除法
  10. 当编程语言都变成女孩子
  11. Web Service简单demo
  12. springboot基于微信小程序“智慧校园” 一体式的设计与实现毕业设计源码091634
  13. 关于蜗牛星际的升级问题!
  14. MBTI职业性格测试小程序
  15. 使用Persimmon UI Builder 开发的一些技巧和注意事项 -- (RT-thread 柿饼UI)
  16. OpenCV读取、保存视频
  17. Linux报错: terminate called after throwing an instanc
  18. Type-c引脚定义
  19. mysql 1701,MySQL ERROR 1701 (42000)
  20. 共享单车项目数据可视化+需求策略分析

热门文章

  1. 终端shell常用命令
  2. LaTeX中TikZ绘图备忘一
  3. 二维码的妙用:通过Zxing实现wifi账号密码分享功能
  4. 《A Metric Learning Reality Check》笔记
  5. 【UE4】视角制作相关知识点(蓝图)
  6. mysql服务器cpu爆满解决办法
  7. http://www.w3school.com.cn/
  8. 【vue设计与实现】解析器 - 解析文本与解码HTML实体
  9. C语言 全局变量和局部变量的特点
  10. 《C陷阱与缺陷》读后感