《编程能力基础》刷题笔记

  • 1. 单调数列
    • 题解:递归、模拟、API
  • 2. 实现 strStr()
    • 题解:API、暴力、滑动窗口
  • 3. 平衡二叉树
    • 题解:迭代
  • 4. 重复的子字符串
    • 题解:模拟、技巧
  • 5. 逆波兰表达式求值
    • 题解:用栈模拟、用数组模拟
  • 6. 加一
    • 题解:分情况模拟及其优化
  • 7. 二叉树中的列表
    • 题解:递归
  • 8. 字符串相乘
  • 9. 二进制求和
    • 题解 :模拟
  • 10. 数组形式的整数加法
    • 题解:模拟
  • 11. 每日温度
    • 题解:暴力、单调 栈
  • 12.最后一个单词长度
    • 题解:API
  • 13. 旋转矩阵
    • 题解:模拟 + 技巧
  • 14. 判断矩阵经轮转后是否一致
    • 题解:模拟
  • 15. 螺旋矩阵
    • 题解:模拟
  • 16. 最接近原点的K个点
    • 题解:数组排序、优先级队列
  • 17. 等差子数组
    • 题解:暴力模拟
  • 18. N 叉树的层次遍历
    • 题解:BFS
  • 19. 下一个更大元素 II
    • 题解:暴力、单调栈
  • 20. 下一个更大元素 III
    • 题解:排序模拟
  • 21. 通知所有员工所需的时间 TODO
  • 22. 字母异位词分组
    • 题解:哈希
  • 23. 找到字符串中所有字母异位词 todo
    • 题解
  • 24. 乘积小于K的子数组 to do
    • 题解
  • 25. 二维区域和检索 - 矩阵不可变
    • 题解:前缀和
  • 26. 最小差值 II
    • 题解
  • 27. 重排链表
    • 题解:模拟、拆分 + 拼接
  • 28. 复制带随机指针的链表
    • 题解:哈希
  • 29. 两数相加
    • 题解:模拟
  • 30. 两数相加 II
    • 题解:反转链表3次、栈
  • 31. 旋转链表
    • 题解:往后拼接
  • 32. 二叉搜索树迭代器
  • 33. 座位预约管理系统
    • 题解:优先级队列
  • 34. 柠檬水找零
    • 题解:贪心 + 模拟
  • 35. 最小栈
    • 题解:优先级队列、双栈、链表
  • 36. 扁平化嵌套列表迭代器
    • 题解:递归扁平化
  • 37. 设计一个验证系统
    • 题解:读题模拟
  • 38. 设计链表
    • 题解:基础功
  • 39. O(1) 时间插入、删除和获取随机元素
    • 题解:List 与 Map
  • 40. 设计循环链表
    • 题解:基础功
  • 41. 我的日程安排表 I
    • 题解:暴力模拟

刷题代码:https://gitee.com/szluyu99/my-leet-code

1. 单调数列

题目:896. 单调数列

题解:递归、模拟、API

因为题目看着让人很想递归,先送上个递归做法:(强行递归,必定超时)

/*** 递归(超时)*/
function isMonotonic(nums: number[]): boolean {return nums[0] < nums[nums.length - 1] ?isMonotonicIncrease(nums) : isMonotonicDecrease(nums)
};// 递归判断是否单调递
const isMonotonicIncrease = function (nums: number[]): boolean {if (nums.length == 1) return trueif (nums[0] > nums[1]) return falsereturn isMonotonicIncrease(nums.slice(1)) ? true : false
}// 递归判断是否单调减
const isMonotonicDecrease = function (nums: number[]): boolean {if (nums.length == 1) return trueif (nums[0] < nums[1]) return falsereturn isMonotonicDecrease(nums.slice(1)) ? true : false
}

然后才是正文。

解法1

  • 通过 nums[0]nums[1] 可以判断这个序列是 递增 还是 递减
  • 明确了目标,然后只需要遍历时两两比较即可,遇到不满足条件的情况直接返回 false
/**
* 通过 nums[0] 和 nums[nums.length - 1] 判断出增减方向
* 遍历数组一次,两两比较
*/
public boolean isMonotonic1(int[] nums) {// 先判断 递增 还是 递减if (nums[0] <= nums[nums.length - 1]) { // 递增for (int i = 1; i < nums.length; i++) if (nums[i - 1] > nums[i]) return false;} else { // 递减for (int i = 1; i < nums.length; i++) if (nums[i - 1] < nums[i]) return false;}return true;
}

解法2

  • 如果没有先判断出序列是 递增 还是 递减,也没关系
  • 可以先假设又递增又递减,然后判断时如果遇到一个不满足条件的情况就可以排除这个假设了
  • 中间如果加上一步减枝(可以快 1s)
public boolean isMonotonic(int[] nums) {boolean inc = true, dec = true;for (int i = 0; i < nums.length - 1; i++) {if (nums[i] > nums[i + 1]) inc = false;if (nums[i] < nums[i + 1]) dec = false;// 剪枝操作, 已经明确既不递增也不递减, 直接返回 falseif (!inc && !dec) return false;}return inc || dec;
}

解法3:然后就是利用语言特性和 API 的做法了,比如 JS

var isMonotonic = function (nums) {return isSorted(nums) || isSorted(nums.reverse())
}
// 判断是否单调增
function isSorted(nums) {return nums.slice(1).every((item, i) => nums[i] <= item)
}

2. 实现 strStr()

题目:28. 实现 strStr()

题解:API、暴力、滑动窗口

先来一个每个人必定会尝试一下的解法:库函数

public int strStr(String haystack, String needle) {return haystack.indexOf(needle);
}

再来个比较常规但是会超时的暴力解法:暴力

public int strStr1(String haystack, String needle) {if (haystack.length() < needle.length()) return -1;if (needle.isEmpty()) return 0;for (int i = 0; i < haystack.length(); i++) {int tmpIdx = 0;while (i + tmpIdx < haystack.length()&& haystack.charAt(i + tmpIdx) == needle.charAt(tmpIdx++)) if (tmpIdx == needle.length()) return i;}return -1;
}

然后来个可以正常通过的解法:滑动窗口

public int strStr2(String haystack, String needle) {if (haystack.length() < needle.length()) return -1;// 窗口长度int width = needle.length();// 当前索引int idx = 0;// 只访问不会越界的部分while (idx + width <= haystack.length()) {// 找到则返回索引if (needle.equals(haystack.substring(idx, idx + width)))return idx;idx++; }return -1;
}

至于其他比较强力的算法如 KMP 等等,待我功力深厚以后再回来将其拿下!

3. 平衡二叉树

题目:110. 平衡二叉树

题解:迭代

解法1:常规思路,先写一个 获取二叉树节点高度 的函数,然后对二叉树进行 遍历 + 判断

const isBalanced = function (root: TreeNode | null): boolean {if (!root) return trueif (Math.abs(getHeight(root.left) - getHeight(root.right)) > 1)return falsereturn isBalanced(root.left) && isBalanced(root.right)
}
// 获取二叉树节点的高度
const getHeight = (root: TreeNode | null) => {if (!root) return 0return Math.max(getHeight(root.left), getHeight(root.right)) + 1
}

解法2:这个有点难理解,其实是解法1的升级版

  • 解法1一定会遍历完整个树,解法2不一定,遍历时发现不满足条件就会直接结束
const isBalanced = function (root: TreeNode | null): boolean {return recur(root) != -1
}const recur = function (root: TreeNode | null): number {if (!root) return 0let left = recur(root.left)if (left == -1) return -1let right = recur(root.right)if (right == -1) return -1return Math.abs(left - right) < 2 ? Math.max(left, right) + 1 : -1
}

4. 重复的子字符串

题目:459. 重复的子字符串

题解:模拟、技巧

这道题比较好想的思路是模拟,但是模拟也是有讲究的,要尽量减少暴力的次数(俗称剪枝)

在我的理解中,剪枝属于一种优化,其实把剪枝部分去掉应该也可以正常运行,但是效率会变低

解法1:模拟 + 剪枝

const repeatedSubstringPattern = function (s: string): boolean {if (s.length < 2) return false// 只需要遍历一半即可, 一半以后必然不能重复for (let i = 1; i <= s.length / 2; i++) {let sub = s.substring(0, i)// 剪枝:字符串的长度不是串的整数倍, 必然不满足条件if (s.length % i != 0) continue// 剪枝:最后不是以该子串结尾, 必然不满足条件if (sub != s.substring(s.length - sub.length)) continue// 利用 repeat API, 查看是否满足满足要求if (sub.repeat(s.length / sub.length) == s)return true}return false
}

解法2:技巧

这个思路是参考其他大佬的,确实很奇妙,学习了,可以留个印象

简单明了!!关于java两行代码实现的思路来源

[1] s = acd
[2] ss = acdacd
[3] ss.substring(1, ss.length - 1) = cdac (去头去尾)
判断: [3] 中包含 [1] 则满足条件, s = 'acd' 不满足条件
---[1] s = acaaca
[2] ss = acaacaacaaca
[3] ss.substring(1, ss.length - 1) = caacaacaac (去头去尾)
判断: [3] 中包含 [1] 则满足条件, s = 'acaaca' 满足条件
const repeatedSubstringPattern = function (s: string): boolean {let ss = s.repeat(2)return ss.substring(1, ss.length - 1).indexOf(s) != -1
}

5. 逆波兰表达式求值

题目:150. 逆波兰表达式求值

题解:用栈模拟、用数组模拟

解法1:用栈模拟,会使这道题万分简单

public int evalRPN(String[] tokens) {Stack<Integer> stack = new Stack<>();for (String token : tokens) {if ("+".equals(token)) {Integer num1 = stack.pop();Integer num2 = stack.pop();stack.push(num2 + num1);} else if ("-".equals(token)) {Integer num1 = stack.pop();Integer num2 = stack.pop();stack.push(num2 - num1);} else if ("*".equals(token)) {Integer num1 = stack.pop();Integer num2 = stack.pop();stack.push(num2 * num1);} else if ("/".equals(token)) {Integer num1 = stack.pop();Integer num2 = stack.pop();stack.push(num2 / num1);} else {stack.push(Integer.parseInt(token));}}return stack.pop();
}

代码丑陋,略微优化一下:(效率无影响)

public int evalRPN(String[] tokens) {Stack<Integer> stack = new Stack<>();for (String token : tokens) {if ("+".equals(token))stack.push(stack.pop() + stack.pop());else if ("-".equals(token))stack.push(-stack.pop() + stack.pop());else if ("*".equals(token))stack.push(stack.pop() * stack.pop());else if ("/".equals(token)) {Integer num1 = stack.pop(), num2 = stack.pop();stack.push(num2 / num1);} elsestack.push(Integer.parseInt(token));}return stack.pop();
}

解法2:用数组代替栈模拟

class Solution {public int evalRPN(String[] tokens) {int[] res = new int[tokens.length];int cur = 1; // 索引从 - 1 开始, 因为必须放进元素才能开始计算for (String token : tokens) {if ("/*-+".contains(token)) {int b = res[cur--], a = res[cur--];res[++cur] = calc(a, b, token);} elseres[++cur] = Integer.parseInt(token);}return res[cur];}public int calc(int a, int b, String op) {if (op.equals("+")) return a + b;else if (op.equals("-")) return a - b;else if (op.equals("*")) return a * b;else if (op.equals("/")) return a / b;else return -1;}
}

6. 加一

题目:66. 加一

题解:分情况模拟及其优化

解法1:最朴素的模拟做法,这个基本上是第一反映想到的,速度也可以击败 100%

public int[] plusOne(int[] digits) {int len = digits.length;// 1 不会进位的情况(最后一位不为 9)// 例如: 123 --> 124if (digits[len - 1] != 9) {digits[len - 1] += 1;return digits;}// 2 全是9的特殊情况: // 例如: 999 --> 1000boolean flag = true; // 是否全是9for (int i = 0; i < len; i++) {if (digits[i] != 9) {flag = false;break;}}if (flag) {int[] res = new int[len + 1];res[0] = 1;return res;}// 3 最后一位是9, 但是不全为 9// 例如: 129 --> 130digits[len - 1] += 1;while (len > 1) {// 无需进位, 直接跳出返回if (digits[len - 1] != 10) break;// 计算进位digits[len - 1] = 0;digits[--len - 1] += 1;}return digits;
}

解法2:其实是对以上代码的优化

public int[] plusOne(int[] digits) {for (int i = digits.length - 1; i >= 0; i--) {// 最后一位不为 9, 直接 + 1 返回结果if (digits[i] != 9) {digits[i]++;return digits;}// 最后一位为 9 或 10, 需要进位digits[i] = 0;}// 走完 for 还没有 return, 说明数字全是 9, 直接构造出结果并返回int[] res = new int[digits.length + 1];res[0] = 1;return res;
}

7. 二叉树中的列表

题目:1367. 二叉树中的列表

题解:递归

解法1:一个递归(超时)

boolean isSubPath(ListNode head, TreeNode root) {if (root == null) return false;if (head == null) return true;// 找到了与链表首节点相同的树节点if (root.val == head.val) {// 走到链表尾部, 表示已经找到和链表对应的路径if (head.next == null) return true;// 沿着这条路径走下去, 能对应上的则返回trueif (root.left != null && head.next.val == root.left.val && isSubPath(head.next, root.left))return true;if (root.right != null && head.next.val == root.right.val && isSubPath(head.next, root.right))return true;}return isSubPath(head, root.left) || isSubPath(head, root.right);
}

解法2:两个递归(可 AC)

class Solution {boolean isSubPath(ListNode head, TreeNode root) {if (root == null) return false;if (dfs(head, root)) return true;return isSubPath(head, root.left) || isSubPath(head, root.right);}boolean dfs(ListNode head, TreeNode root) {// 链表全部匹配完, 匹配成功if (head == null) return true;// 二叉树访问到空节点, 匹配失败if (root == null) return false;// 当前二叉树上的节点与链表节点值不相等, 匹配失败if (root.val != head.val) return false;return dfs(head.next, root.left) || dfs(head.next, root.right);}
}

8. 字符串相乘

题目:43. 字符串相乘

9. 二进制求和

题目:67. 二进制求和

题解 :模拟

这题可以根据题意直接模拟,最多也就是代码长点,if 条件多点,先做出来再说!

以下两个解法就是代码长短问题,效率是一样的。

解法1:模拟 + 各种 if + StringBuilder

public String addBinary(String a, String b) {StringBuilder sb = new StringBuilder();int carry = 0;int p1 = a.length(), p2 = b.length();while (p1 >= 0 || p2 >= 0) {char ca = (--p1 >= 0) ? a.charAt(p1) : '0';char cb = (--p2 >= 0) ? b.charAt(p2) : '0';if (ca == '1' && cb == '1') {if (carry == 1) sb.insert(0, "1");else sb.insert(0, "0");carry = 1;continue;}if (ca == '0' && cb == '0') {if (carry == 1) sb.insert(0, "1");else sb.insert(0, "0");carry = 0;continue;}if (carry == 1) {sb.insert(0, "0");carry = 1;} else {sb.insert(0, "1");carry = 0;}}return sb.charAt(0) == '0' ? sb.substring(1).toString() : sb.toString();
}

题解2:善用 'a' - '0' 这种方式转数字,优化代码

public String addBinary(String a, String b) {StringBuilder sb = new StringBuilder();int i = a.length() - 1, j = b.length() - 1;int c = 0; // 进位while (i >= 0 || j >= 0) {if (i >= 0) c += a.charAt(i--) - '0';if (j >= 0) c += b.charAt(j--) - '0';sb.append(c & 1);c >>= 1;}String res = sb.reverse().toString();return c > 0 ? "1" + res : res;
}

10. 数组形式的整数加法

题目:989. 数组形式的整数加法

题解:模拟

这题常规思路是比较容易做出来的,就是从后往前遍历,然后相加,同时保存一个进位,每次注意更新进位的值就行了

基于以上思路,Java 中使用 ArrayList 的话,前插 res.Add(0, val) 效率不如直接后插然后反转。。。

解法1:模拟,用 ArrayList 往后插入数据,然后反转容器

public List<Integer> addToArrayForm(int[] num, int k) {List<Integer> res = new ArrayList<>();int carry = 0, len = num.length - 1;while (len >= 0 || k != 0) {int n1 = len >= 0 ? num[len--] : 0;int n2 = k % 10;k /= 10;res.add((n1 + n2 + carry) % 10);carry = n1 + n2 + carry >= 10 ? 1 : 0;}if (carry == 1) res.add(1);Collections.reverse(res);return res;
}

但是如果使用 LinkedList 的前插 res.addFirst(val) 效果会很快

解法2:模拟,用 LinkedList 直接往前插入数据

public List<Integer> addToArrayForm(int[] num, int k) {LinkedList<Integer> res = new LinkedList<>();int carry = 0, len = num.length - 1;while (len >= 0 || k != 0) {int n1 = len >= 0 ? num[len--] : 0;int n2 = k % 10;k /= 10;res.addFirst((n1 + n2 + carry) % 10);carry = n1 + n2 + carry >= 10 ? 1 : 0;}if (carry == 1) res.addFirst(1);return res;
}

解法3:还有很多其他做法,比如将数字转成 int 数组后操作…

public List<Integer> addToArrayForm(int[] num, int k) {String[] ss = String.valueOf(k).split("");// string[] => int[]int[] is = Arrays.stream(ss).mapToInt(Integer::valueOf).toArray();List<Integer> res = new ArrayList<>();int carry = 0;int p1 = num.length - 1, p2 = is.length - 1;while (p1 >= 0 || p2 >= 0) {int n1 = p1 >= 0 ? num[p1--] : 0;int n2 = p2 >= 0 ? is[p2--] : 0;int sum = (n1 + n2 + carry) % 10;carry = (n1 + n2 + carry) >= 10 ? 1 : 0;res.add(0, sum);}if (carry == 1) res.add(0, 1);return res;
}

11. 每日温度

题目:739. 每日温度

题解:暴力、单调 栈

解法1:暴力

public int[] dailyTemperatures(int[] T) {int[] res = new int[T.length];for (int i = 0; i < T.length; i++) {for (int j = i + 1; j < T.length; j++) {if (T[j] > T[i]) {res[i] = j - i;break;}}}return res;
}

解法2:单调递减栈

注意:Java 中建议使用 Deque 而不是用 Stack

public int[] dailyTemperatures(int[] T) {// 单调递减栈(栈中存储的是下标)Deque<Integer> stack = new ArrayDeque<>();int[] res = new int[T.length];for (int i = 0; i < T.length; i++) {// 当前元素 > 栈顶下标对应元素while (!stack.isEmpty() && T[i] > T[stack.peekLast()]) {int temp = stack.removeLast(); // 获取栈顶下标res[temp] = i - temp; // 计算下标距离}stack.addLast(i); // 当前位置入栈}return res;
}

12.最后一个单词长度

题目:58. 最后一个单词的长度

题解:API

解法1:暴力使用 API

public int lengthOfLastWord(String s) {String[] words = s.split("\\s+");return words[words.length - 1].length();
}

解法2:轻度使用 API

public int lengthOfLastWord(String s) {s = s.trim();for (int i = s.length() - 1; i >= 0; i--)if (s.charAt(i) == ' ')return s.length() - i - 1;return s.length();
}

解法3:不使用 API

public int lengthOfLastWord(String s) {int i = s.length() - 1;// 去除末尾的空字符while (s.charAt(i) == ' ') i--;int cnt = 0;while (i >= 0 && s.charAt(i) != ' ') {cnt++;i--;}return cnt;
}

13. 旋转矩阵

题目:48. 旋转图像

题解:模拟 + 技巧

解法1:暴力模拟

  • 找到每个要交换的点的位置,然后模拟进行交换操作
public void rotate(int[][] matrix) {int n = matrix.length;for (int i = 0; i < n / 2; i++) {for (int j = i; j < n - i - 1; j++) {// 00 -> 02// 01 -> 12int temp = matrix[j][n - i - 1];matrix[j][n - i - 1] = matrix[i][j];// 02 -> 22// 12 -> 21int temp2 = matrix[n - i - 1][n - j - 1];matrix[n - i - 1][n - j - 1] = temp;// 22 -> 20// 21 -> 10temp = matrix[n - j - 1][i];matrix[n - j - 1][i] = temp2;// 20 -> 00// 10 -> 01matrix[i][j] = temp;}}
}

优化以上代码:

public void rotate(int[][] matrix) {int n = matrix.length - 1;for (int i = 0; i <= matrix.length / 2; i++) {for (int j = i; j < n - i; j++) {// 获取各顶点的值int a = matrix[i][j]; // 左上角int b = matrix[j][n - i]; // 右上角int c = matrix[n - i][n - j]; // 右下角int d = matrix[n - j][i]; // 左下角// 交换各顶点的值matrix[i][j] = d;matrix[j][n - i] = a;matrix[n - i][n - j] = b;matrix[n - j][i] = c;}}
}

解法2:技巧

  • 先沿对角线翻转,再沿垂直竖线翻转,即可实现旋转矩阵
public void rotate(int[][] matrix) {int n = matrix.length;// 先沿对角线翻转for (int i = 0; i < n; i++) {for (int j = 0; j < i; j++) {int temp = matrix[i][j];matrix[i][j] = matrix[j][i];matrix[j][i] = temp;}}// 再沿垂直竖线翻转for (int i = 0; i < n; i++) {for (int j = 0, k = n - 1; j < k; j++, k--) {int temp = matrix[i][k];matrix[i][k] = matrix[i][j];matrix[i][j] = temp;}}
}

14. 判断矩阵经轮转后是否一致

题目:1886. 判断矩阵经轮转后是否一致

n x n 矩阵旋转总结:

  • 90 度:沿左上到右下翻转,再沿垂直中线翻转
  • 180 度:沿水平中线翻转,沿垂直中线翻转
  • 270 度:沿左上到右下翻转,再沿水平中线翻转
  • 360 度:本身

题解:模拟

n x n 矩阵旋转总结:

  • 90 度:沿左上到右下翻转,再沿垂直中线翻转
  • 180 度:沿水平中线翻转,沿垂直中线翻转
  • 270 度:沿左上到右下翻转,再沿水平中线翻转
  • 360 度:本身

但是如果不是要一下子求出结果,而是要每个轮流判断,只需要利用转 90 度就行。

本题解法:暴力模拟

  • 先判断不转(相当于转 360度)是否满足两矩阵相等
  • 再判断转 90度 是否满足两矩阵相等
  • 再判断转 180度 满足两矩阵相等(在上面基础上再转 90度)
  • 再判断转 270度 满足两矩阵相等(在上面基础上再转 90度)

转 90 度的两种思路:

  • 思路1:暴力模拟,计算好 4 个顶点,模拟移动到下一个位置
public void rotate(int[][] matrix) {int n = matrix.length - 1;for (int i = 0; i <= matrix.length / 2; i++) {for (int j = i; j < n - i; j++) {// 获取各顶点的值int a = matrix[i][j]; // 左上角int b = matrix[j][n - i]; // 右上角int c = matrix[n - i][n - j]; // 右下角int d = matrix[n - j][i]; // 左下角// 交换各顶点的值matrix[i][j] = d;matrix[j][n - i] = a;matrix[n - i][n - j] = b;matrix[n - j][i] = c;}}
}

思路2:技巧,也就是上面的矩阵旋转总结中,沿左上到右下翻转,再沿垂直中线翻转

public void rotate(int[][] matrix) {int n = matrix.length;// 先沿对角线翻转for (int i = 0; i < n; i++) {for (int j = 0; j < i; j++) {int temp = matrix[i][j];matrix[i][j] = matrix[j][i];matrix[j][i] = temp;}}// 再沿垂直竖线翻转for (int i = 0; i < n; i++) {for (int j = 0, k = n - 1; j < k; j++, k--) {int temp = matrix[i][k];matrix[i][k] = matrix[i][j];matrix[i][j] = temp;}}
}

根据以上的基础,可以知道本题代码为下:

class Solution {public boolean findRotation(int[][] mat, int[][] target) {// 不转if (checkMatrix(mat, target)) return true;// 转 90 度rotate(mat);if (checkMatrix(mat, target)) return true;// 转 180 度(再转 90 度)rotate(mat);if (checkMatrix(mat, target)) return true;// 转 270 度(再转 90 度)rotate(mat);if (checkMatrix(mat, target)) return true;return false;}/*** 将矩阵旋转 90 度*/void rotate(int [][] matrix) {int n = matrix.length;// 沿 左上->右下 翻转for (int i = 0; i < n; i++) {for (int j = 0; j < i; j++) {int temp = matrix[i][j];matrix[i][j] = matrix[j][i];matrix[j][i] = temp;}}// 沿 垂直中线 翻转for (int i = 0; i < n; i++) {for (int j = 0, k = n -1; j < k; j++, k--) {int temp = matrix[i][k];matrix[i][k] = matrix[i][j];matrix[i][j] = temp;}}}/*** 检查两矩阵是否相等*/boolean checkMatrix(int[][] m1, int[][] m2) {for (int i = 0; i < m1.length; i++) {for (int j = 0; j < m1[0].length; j++) {if (m1[i][j] != m2[i][j]) return false; }}return true;}
}

15. 螺旋矩阵

题目:54. 螺旋矩阵

题解:模拟

最朴素易懂的做法:

public List<Integer> spiralOrder(int[][] matrix) {int m = matrix.length, n = matrix[0].length;List<Integer> res = new ArrayList<>();int direction = 1; // 1234 - 右下左上boolean[][] visited = new boolean[m][n]; // 记录访问过的坐标, 防止重复访问int x = 0, y = 0; // 当前访问坐标// 初始化第一个位置res.add(matrix[0][0]);visited[0][0] = true;while (res.size() < m * n) {// 一直往 右 走while (direction == 1 && y + 1 < n && !visited[x][y + 1]) {res.add(matrix[x][++y]);visited[x][y] = true;}direction = 2; // 更改方向为 下// 一直往 下 走while (direction == 2 && x + 1 < m && !visited[x + 1][y]) {res.add(matrix[++x][y]);visited[x][y] = true;}direction = 3; // 更改方向为 左// 一直往 左 走while (direction == 3 && y - 1 >= 0 && !visited[x][y - 1]) {res.add(matrix[x][--y]);visited[x][y] = true;}direction = 4; // 更改方向为 上// 一直往 上 走while (direction == 4 && x - 1 >= 0 && !visited[x - 1][y]) {res.add(matrix[--x][y]);visited[x][y] = true;}direction = 1; // 更改方向为 右}return res;
}

16. 最接近原点的K个点

题目:973. 最接近原点的 K 个点

题解:数组排序、优先级队列

解法1:自定义排序规则后,利用数组的排序函数

public int[][] kClosest(int[][] points, int k) {Queue<int[]> queue = new PriorityQueue<>(Comparator.comparingDouble(o -> o[0] * o[0] + o[1] * o[1]));queue.addAll(Arrays.asList(points));int[][] res = new int[k][2];for (int i = 0; i < k; i++) res[i] = queue.poll();return res;
}

解法2:利用优先级队列

public int[][] kClosest(int[][] points, int k) {Queue<int[]> queue = new PriorityQueue<>(Comparator.comparingDouble(o -> o[0] * o[0] + o[1] * o[1]));queue.addAll(Arrays.asList(points));int[][] res = new int[k][2];for (int i = 0; i < k; i++) res[i] = queue.poll();return res;
}

对优先级队列做法的优化:只维护 size = k 的优先队列

public int[][] kClosest(int[][] points, int k) {PriorityQueue<int[]> queue = new PriorityQueue<>((a, b) -> cmp(a, b));// 遍历的同时维护 size = k 的优先级队列for (int i = 0; i < points.length; i++) {// 堆中元素数量 < k, 直接放入if (queue.size() < k)queue.add(points[i]);// 堆中已经有 k 个元素, 比较堆顶元素和当前遍历元素else {int[] temp = queue.peek();if (cmp(points[i], temp) > 0) {queue.poll();queue.add(points[i]);}}}int[][] res = new int[k][2];for (int i = 0; i < k; i++)res[i] = queue.poll();return res;
}int cmp(int[] a, int[] b) {return (b[0] * b[0] + b[1] * b[1]) - (a[0] * a[0] + a[1] * a[1]);
}

17. 等差子数组

题目:1630. 等差子数组

题解:暴力模拟

思路:暴力模拟

public List<Boolean> checkArithmeticSubarrays(int[] nums, int[] l, int[] r) {List<Boolean> res = new ArrayList<>();for (int i = 0; i < l.length; i++)res.add(isArithmetic(Arrays.copyOfRange(nums, l[i], r[i] + 1)));return res;
}boolean isArithmetic(int[] arr) {if (arr.length <= 1) return true;Arrays.sort(arr);int diff = arr[1] - arr[0];for (int i = 1; i < arr.length; i++) {if (arr[i] - arr[i - 1] != diff)return false;}return true;
}

18. N 叉树的层次遍历

题目:429. N 叉树的层序遍历

题解:BFS

let levelOrder = function (root: Node | null): number[][] {if (!root) return []let queue = [root], res = []while (queue.length) {let temp = []for (let i = queue.length - 1; i >= 0; i--) {let node = queue.pop()temp.unshift(node.val)queue.unshift(...node.children)}res.push(temp)}return res
};

19. 下一个更大元素 II

题目:503. 下一个更大元素 II

题解:暴力、单调栈

暴力:

  • 读懂题目就能做,注意判断特殊情况
  • 复杂度为 O(n * n)
const nextGreaterElements = function (nums: number[]): number[] {let len = nums.lengthif (len <= 1) return [-1]let res = new Array(len).fill(null)for (let i = 0; i < len; i++) {for (let j = i + 1; j < len + i; j++) {if (nums[j % len] > nums[i]) {res[i] = nums[j % len]break}}if (res[i] == null) res[i] = -1}return res
};

单调栈:

  • 「单调栈」就是栈内元素满足单调性的栈结构。
  • 单调栈的根本作用在于求得「每一个数字在原始序列中左 / 右边第一个大于 / 小于它自身的数字」
  • 由于每一个数字只会入栈一次且最多出栈一次,因此总的时间复杂度为 O(n)
public int[] nextGreaterElements(int[] nums) {int[] res = new int[nums.length];Arrays.fill(res, -1);Deque<Integer> stack = new ArrayDeque<>();for (int i = 0; i < nums.length * 2; i++) {// 栈不为空, 且当前元素 > 栈顶下标对应的元素while (!stack.isEmpty() && nums[i % nums.length] > nums[stack.getLast()]) {// 更新栈顶下标对应的结果res[stack.removeLast()] = nums[i % nums.length];}stack.addLast(i % nums.length);}return res;
}

20. 下一个更大元素 III

题目:556. 下一个更大元素 III

题解:排序模拟

此题和 31.下一个排列 几乎一样,加了一个返回值判断而已

思路:

  • 对于 [2, 6, 3, 5, 4, 1]

  • 先从后往前找到一个逆序数 N, 即为 3

  • 然后再从 N 后面找到一个比它大的 “最小数”

  • 即在 [5, 4, 1] 中找到比 3 大的最小数, 为 4

    (这里可以直接再次从后往前找,因为这段数字倒着遍历必然是正序的)

  • 交换两者位置,则为 [2, 6, 4, 5, 3, 1]

  • 然后对一开始 N 后面位置进行反转

  • 即从 [2, 6, 4, 5, 3, 1] 到 [2, 6, 4, 1, 3, 5]

public int nextGreaterElement(int n) {char[] cs = String.valueOf(n).toCharArray();int i = cs.length - 2;// 从后往前找到第一个逆序的数字 Nwhile (i >= 0 && cs[i] >= cs[i + 1]) i--;if (i < 0) return -1;// 找到 N 后面比 N 大的 "最小数字"int j = cs.length - 1;while (j >= 0 && cs[j] <= cs[i]) j--;// 交换 N 和 比N大的"最小数字"的位置swap(cs, i, j); // 将原来的 N 位置后面的数字变为最小序(本就是倒序,反转一下即可)reverse(cs, i + 1);// 转换成 int, 越界则返回 -1long res = 0;for (char c : cs) res = res * 10 + (c - '0');return res > Integer.MAX_VALUE ? -1 : (int)res;
}void reverse(char[] cs, int start) {int i = start, j = cs.length - 1;while (i < j) swap(cs, i++, j--);
}void swap(char[] cs, int i, int j) {char temp = cs[i];cs[i] = cs[j];cs[j] = temp;
}

21. 通知所有员工所需的时间 TODO

题目:1376. 通知所有员工所需的时间

22. 字母异位词分组

题目:49. 字母异位词分组

题解:哈希

其实这题可以将问题转化一下,如何确定一个 “字母异位词” 的 “唯一值”:

  • 对于 “abc”, “acb”, “bca” 等都属于字母异位词,如何找到一个 “唯一值” 将他们进行分组
  • 最直接的做法就是排序,他们经过排序后都会变成:“abc”,也就找到了那个 “唯一值”
  • 所以如果可以将找到唯一值的过程优化的更快,整体效率也就更快(解法 2)

解法1:排序来找到唯一值

public List<List<String>> groupAnagrams(String[] strs) {Map<String, List<String>> map = new HashMap<>();for (int i = 0; i < strs.length; i++) {String temp = getFeature(strs[i]);if (!map.containsKey(temp)) map.put(temp, new ArrayList<>());map.get(temp).add(strs[i]);}return new ArrayList<>(map.values());
}String getFeature(String s) {char[] cs = s.toCharArray();Arrays.sort(cs);return String.valueOf(cs);
}

解法2:编码出唯一值

public List<List<String>> groupAnagrams(String[] strs) {Map<String, List<String>> map = new HashMap<>();for (String s : strs) {String encode = encode(s);if (!map.containsKey(encode)) map.put(encode, new ArrayList<>());map.get(encode).add(s);}return new ArrayList<>(map.values());
}// 根据出现的次数进行编码,26位
public String encode(String s) {char[] code = new char[26];for (char c : s.toCharArray()) {int delta = c - 'a';code[delta]++;}return new String(code);
}

23. 找到字符串中所有字母异位词 todo

题目:438. 找到字符串中所有字母异位词

题解

暴力 + 排序:

public List<Integer> findAnagrams(String s, String p) {int len = p.length();char[] pcs = p.toCharArray();Arrays.sort(pcs);List<Integer> res = new ArrayList<>();for (int i = 0; i <= s.length() - len; i++) {char[] cs = s.substring(i, i + len).toCharArray();Arrays.sort(cs);if (Arrays.equals(cs, pcs)) res.add(i);}return res;
}

24. 乘积小于K的子数组 to do

题目:713. 乘积小于K的子数组

题解

暴力:

public int numSubarrayProductLessThanK(int[] nums, int k) {int count = 0;for (int i = 0; i < nums.length; i++) {int sum = nums[i];if (sum < k) count++;for (int j = i + 1; j < nums.length; j++) {sum *= nums[j];if (sum < k) count++;else break;}}return count;
}

25. 二维区域和检索 - 矩阵不可变

题目:304. 二维区域和检索 - 矩阵不可变

题解:前缀和

暴力可以过:

class NumMatrix {int[][] m;public NumMatrix(int[][] matrix) {m = matrix;}public int sumRegion(int row1, int col1, int row2, int col2) {int sum = 0;for (int i = row1; i <= row2; i++)for (int j = col1; j <= col2; j++)sum += m[i][j];return sum;}
}

前缀和一:一维前缀和的角度,计算每一行的前缀和

class NumMatrix {int[][] pre;public NumMatrix(int[][] matrix) {int n = matrix.length, m = (n == 0) ? 0 : matrix[0].length;pre = new int[n + 1][m + 1];for (int i = 0; i < n; i++) {int tempSum = 0; // 每一行的前缀和数组for (int j = 0; j < m; j++) {tempSum += matrix[i][j];pre[i + 1][j + 1] = tempSum;}}}public int sumRegion(int row1, int col1, int row2, int col2) {int sum = 0;for (int i = row1; i <= row2; i++) sum += pre[i + 1][col2 + 1] - pre[i + 1][col1];return sum;}
}

前缀和二:二维前缀和的角度

class NumMatrix {int[][] sum;public NumMatrix(int[][] matrix) {int n = matrix.length, m = (n == 0) ? 0 : matrix[0].length;sum = new int[n + 1][m + 1];// 预处理前缀和数组(模板)for (int i = 1; i <= n; i++)for (int j = 1; j <= m; j++)sum[i][j] = sum[i - 1][j] + sum[i][j - 1] - sum[i - 1][j - 1] + matrix[i - 1][j - 1];}public int sumRegion(int x1, int y1, int x2, int y2) {x1++;y1++;x2++;y2++;return sum[x2][y2] - sum[x1 - 1][y2] - sum[x2][y1 - 1] + sum[x1 - 1][y1 - 1];}}

26. 最小差值 II

题目:910. 最小差值 II

题解

public boolean reachingPoints(int sx, int sy, int tx, int ty) {while (tx > 0 && ty > 0){ // 因为sx, sy, tx, ty 是范围在 [1, 10^9] 的整数,逆推不能出界if (sx == tx && sy == ty) return true; // 判断是否到达了起始值// 每次逆推只能有tx、ty中较大值减去若干个较小值if (tx > ty) tx -= ty;else ty -= tx;}return false;
}
public boolean reachingPoints1(int sx, int sy, int tx, int ty) {while (tx > 0 && ty > 0){ // 因为sx, sy, tx, ty 是范围在 [1, 10^9] 的整数,逆推不能出界if (sx == tx && sy == ty) return true; // 判断是否到达了起始值// 此时只能有tx减去ty// tx - sx是目标与起始值在x的差距,我们需要一次减去n * ty达到快速逼近sx的目的if (tx > ty) tx -= Math.max((tx - sx) / ty, 1) * ty;// 此时只能有ty减去tx// ty - sy是目标与起始值在y的差距,我们需要一次减去n * tx达到快速逼近sy的目的else ty -= Math.max((ty - sy) / tx, 1) * tx;}return false;
}

27. 重排链表

题目:143. 重排链表

题解:模拟、拆分 + 拼接

暴力模拟:实现一个方法:将最链表的最后一个节点移动到首位,然后不停的调用这个方法就行

public void reorderList(ListNode head) {ListNode cur = head;while (cur != null && cur.next != null && cur.next.next != null) {moveList(cur);cur = cur.next.next;}
}/*** 将最后一个节点移到首位* @param head*/
void moveList(ListNode head) {ListNode last = head, pre = null;while (last.next != null) {pre = last;last = last.next;}ListNode temp = head.next;head.next = last;last.next = temp;pre.next = null;
}

拆分 + 拼接:

  1. 快慢指针将链表拆分成 两段
  2. 反转后段链表
  3. 将反转后的后段链表插入到前段的的节点之间
public void reorderList(ListNode head) {// 快慢指针分出两段链表ListNode slow = head, fast = head.next;while (fast != null && fast.next != null) {slow = slow.next;fast = fast.next.next;}// 后半部分的链表头节点ListNode later = slow.next;slow.next = null; // 断开两部分链表的连接later = reverseList(later); // 反转后半部分链表// 拼接两段链表ListNode front = head;while (front != null && later != null) {ListNode laterNext = later.next;ListNode frontNext = front.next;front.next = later;later.next = frontNext;front = frontNext;later = laterNext;}}ListNode reverseList(ListNode head) {if (head == null || head.next == null) return head;ListNode node = reverseList(head.next);head.next.next = head;head.next = null;return node;
}

28. 复制带随机指针的链表

题目:138. 复制带随机指针的链表

题解:哈希

public Node copyRandomList(Node head) {// 使用 map 存储 旧结点 和 新结点 的映射Map<Node, Node> map = new HashMap<>();Node node = head;while (node != null) {map.put(node, new Node(node.val));node = node.next;}node = head;while (node != null) {map.get(node).next = map.get(node.next);map.get(node).random = map.get(node.random);node = node.next;}return map.get(head);
}

29. 两数相加

题目:2. 两数相加

难度中等7863收藏分享切换为英文接收动态反馈

题解:模拟

思路:模拟遍历,同时维护一个进位变量

public ListNode addTwoNumbers(ListNode l1, ListNode l2) {ListNode root = new ListNode(0), cursor = root;int carry = 0;while (l1 != null || l2 != null || carry > 0) {int n1 = (l1 == null) ? 0 : l1.val;int n2 = (l2 == null) ? 0 : l2.val;int sumVal = n1 + n2 + carry; // 计算数字 + 进位和carry = sumVal / 10; // 计算下一个进位cursor.next = new ListNode(sumVal % 10);cursor = cursor.next;if (l1 != null) l1 = l1.next;if (l2 != null) l2 = l2.next;}return root.next;
}

30. 两数相加 II

题目:445. 两数相加 II

题解:反转链表3次、栈

思路1:

  • 反转 l1、反转 l2
  • 计算 l1 + l2
  • 反转 l1 + l2 的结果
class Solution {public ListNode addTwoNumbers(ListNode l1, ListNode l2) {l1 = reverList(l1);l2 = reverList(l2);ListNode root = new ListNode(0), cursor = root;int carry = 0;while (l1 != null || l2 != null || carry > 0) {int n1 = (l1 == null) ? 0 : l1.val;int n2 = (l2 == null) ? 0 : l2.val;int sumVal = n1 + n2 + carry; // 计算数字 + 进位和carry = sumVal / 10; // 计算下一个进位cursor.next = new ListNode(sumVal % 10);cursor = cursor.next;if (l1 != null) l1 = l1.next;if (l2 != null) l2 = l2.next;}return reverList(root.next);}ListNode reverList(ListNode head) {if (head == null || head.next == null) return head;ListNode node = reverList(head.next);head.next.next = head;head.next = null;return node;}
}

思路2:栈

public ListNode addTwoNumbers(ListNode l1, ListNode l2) {LinkedList<Integer> s1 = new LinkedList<>(), s2 = new LinkedList<>();while(l1 != null) {s1.addFirst(l1.val);l1 = l1.next;}while(l2 != null) {s2.addFirst(l2.val);l2 = l2.next;}int carry = 0;ListNode lastNode = null;while(!s1.isEmpty() || !s2.isEmpty() || carry > 0) {int n1 = s1.isEmpty() ? 0 : s1.removeFirst();int n2 = s2.isEmpty() ? 0 : s2.removeFirst();int sum = n1 + n2 + +carry;carry = sum / 10;ListNode curNode = new ListNode(sum % 10);curNode.next = lastNode;lastNode = curNode;}return lastNode;
}

31. 旋转链表

题目:61. 旋转链表

题解:往后拼接

public ListNode rotateRight(ListNode head, int k) {if (head == null || k == 0) return head;// 将某个节点指向链表最后, 此时顺便拿到链表长度ListNode node = head;int size = 1;while (node.next != null) {size++;node = node.next;}// 计算头节点后移的次数int i = (size - k % size);// 将头节点的值往后拼接并后移while (i > 0) {node.next = new ListNode(head.val);head = head.next;node = node.next;i--;}return head;
}

32. 二叉搜索树迭代器

题目:173. 二叉搜索树迭代器

class BSTIterator {List<Integer> list;public BSTIterator(TreeNode root) {list = new LinkedList<>();dfs(root);}void dfs(TreeNode root) {if (root == null)return;dfs(root.left);list.add(root.val);dfs(root.right);}public int next() {return list.remove(0);}public boolean hasNext() {return !list.isEmpty();}
}

33. 座位预约管理系统

题目:1845. 座位预约管理系统

题解:优先级队列

class SeatManager {Queue<Integer> q;public SeatManager(int n) {q = new PriorityQueue<>();for (int i = 1; i <= n; i++) q.add(i);}public int reserve() {return q.poll();}public void unreserve(int seatNumber) {q.add(seatNumber);}
}

34. 柠檬水找零

题目:860. 柠檬水找零

题解:贪心 + 模拟

这道题是很好的 贪心 + 模拟:

public boolean lemonadeChange(int[] bills) {// 20 根本不用关心int five = 0, ten = 0;for (int bill: bills) {// 5 不用找if (bill == 5) five++;// 10 必须找 5else if (bill == 10) {if (five == 0) return false;five--;ten++;// 20 从 10, 5 依次找, 凑不到 15 则无法找} else if (bill == 20) {int temp = 15;while (temp != 0) {if (ten > 0 && temp >= 10) {temp -= 10;ten--;} else if (five > 0) {temp -= 5;five--;} else return false;}}}return true;
}

35. 最小栈

题目:155. 最小栈

题解:优先级队列、双栈、链表

思路1:优先级队列

class MinStack {Deque<Integer> stack;Queue<Integer> queue;public MinStack() {stack = new ArrayDeque<>();queue = new PriorityQueue<>();}public void push(int val) {stack.addLast(val);queue.add(val);}public void pop() {queue.remove(stack.removeLast());}public int top() {return stack.getLast();}public int getMin() {return queue.peek();}
}

思路2:维护两个栈

class MinStack {Deque<Integer> stack, minStack;public MinStack() {stack = new ArrayDeque<>();minStack = new ArrayDeque<>();}public void push(int val) {if (minStack.isEmpty() || val <= minStack.getLast())minStack.addLast(val);stack.addLast(val);}public void pop() {if (minStack.getLast().equals(stack.removeLast()))minStack.removeLast();}public int top() {return stack.getLast();}public int getMin() {return minStack.getLast();}
}

思路3:维护一个链表,在链表的属性上维护 min

class MinStack {private Node head;public void push(int x) {if (head == null)head = new Node(x, x);elsehead = new Node(x, Math.min(x, head.min), head);}public void pop() {head = head.next;}public int top() {return head.val;}public int getMin() {return head.min;}private class Node {int val;int min;Node next;private Node(int val, int min) {this(val, min, null);}private Node(int val, int min, Node next) {this.val = val;this.min = min;this.next = next;}}
}

36. 扁平化嵌套列表迭代器

题目:341. 扁平化嵌套列表迭代器

题解:递归扁平化

public class NestedIterator implements Iterator<Integer> {int idx = 0;List<Integer> list; // 存储扁平化以后的列表// 递归扁凭化列表void add(List<NestedInteger> nestedList) {for (NestedInteger integer : nestedList) {if (!integer.isInteger()) { // 不是数字, 递归add(integer.getList());} else { // 是数字, 直接加到 listlist.add(integer.getInteger());}}}public NestedIterator(List<NestedInteger> nestedList) {list = new ArrayList<>();add(nestedList);}@Overridepublic Integer next() {return list.get(idx++);}@Overridepublic boolean hasNext() {return idx < list.size();}
}

37. 设计一个验证系统

题目:1797. 设计一个验证系统

题解:读题模拟

class AuthenticationManager {int timeToLive; // 过期时间Map<String, Integer> map; // 验证码与其过期时间public AuthenticationManager(int timeToLive) {this.map = new HashMap<>();this.timeToLive = timeToLive;}public void generate(String tokenId, int currentTime) {map.put(tokenId, currentTime + timeToLive);}public void renew(String tokenId, int currentTime) {// 没有这个验证码、验证码已经过期, 或者正好过期if (!map.containsKey(tokenId) || map.get(tokenId) <= currentTime) return;map.put(tokenId, currentTime + timeToLive);}public int countUnexpiredTokens(int currentTime) {int count = 0;for (Map.Entry<String, Integer> entry: map.entrySet()) {if (entry.getValue() > currentTime) count++;}return count;}
}

38. 设计链表

题目:设计链表

题解:基础功

class MyLinkedList {class Node {int val;Node next;Node() {this.val = 0;this.next = null;}Node(int val, Node next) {this.val = val;this.next = next;}}Node head; // 虚拟头结点Node tail; // 尾节点, 方便从最后插入public MyLinkedList() {this.head = new Node(); // 虚拟头节点this.tail = this.head; // 一开始没有尾节点}public int get(int index) {Node node = head.next;while (index > 0) {node = node.next;index--;if (node == null) return -1;}return node.val;}public void addAtHead(int val) {Node newNode = new Node(val, head.next);head.next = newNode;maintainTail();}public void addAtTail(int val) {Node newNode = new Node(val, null);tail.next = newNode;maintainTail();}public void addAtIndex(int index, int val) {if (index <= 0) {addAtHead(val); // index <= 0, 直接往前面插入return;}Node node = head;while (index > 0) {node = node.next;index--;// index 大于链表长度, 不会插入节点if (node == null) return;}// index 等于链表长度, 在后面插入节点if (node.next == null) {addAtTail(val);return;}Node newNode = new Node(val, node.next);node.next = newNode;maintainTail();}public void deleteAtIndex(int index) {if (index < 0) return;Node node = head;while (index > 0) {node = node.next;index--;if (node == null) return;}tail = node; // 需要维护一下尾节点, 直接删除有可能让 tail 乱掉if (node.next != null) {node.next = node.next.next;} else {node.next = null;}maintainTail();}// 维护尾节点void maintainTail() {while (tail.next != null) {tail = tail.next;}}
}

39. O(1) 时间插入、删除和获取随机元素

题目:O(1) 时间插入、删除和获取随机元素

题解:List 与 Map

class RandomizedSet {Map<Integer, Integer> map;List<Integer> list;Random random;public RandomizedSet() {map = new HashMap<>();list = new ArrayList<>();random = new Random();}public boolean insert(int val) {if (map.containsKey(val))return false;list.add(val);map.put(val, list.size() - 1);return true;}public boolean remove(int val) {if (!map.containsKey(val))return false;int index = map.get(val);int last = list.get(list.size() - 1); // 最后一个元素的值// 用最后一个元素覆盖要删除的元素(同时维护 map 和 list)list.set(index, last);map.put(last, index);list.remove(list.size() - 1);map.remove(val);return true;}public int getRandom() {int randomIdx = random.nextInt(list.size());return list.get(randomIdx);}
}

40. 设计循环链表

题目:622. 设计循环队列

题解:基础功

class MyCircularQueue {int[] nums; // 元素, nums.length - 队列容量int head; // 当前指针int size; // 当前元素数量public MyCircularQueue(int k) {nums = new int[k];}public boolean enQueue(int value) {if (isFull()) return false;nums[(head + size) % nums.length] = value;size++;return true;}public boolean deQueue() {if (isEmpty()) return false;head = (head + 1) % nums.length;size--;return true;}public int Front() {return (isEmpty()) ? -1 : nums[head];}public int Rear() {return (isEmpty()) ? -1 : nums[(head + size - 1) % nums.length];}public boolean isEmpty() {return size == 0;}public boolean isFull() {return nums.length == size;}
}

41. 我的日程安排表 I

题目:729. 我的日程安排表 I

题解:暴力模拟

public class MyCalendar {List<int[]> calendar;MyCalendar() {calendar = new ArrayList();}public boolean book(int start, int end) {for (int[] iv : calendar) {if (iv[0] < end && start < iv[1]) return false;}calendar.add(new int[] { start, end });return true;}
}

《编程能力基础》刷题笔记(41 题)相关推荐

  1. 《剑指 Offer I》刷题笔记 41 ~ 50 题

    <剑指 Offer I>刷题笔记 41_50 排序(中等) 41. 最小的k个数# _解法1:排序 API + 数组复制 API 42. 数据流中的中位数 _解法1:暴力 搜索和回溯算法( ...

  2. 华南农业大学计算机考研真题,华南农业大学341农业知识综合三考研真题笔记期末题等...

    2017年华南农业大学341农业知识综合三全套考研资料 第一部分  考研历年真题(非常重要) 1-1(这一项是发送电子版)  本套资料没有真题.赠送<重点名校近三年考研真题汇编>供参考. ...

  3. 云南农业大学C语言程序设计,云南农业大学341农业知识综合三考研真题笔记期末题等...

    2017年云南农业大学341农业知识综合三全套考研资料 第一部分  考研历年真题(非常重要) 1-1(这一项是发送电子版)  本套资料没有真题.赠送<重点名校近三年考研真题汇编>供参考. ...

  4. 掌握Python语法篇:核心编程能力基础第一阶段

    就想问一下有多少是看到封面就点进来的,不过不要失望今天依旧会是收获满满的一天,记得点一下最后的五角星,哪怕是起灰也行啊,支持一下辣条哥啊 今天我们继续聊python 之前我们讲了python的输入和输 ...

  5. LeetCode《编程能力入门》刷题笔记(34 题全)

    LeetCode<编程能力入门>刷题笔记 基本数据类型 1. 在区间范围内统计奇数数目 _解法1:暴力迭代 _解法2:数学(找规律) 2. 去掉最低工资和最高工资后的工资平均值 _解法1: ...

  6. 小何同学的leetcode刷题笔记 基础篇(01)整数反转

    小何同学的leetcode刷题笔记 基础篇(01)整数反转[07] *** [01]数学取余法*** 对数字进行数位操作时,常见的方法便是用取余的方法提取出各位数字,再进行操作 操作(1):对10取余 ...

  7. 赞!Google 资深软件工程师 LeetCode 刷题笔记首次公开

    有人说写代码就像我们平时开车,仅凭经验你就可以将车开走:但当有一天,这辆车出问题跑不起来的时候,你不懂汽车的运行机制,你要怎么排除和解决问题?所以拥有扎实的数据结构和算法,才能开好编程这辆车. 作为程 ...

  8. 无意中发现的一份清华大佬的刷题笔记!

    对于刷题相关的文章,在之前我也推荐过不少,今天在给大家推荐一份算法刷题笔记,这份笔记与以往的刷题有所区别,作者把 Leetcode 一千多道题都进行了系统的整理,并且对于每一道题的代码,都要求 bea ...

  9. 《Data Structures and Algorithm Analysis in C》学习与刷题笔记

    <Data Structures and Algorithm Analysis in C>学习与刷题笔记 为什么要学习DSAAC? 某个月黑风高的夜晚,下班的我走在黯淡无光.冷清无人的冲之 ...

最新文章

  1. bmp文件头_「正点原子FPGA连载」第十九章SD卡读BMP图片LCD显示
  2. Linux平台下QtCreator集成代码静态分析工具clang-tidy和Clazy
  3. java中路径中参数化_Azure数据工厂:参数化文件夹和文件路径
  4. coroSync packmarker
  5. C++反转字符串的算法(附完整源码)
  6. 基于DVWA的CSRF
  7. 奥巴马写的c语言正方形,团体程序设计天梯赛-练习集 L1-015 跟奥巴马一起画方块...
  8. js系列教程1-数组操作全解
  9. gdb 7.X 下载编译(aarch64)
  10. onfigure: error: cannot find install-sh, install.sh, or shtool in
  11. 硬件工程师面试基础知识点
  12. 测试学习——全链路压测
  13. 机器人动力学(雅克比)
  14. 74HC/LS/HCT/F系列芯片的区别及使用[转]
  15. Golang的基础数据类型
  16. 雷达原理-雷达接收机
  17. table 手机 滑动_移动端touch事件滚动
  18. 如何用html制作田字格,WPS文字怎么制作田字格 WPS文字制作田字格的方法
  19. 汇编指令学习(AND,OR,XOR,NOT)
  20. 【转】一个交警肺腑之言:高速公路行车的安全注意事项

热门文章

  1. 自我投资,最好的方式就是写作
  2. 扩展 HashMap
  3. As Foxit Software disclosed in its prospectus
  4. Qt4_在次线程中使用Qt的类
  5. 几种常见的分布及其性质
  6. 小心编译器的隐式声明
  7. sql过滤代码段_如何创建和管理T-SQL代码段
  8. 如何查找数据库服务器ip_多服务器管理–查找数据库服务器
  9. iphone 低版本渲染不出来内容的一种解决办法
  10. python 空值(NoneType)