1.剑指 Offer 68 - II. 二叉树的最近公共祖先 2022.7.17
力扣算法 Java 刷题笔记【二叉树篇】hot100(十)GIT原理之最近公共祖先(二叉树的最近公共祖先)中等 1
五个变式
class Solution {
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
if (root == null) {
return null;
}
if (root == p || root == q) {
return root;
}
TreeNode left = lowestCommonAncestor(root.left, p, q);
TreeNode right = lowestCommonAncestor(root.right, p, q);
if (left == null && right == null) {
return null;
}
if (left != null && right != null) {
return root;
}
return left == null ? right : left;
}
}
/*
class Solution {
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
if (root == null) {
return null;
}
if (root == p || root == q) {
return root;
}
TreeNode left = lowestCommonAncestor(root.left, p, q);
TreeNode right = lowestCommonAncestor(root.right, p, q);
if (left == null) {
return right;
}
if (right == null) {
return left;
}
return root;
}
}
*/
剑指 Offer 10- I. 斐波那契数列 2022.7.17
力扣算法 Java 刷题笔记【动态规划篇 DP】hot100(一)动态规划解题核心框架 & 斐波那契数 & 零钱兑换 5
解法一:自顶向下(备忘录)–递归
class Solution {
// static final int MOD = 1000000007;
int[] memo = null;
public int fib(int n) {
if (n <= 1) {
return n;
}
if (memo == null) {
memo = new int[n + 1];
}
if (memo[n] != 0) {
return memo[n];
}
memo[n] = fib(n - 1) + fib(n - 2); // 取模 memo[n] = (fib(n - 1) + fib(n - 2)) % MOD;
return memo[n];
}
}
解法二:自底向上(dp)–迭代
class Solution {
public int fib(int n) {
if (n == 0) {
return 0;
}
int[] dp = new int[n + 1];
dp[0] = 0; dp[1] = 1;
for (int i = 2; i <= n; i++) {
dp[i] = dp[i - 1] + dp[i - 2]; // 取模 dp[i] = (dp[i - 1] + dp[i - 2]) % 1000000007;
}
return dp[n];
}
}
剑指 Offer 10- II. 青蛙跳台阶问题 2022.7.17
力扣算法 Java 刷题笔记【动态规划篇 DP】hot100(一)动态规划解题核心框架 & 斐波那契数 & 零钱兑换 5
class Solution {
static final int MOD = 1000000007;
public int numWays(int n) {
if (n <= 1) {
return 1;
}
int[] dp = new int[n + 1];
dp[1] = 1; dp[2] = 2;
for (int i = 3; i <= n; i++) {
dp[i] = (dp[i - 1] + dp[i - 2]) % MOD;
}
return dp[n];
}
}
剑指 Offer 25. 合并两个排序的链表 2022.7.17
力扣算法 Java 刷题笔记【链表篇】hot100(一)合并 K 个升序链表(困难) 删除链表的倒数第 N 个结点(中等) 7
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
ListNode dummy = new ListNode(-1), p = dummy;
while (l1 != null && l2 != null) {
if (l1.val > l2.val) {
p.next = l2;
l2 = l2.next;
} else {
p.next = l1;
l1 = l1.next;
}
p = p.next;
}
if (l1 == null) {
p.next = l2;
}
if (l2 == null) {
p.next = l1;
}
return dummy.next;
}
}
剑指 Offer 24. 反转链表 2022.7.17
力扣算法 Java 刷题笔记【链表篇】hot100(二)递归反转链表的一部分 4
解法一:递归
class Solution {
public ListNode reverseList(ListNode head) {
if (head == null || head.next == null) {
return head;
}
ListNode last = reverseList(head.next);
head.next.next = head;
head.next = null;
return last;
}
}
解法二:迭代
class Solution {
public ListNode reverseList(ListNode head) {
ListNode pre = null, cur = head, nxt = head;
while (cur != null) {
nxt = cur.next;
cur.next = pre;
pre = cur;
cur = nxt;
}
return pre;
}
}
剑指 Offer 27. 二叉树的镜像 2022.7.18
力扣算法 Java 刷题笔记【二叉树篇】hot100(一)翻转二叉树 填充每个节点的下一个右侧节点指针(中等) 二叉树展开为链表(中等)3
// 后序遍历
class Solution {
public TreeNode mirrorTree(TreeNode root) {
if (root == null) {
return null;
}
TreeNode left = mirrorTree(root.left);
TreeNode right = mirrorTree(root.right);
TreeNode node = new TreeNode();
node = left;
root.left = right;
root.right = node;
return root;
}
}
/*
class Solution {
public TreeNode mirrorTree(TreeNode root) {
if (root == null) {
return null;
}
TreeNode node = root.left;
root.left = mirrorTree(root.right);
root.right = mirrorTree(node);
return root;
}
}
*/
/* 前序遍历
class Solution {
public TreeNode mirrorTree(TreeNode root) {
if (root == null) {
return null;
}
TreeNode node = root.left;
root.left = root.right;
root.right = node;
mirrorTree(root.left);
mirrorTree(root.right);
return root;
}
}
*/
剑指 Offer 55 - I. 二叉树的深度 2022.7.18
力扣算法 Java 刷题笔记【二叉树篇】hot100(十三) 二叉树的最大深度 对称二叉树 相同的树 3
class Solution {
public int maxDepth(TreeNode root) {
if (root == null) {
return 0;
}
int leftMax = maxDepth(root.left);
int rightMax = maxDepth(root.right);
return Math.max(leftMax, rightMax) + 1; // return leftDepth > rightDepth ? leftDepth + 1 : rightDepth + 1;
}
}
剑指 Offer 29. 顺时针打印矩阵 2022.7.18
力扣算法 Java 刷题笔记【数组篇】hot100(五)二维数组的花式遍历 3
class Solution {
public int[] spiralOrder(int[][] matrix) {
int m = matrix.length;
if (m == 0) {
return new int[0];
}
int n = matrix[0].length;
int upper_bound = 0, lower_bound = m - 1;
int left_bound = 0, right_bound = n - 1;
int[] res = new int[m * n];
res[m * n - 1] = -1;
int id = 0;
while (res[m * n - 1] == -1) { // 注意循环条件-->此处仍有改进空间
if (upper_bound <= lower_bound) {
for (int i = left_bound; i <= right_bound; i++) {
res[id++] = matrix[upper_bound][i];
}
upper_bound++;
}
if (left_bound <= right_bound) {
for (int i = upper_bound; i <= lower_bound; i++) {
res[id++] = matrix[i][right_bound];
}
right_bound--;
}
if (upper_bound <= lower_bound) {
for (int i = right_bound; i >= left_bound; i--) {
res[id++] = matrix[lower_bound][i];
}
lower_bound--;
}
if (left_bound <= right_bound) {
for (int i = lower_bound; i >= upper_bound; i--) {
res[id++] = matrix[i][left_bound];
}
left_bound++;
}
}
return res;
}
}
剑指 Offer 68 - I. 二叉搜索树的最近公共祖先 2022.7.18
五个变式
方法一:同二叉树(题1)
方法二:利用二叉搜索树的性质
class Solution {
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
int val1 = Math.min(p.val, q.val);
int val2 = Math.max(p.val, q.val);
return find(root, val1, val2);
}
public TreeNode find(TreeNode root, int val1, int val2) {
if (root == null) {
return null;
}
if (val1 > root.val) {
return find(root.right, val1, val2);
}
if (val2 < root.val) {
return find(root.left, val1, val2);
}
return root;
}
}
[剑指 Offer 52. 两个链表的第一个公共节点](https://leetcode.cn/problems/liang-ge-lian-biao-de-di-yi-ge-gong-gong-jie-dian-lcof/ 2022.7.19
力扣算法 Java 刷题笔记【链表篇】hot100(一)合并 K 个升序链表(困难) 删除链表的倒数第 N 个结点(中等) 7
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) {
* val = x;
* next = null;
* }
* }
*/
public class Solution {
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
ListNode p1 = headA, p2 = headB;
while (p1 != p2) {
if (p1 == null) {
p1 = headB;
} else {
p1 = p1.next;
}
if (p2 == null) {
p2 = headA;
} else {
p2 = p2.next;
}
}
return p1;
}
}
剑指 Offer 28. 对称的二叉树 2022.7.19
力扣算法 Java 刷题笔记【二叉树篇】hot100(十三) 二叉树的最大深度 对称二叉树 相同的树 3
class Solution {
public boolean isSymmetric(TreeNode root) {
if (root == null) {
return true;
}
return check(root.left, root.right);
}
boolean check(TreeNode left, TreeNode right) {
if (left == null || right == null) {
return left == right;
}
if (left.val != right.val) {
return false;
}
return check(left.left, right.right) && check(left.right, right.left);
}
}
剑指 Offer 48. 最长不含重复字符的子字符串 2022.7.19
力扣算法 Java 刷题笔记【数组篇 滑动窗口算法】hot100(四)最小覆盖子串、字符串的排列、异位词、 无重复字符的最长子串 4
class Solution {
public int lengthOfLongestSubstring(String s) {
HashMap<Character, Integer> window = new HashMap<>();
int l = 0, r = 0;
int res = 0;
while (r < s.length()) {
char c = s.charAt(r);
window.put (c, window.getOrDefault(c, 0) + 1);
r++;
while (window.get(c) > 1) {
window.put(s.charAt(l), window.get(s.charAt(l)) - 1);
l++;
}
res = Math.max(res, r - l);
}
return res;
}
}
剑指 Offer 42. 连续子数组的最大和 2022.7.19
力扣算法 Java 刷题笔记【动态规划篇 DP 子序列类型问题】hot100(二) LIS 最长递增子序列及其变形 3
方法一:动态规划(不是最优解)
class Solution {
public int maxSubArray(int[] nums) {
int n = nums.length;
int[] dp = new int[n];
dp[0] = nums[0];
for (int i = 1; i < nums.length; i++) {
dp[i] = Math.max(nums[i], dp[i - 1] + nums[i]);
}
int res = Integer.MIN_VALUE;
for (int i = 0; i < nums.length; i++) {
res = Math.max(dp[i], res);
}
return res;
}
}
最优解
class Solution {
public int maxSubArray(int[] nums) {
int dp = nums[0];
int max = nums[0];
for (int i = 1; i < nums.length; i++) {
dp = Math.max(dp + nums[i], nums[i]);
max = Math.max(dp, max);
}
return max;
}
}
剑指 Offer 06. 从尾到头打印链表 2022.7.20
力扣算法 Java 刷题笔记【链表篇】hot100(二)递归反转链表的一部分 4
最优解:时间 O(n) 空间 O(1)
从右边往左填充
public int[] reversePrint(ListNode head) {
if(head == null)
return new int[0];
// 统计链表节点个数,方便创建数组
int count = 0;
ListNode temp = head;
while(temp != null){
count++;
temp = temp.next;
}
int[] res = new int[count];
int k = count - 1;
// 从右往左填充数组
while(head != null){
res[k--] = head.val;
head = head.next;
}
return res;
}
方法一:反转后取值
class Solution {
int count = 0;
public int[] reversePrint(ListNode head) {
if (head == null) {
return new int[0];
}
ListNode last = reverseNode(head);
int[] res = new int[count];
for (int i = 0; last != null; i++) {
res[i] = last.val;
last = last.next;
}
return res;
}
ListNode reverseNode(ListNode head) {
count++;
if (head == null || head.next == null) {
return head;
}
ListNode last = reverseNode(head.next);
head.next.next = head;
head.next = null;
return last;
}
}
方法二:栈
class Solution {
public int[] reversePrint(ListNode head) {
Stack<ListNode> stack = new Stack<ListNode>();
ListNode temp = head;
while (temp != null) {
stack.push(temp);
temp = temp.next;
}
int size = stack.size();
int[] print = new int[size];
for (int i = 0; i < size; i++) {
print[i] = stack.pop().val;
}
return print;
}
}
剑指 Offer 18. 删除链表的节点 2022.7.20
力扣算法 Java 刷题笔记【链表篇】hot100(一)合并 K 个升序链表(困难) 删除链表的倒数第 N 个结点(中等) 7
方法一:常规解法
class Solution {
public ListNode deleteNode(ListNode head, int val) {
ListNode res = head;
if (head.val == val) {
return head.next;
}
while (head != null) {
if (head.next == null) {
break;
}
if (head.next.val == val) {
head.next = head.next.next;
}
head = head.next;
}
return res;
}
}
方法二:递归
class Solution {
public ListNode deleteNode(ListNode head, int val) {
if (head == null) {
return null;
}
if (head.val == val) {
return head.next;
} else {
head.next = deleteNode(head.next, val);
}
return head;
}
}
方法三:双指针
class Solution {
public ListNode deleteNode(ListNode head, int val) {
if (head.val == val) {
return head.next;
}
ListNode pre = head, cur = head.next;
while (cur != null && cur.val != val) {
pre = cur;
cur = cur.next;
}
if (cur != null) {
pre.next = cur.next;
}
return head;
}
}
剑指 Offer 09. 用两个栈实现队列 2022.7.20
class CQueue {
private Stack<Integer> s1, s2;
public CQueue() {
s1 = new Stack<>();
s2 = new Stack<>();
}
public void appendTail(int value) {
s1.push(value);
}
public int deleteHead() {
if (s2.isEmpty()) {
while (!s1.isEmpty()) {
s2.push(s1.pop());
}
}
if (s2.size() == 0) {
return -1;
}
return s2.pop();
}
}
/**
* Your CQueue object will be instantiated and called as such:
* CQueue obj = new CQueue();
* obj.appendTail(value);
* int param_2 = obj.deleteHead();
*/
剑指 Offer 64. 求1+2+…+n 2022.7.20
class Solution {
int res = 0;
public int sumNums(int n) {
if (n == 1) {
return 1;
}
res = n + sumNums(n - 1);
return res;
}
}
树->遍历框架
链表->双指针、虚拟头节点
注意边界
剑指 Offer 55 - II. 平衡二叉树 2022.7.21
class Solution {
public boolean isBalanced(TreeNode root) {
maxDeepth(root);
return isBalancedTree;
}
boolean isBalancedTree = true;
int maxDeepth(TreeNode root) {
if (root == null) {
return 0;
}
int leftMaxDeepth = maxDeepth(root.left);
int rightMaxDeepth = maxDeepth(root.right);
if (Math.abs(leftMaxDeepth - rightMaxDeepth) > 1) {
isBalancedTree = false;
}
return 1 + Math.max(leftMaxDeepth, rightMaxDeepth);
}
}
剑指 Offer 05. 替换空格 2022.7.21
class Solution {
public String replaceSpace(String s) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
if (c == ' ') {
sb.append("%20");
}else {
sb.append(c);
}
}
return sb.toString();
}
}
剑指 Offer 54. 二叉搜索树的第k大节点 2022.7.21
力扣算法 Java 刷题笔记【二叉搜索树 BST 篇】hot100(一)BST 中序遍历的应用: BST 第 K 小的元素(中等)二叉搜索树转化累加树(中等)3
class Solution {
public int kthLargest(TreeNode root, int k) {
traverse(root, k);
return res;
}
int i = 0, res = 0;
void traverse(TreeNode root, int k) {
if (root == null) {
return;
}
traverse(root.right, k);
i++;
if (i == k) {
res = root.val;
return;
}
traverse(root.left, k);
}
}
剑指 Offer 22. 链表中倒数第k个节点 2022.7.21
力扣算法 Java 刷题笔记【链表篇】hot100(一)合并 K 个升序链表(困难) 删除链表的倒数第 N 个结点(中等) 7
方法一:栈
class Solution {
public ListNode getKthFromEnd(ListNode head, int k) {
Stack<ListNode> sk = new Stack<>();
while (head != null) {
sk.push(head);
head = head.next;
}
for (int i = 1; i < k; i++) {
sk.pop();
}
return sk.pop();
}
}
方法二:双指针
class Solution {
public ListNode getKthFromEnd(ListNode head, int k) {
ListNode slow = head, fast = head;
for (int i = 0; i < k; i++) {
fast = fast.next;
}
while (fast != null) {
fast = fast.next;
slow = slow.next;
}
return slow;
}
}
剑指 Offer 03. 数组中重复的数字 2022.7.21
哈希表:时间 O(n) 空间 O(n) 我们更关注时间复杂度
class Solution {
public int findRepeatNumber(int[] nums) {
HashMap<Integer, Integer> hashMap = new HashMap<>();
for (int i = 0; i < nums.length; i++) {
int val = nums[i];
hashMap.put(val, hashMap.getOrDefault(val, 0) + 1);
if (hashMap.get(val) > 1) {
return val;
}
}
return -1;
}
}
最优解:
下标法:时间 O(n) 空间 O(1) —> 看到 0~n-1 联想下标法
下标法:通过不停交换元素,使得元素和它所对应的下标相等: nums[i] = i
public int findRepeatNumber(int[] nums) {
// 遍历数组
for(int i = 0; i < nums.length; i++) {
// 之所以用while,是因为交换之后,改位置的元素任然没有在正确的位置
while(i != nums[i]){
if(nums[i] == nums[nums[i]]){
return nums[i];
}
// nums[i] 正确的位置在 nums[nums[i]]
int k = nums[nums[i]];
nums[nums[i]] = nums[i];
nums[i] = k;
}
}
return -1;
}
剑指 Offer 04. 二维数组中的查找 2022.7.21
class Solution {
public boolean findNumberIn2DArray(int[][] matrix, int target) {
if (matrix.length == 0) { //matrix == null || matrix.length <= 0 || matrix[0].length <= 0
return false;
}
int n = matrix.length, m = matrix[0].length;
int row = 0, col = m - 1;
while (row < n && col >= 0) {
if (matrix[row][col] > target) {
col--;
} else if (matrix[row][col] < target) {
row++;
} else {
return true;
}
}
return false;
}
}
剑指 Offer 07. 重建二叉树 2022.7.22
力扣算法 Java 刷题笔记【二叉树篇】hot100(二)最大二叉树(中等) 从前序与中序遍历序列构造二叉树(中等) 3
class Solution {
public TreeNode buildTree(int[] preorder, int[] inorder) {
return build(preorder, 0, preorder.length - 1,
inorder, 0, inorder.length - 1);
}
TreeNode build(int[] preorder, int preStart, int preEnd,
int[] inorder, int inStart, int inEnd) {
if (preStart > preEnd || inStart > inEnd) {
return null;
}
int index = 0, rootVal = preorder[preStart];
for (int i = inStart; i <= inEnd; i++) {
if (inorder[i] == rootVal) {
index = i;
break;
}
}
int leftSize = index - inStart;
TreeNode root = new TreeNode(rootVal);
root.left = build(preorder, preStart + 1, preStart + leftSize,
inorder, inStart, index - 1);
root.right = build(preorder, preStart + leftSize + 1, preEnd,
inorder, index + 1, inEnd);
return root;
}
}
剑指 Offer 17. 打印从1到最大的n位数 2022.7.22
class Solution {
public int[] printNumbers(int n) {
int max = (int)Math.pow(10, n);
int[] res = new int[max - 1];
for (int i = 0; i < max - 1; i++) {
res[i] = i + 1;
}
return res;
}
}
大数问题:
剑指 Offer 15. 二进制中1的个数 2022.7.24
public class Solution {
// you need to treat n as an unsigned value
public int hammingWeight(int n) {
int res = 0;
while (n != 0) {
n = n & (n - 1);
res++;
}
return res;
}
}
剑指 Offer 13. 机器人的运动范围 2022.7.24
力扣算法 Java 刷题笔记【DFS篇】hot100(三)岛屿问题(一) 3
力扣算法 Java 刷题笔记【DFS篇】hot100(三)岛屿问题(二) 2|1
class Solution {
int res = 0;
public int movingCount(int m, int n, int k) {
boolean[][] visited = new boolean[m][n];
dfs(m, n, 0, 0, k, visited);
return res;
}
void dfs(int m, int n, int i, int j, int k, boolean[][] visited) {
if (i < 0 || j < 0 || i > m - 1 || j > n - 1) {
return;
}
if (i / 10 + i % 10 + j / 10 + j % 10 > k) {
return;
}
if (visited[i][j] == true) {
return;
}
res++;
visited[i][j] = true;
dfs(m, n, i - 1, j, k, visited);
dfs(m, n, i + 1, j, k, visited);
dfs(m, n, i, j - 1, k, visited);
dfs(m, n, i, j + 1, k, visited);
}
}
剑指 Offer 26. 树的子结构 2022.7.24
力扣算法 Java 刷题笔记【二叉树篇】hot100(十三) 二叉树的最大深度 对称二叉树 相同的树 3
class Solution {
public boolean isSubStructure(TreeNode A, TreeNode B) {
if (A == null || B == null) {
return A == B;
}
if (A.val == B.val && check(A, B)) {
return true;
}
return isSubStructure(A.left, B) || isSubStructure(A.right, B);
}
boolean check(TreeNode A, TreeNode B) {
if (B == null) {
return true;
}
if (A == null && B != null) {
return false;
}
if (A.val != B.val) {
return false;
}
return check(A.left, B.left) && check(A.right, B.right);
}
}
剑指 Offer 21. 调整数组顺序使奇数位于偶数前面 2022.7.24
力扣算法 Java 刷题笔记【数组篇】hot100(三)去除有序数组的重复元素 4
class Solution {
public int[] exchange(int[] nums) {
int slow = 0, fast = 0;
while (fast < nums.length) {
if (nums[fast] % 2 == 1) {
int temp = nums[slow];
nums[slow] = nums[fast];
nums[fast] = temp;
slow++;
}
fast++;
}
return nums;
}
}
剑指 Offer 32 - II. 从上到下打印二叉树 II 2022.7.25
class Solution {
public List<List<Integer>> levelOrder(TreeNode root) {
List<List<Integer>> res = new LinkedList<>();
if (root == null) {
return res;
}
Queue<TreeNode> q = new LinkedList<>();
q.offer(root);
while (!q.isEmpty()) {
List<Integer> temp = new LinkedList<>();
int sz = q.size();
for (int i = 0; i < sz; i++) {
TreeNode cur = q.poll();
temp.add(cur.val);
if (cur.left != null) {
q.offer(cur.left);
}
if (cur.right != null) {
q.offer(cur.right);
}
}
res.add(temp);
}
return res;
}
}
剑指 Offer 57. 和为s的两个数字 2022.7.25
力扣算法 Java 刷题笔记【nSum 问题】hot100(一)团灭 nSum 3
class Solution {
public int[] twoSum(int[] nums, int target) {
int slow = 0, fast = nums.length - 1;
while (slow < fast) {
int sum = nums[slow] + nums[fast];
if (sum < target) {
slow++;
}
if (sum > target) {
fast--;
}
if (sum == target) {
return new int[]{nums[slow], nums[fast]};
}
}
return null;
}
}
剑指 Offer 16. 数值的整数次方 2022.7.25
class Solution {
public double myPow(double x, int n) {
if (n == 0) {
return 1;
}
if (n == Integer.MIN_VALUE) {
return myPow(1 / x, -(n + 1)) / x;
}
if (n < 0) {
return myPow(1 / x, -n);
}
if (n % 2 == 1) {
return myPow(x, n - 1) * x;
}
if (n % 2 == 0) {
double sub = myPow(x, n / 2);
return sub * sub;
}
return -1;
// if (n % 2 == 1) {
// return myPow(x, n - 1) * x;
// } else {
// double sub = myPow(x, n / 2);
// return sub * sub;
// }
}
}
剑指 Offer 32 - I. 从上到下打印二叉树 2022.7.25
class Solution {
public int[] levelOrder(TreeNode root) {
LinkedList<Integer> res = new LinkedList<>();
if (root == null) {
return new int[0];
}
Queue<TreeNode> q = new LinkedList<>();
q.offer(root);
while (!q.isEmpty()) {
int sz = q.size();
for (int i = 0; i < sz; i++) {
TreeNode cur = q.poll();
res.add(cur.val);
if (cur.left != null) {
q.offer(cur.left);
}
if (cur.right != null) {
q.offer(cur.right);
}
}
}
// return res.stream().mapToInt(Integer::valueOf).toArray();
int[] ans = new int[res.size()];
for (int i = 0; i < res.size(); i++) {
ans[i] = res.get(i);
}
return ans;
}
}
剑指 Offer 32 - III. 从上到下打印二叉树 III 2022.7.25
双端对列
复杂情况还是用笔画一画,更清晰->辅助分析
class Solution {
public List<List<Integer>> levelOrder(TreeNode root) {
List<List<Integer>> res = new LinkedList<>();
if (root == null) {
return res;
}
Deque<TreeNode> dq = new LinkedList<>();
dq.offer(root);
int count = 0;
while (!dq.isEmpty()) {
List<Integer> temp = new LinkedList<>();
int sz = dq.size();
if (count % 2 == 0) {
for (int i = 0; i < sz; i++) {
TreeNode cur = dq.pollFirst();
temp.add(cur.val);
if (cur.left != null) {
dq.offerLast(cur.left);
}
if (cur.right != null) {
dq.offerLast(cur.right);
}
}
}
if (count % 2 == 1) {
for (int i = 0; i < sz; i++) {
TreeNode curr = dq.pollLast();
temp.add(curr.val);
if (curr.right != null) {
dq.offerFirst(curr.right);
}
if (curr.left != null) {
dq.offerFirst(curr.left);
}
}
}
count++;
res.add(temp);
}
return res;
}
}
剑指 Offer 30. 包含min函数的栈 2022.7.26
class MinStack {
Stack<Integer> sk = new Stack<>();
Stack<Integer> minStack = new Stack<>();
/** initialize your data structure here. */
public MinStack() {
}
public void push(int x) {
if (minStack.isEmpty() || x <= minStack.peek()) {
minStack.push(x);
}
sk.push(x);
}
public void pop() {
if (minStack.peek().equals(sk.peek())) {
minStack.pop();
}
sk.pop();
}
public int top() {
return sk.peek();
}
public int min() {
return minStack.peek();
}
}
剑指 Offer 38. 字符串的排列 2022.7.26
力扣算法 Java 刷题笔记【回溯算法篇 DFS】hot100(一)全排列 、子集 、组合 4
class Solution {
public String[] permutation(String s) {
permutaUnique(s.toCharArray());
String[] arr = new String[res.size()];
for (int i = 0; i < res.size(); i++) {
arr[i] = res.get(i);
}
return arr;
}
List<String> res = new LinkedList<>();
boolean[] used;
StringBuilder track = new StringBuilder();
public List<String> permutaUnique(char[] nums) {
Arrays.sort(nums);
used = new boolean[nums.length];
backtrack(nums);
return res;
}
void backtrack(char[] nums) {
if (track.length() == nums.length) {
res.add(track.toString());
return;
}
for (int i = 0; i < nums.length; i++) {
if (used[i] == true) {
continue;
}
if (i > 0 && nums[i] == nums[i - 1] && !used[i - 1]) {
continue;
}
track.append(nums[i]);
used[i] = true;
backtrack(nums);
track.deleteCharAt(track.length() - 1);
used[i] = false;
}
}
}
剑指 Offer 36. 二叉搜索树与双向链表 2022.7.26
class Solution {
Node pre, head;
public Node treeToDoublyList(Node root) {
if (root == null) {
return null;
}
traverse(root);
head.left = pre;
pre.right = head;
return head;
}
void traverse(Node root) {
if (root == null) {
return;
}
traverse(root.left);
if (pre == null) {
head = root;
} else {
pre.right = root;
root.left = pre;
}
pre = root;
traverse(root.right);
}
}
剑指 Offer 53 - II. 0~n-1中缺失的数字 2022.7.26
力扣算法 Java 刷题笔记【数组篇 二分搜索】hot100(一)二分查找、搜索插入位置、在排序数组中查找元素的第一个和最后一个位置 3
class Solution {
public int missingNumber(int[] nums) {
int left = 0, right = nums.length - 1;
while (left <= right) {
int mid = left + (right - left) / 2;
if (nums[mid] > mid) {
right = mid - 1;
} else {
left = mid + 1;
}
}
return left;
}
}
剑指 Offer 11. 旋转数组的最小数字 2022.7.26
class Solution {
public int minArray(int[] numbers) {
int l = 0, r = numbers.length - 1;
while (l < r) {
if (numbers[l] < numbers[r]) {
return numbers[l];
}
int mid = l + (r - l) / 2;
if (numbers[mid] > numbers[l]) {
l = mid + 1;
} else if (numbers[mid] < numbers[l]) {
r = mid;
} else {
l++;
}
}
return numbers[l];
}
}
剑指 Offer 65. 不用加减乘除做加法 2022.7.27
class Solution {
public int add(int a, int b) {
if(a == 0 || b == 0) {
return a == 0 ? b : a;
}
// 设 a = 1001
// 设 b = 0101
// 求和 1100
int sum = a ^ b;
// 进位 0001 << 1 = 0010
int carry = (a & b) << 1;
// add(1100, 0010)
return add(sum, carry);
}
}
剑指 Offer 14- I. 剪绳子 2022.7.28
class Solution {
public int cuttingRope(int n) {
if (n <= 2) {
return 1;
}
if (n == 3) {
return 2;
}
if (n == 4) {
return 4;
}
int res = n / 3;
int mod = n % 3;
if (n % 3 == 0) {
return (int)Math.pow(3, res);
} else if (n % 3 == 1) {
return (int)(Math.pow(3, res - 1) * 4);
} else {
return (int)(Math.pow(3, res) * 2);
}
}
}
剑指 Offer 14- II. 剪绳子 II 2022.7.28
class Solution {
public int cuttingRope(int n) {
if (n < 4) {
return n - 1;
}
int res = n / 3;
int mod = n % 3;
int p = 1000000007;
if (mod == 0) {
return (int)(pow(3, res));
} else if (mod == 1) {
return (int)(pow(3, res - 1) * 4 % p);
} else {
return (int)(pow(3, res) * 2 % p);
}
}
long pow (int a, int n) {
long sum = 1;
int p = 1000000007;
for (int i = 0; i < n; i++) {
sum = sum * a % p;
}
return sum;
}
}
剑指 Offer 33. 二叉搜索树的后序遍历序列 2022.7.28
class Solution {
public boolean verifyPostorder(int[] postorder) {
return verify(postorder, 0, postorder.length - 1);
}
boolean verify(int[] postorder, int start, int end) {
if (start >= end) {
return true;
}
int p = start;
while (postorder[p] < postorder[end]) {
p++;
}
int m = p;
while (postorder[m] > postorder[end]) {
m++;
}
return m == end && verify(postorder, start, p - 1) && verify(postorder, p, m - 1);
}
}
剑指 Offer 50. 第一个只出现一次的字符 2022.7.28
class Solution {
public char firstUniqChar(String s) {
int[] nums = new int[26];
for (int i = 0; i < s.length(); i++) {
nums[s.charAt(i) - 'a']++;
}
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
if (nums[c - 'a'] == 1) {
return c;
}
}
return ' ';
}
}
剑指 Offer 34. 二叉树中和为某一值的路径 2022.7.28
class Solution {
List<List<Integer>> res = new LinkedList<>();
// LinkedList path = new LinkedList<>();
List<Integer> path = new LinkedList<>();
public List<List<Integer>> pathSum(TreeNode root, int target) {
dfs(root, target);
return res;
}
void dfs(TreeNode root, int sum) {
if (root == null) {
return;
}
path.add(root.val);
sum = sum - root.val;
if (root.left == null && root.right == null && sum == 0) {
res.add(new LinkedList<>(path));
}
dfs(root.left, sum);
dfs(root.right, sum);
// path.removeLast();
path.remove(path.size() - 1);
}
}
剑指 Offer 53 - I. 在排序数组中查找数字 I 2022.7.29
力扣算法 Java 刷题笔记【数组篇 二分搜索】hot100(一)二分查找、搜索插入位置、在排序数组中查找元素的第一个和最后一个位置 3
class Solution {
public int search(int[] nums, int target) {
int left_index = left_bound(nums, target);
if (left_index == -1) {
return 0;
}
int right_index = right_bound(nums, target);
return right_index - left_index + 1;
}
int left_bound(int[] nums, int target) {
int left = 0, right = nums.length - 1;
while (left <= right) {
int mid = left + (right - left) / 2;
if (nums[mid] < target) {
left = mid + 1;
} else if (nums[mid] > target) {
right = mid - 1;
} else if (nums[mid] == target) {
right = mid - 1;
}
}
if (left >= nums.length || nums[left] != target) {
return -1;
}
return left;
}
int right_bound(int[] nums, int target) {
int left = 0, right = nums.length - 1;
while (left <= right) {
int mid = left + (right - left) / 2;
if (nums[mid] < target) {
left = mid + 1;
} else if (nums[mid] > target) {
right = mid - 1;
} else if (nums[mid] == target) {
left = mid + 1;
}
}
if (right < 0 || nums[right] != target) {
return -1;
}
return right;
}
}
剑指 Offer 40. 最小的k个数 2022.7.29
力扣算法 Java 刷题笔记【快速排序】hot100(一)快速排序 快速选择算法 1
方法一:优先队列
class Solution {
public int[] getLeastNumbers(int[] arr, int k) {
if (k == 0 || arr.length == 0) {
return new int[0];
}
PriorityQueue<Integer> pq = new PriorityQueue<>();
int[] res = new int[k];
int i = 0;
for (int a : arr) {
pq.offer(a);
if (pq.size() > arr.length - k) {
res[i] = pq.poll();
i++;
}
}
return res;
}
}
******** 另一种写法
class Solution {
public int[] getLeastNumbers(int[] arr, int k) {
if (k == 0 || arr.length == 0) {
return new int[0];
}
// 默认是小根堆,实现大根堆需要重写一下比较器。
Queue<Integer> pq = new PriorityQueue<>((v1, v2) -> v2 - v1);
for (int num: arr) {
if (pq.size() < k) {
pq.offer(num);
} else if (num < pq.peek()) {
pq.poll();
pq.offer(num);
}
}
int[] res = new int[pq.size()];
int idx = 0;
for(int num: pq) {
res[idx++] = num;
}
return res;
}
}
方法二:快排
剑指 Offer 39. 数组中出现次数超过一半的数字 2022.7.29
摩尔投票法(对拼消耗)
class Solution {
public int majorityElement(int[] nums) {
int count = 0;
int candicate = 0;
for (int num : nums) {
if (count == 0) {
candicate = num;
}
count += candicate == num ? 1 : -1;
}
return candicate;
}
}
剑指 Offer 12. 矩阵中的路径 2022.7.31
class Solution {
int m = 0, n = 0, len = 0;
public boolean exist(char[][] board, String word) {
m = board.length;
n = board[0].length;
len = word.length();
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
if (dfs(board, i, j, word, 0)) {
return true;
}
}
}
return false;
}
boolean dfs(char[][] board, int i, int j, String word, int k) {
if (i < 0 || i >= m || j < 0 || j >= n || board[i][j] != word.charAt(k)) {
return false;
}
if (k == len - 1) {
return true;
}
board[i][j] = '\n';
boolean res = dfs(board, i - 1, j, word, k + 1) ||
dfs(board, i + 1, j, word, k + 1) ||
dfs(board, i, j - 1, word, k + 1) ||
dfs(board, i, j + 1, word, k + 1);
board[i][j] = word.charAt(k);
return res;
}
}
剑指 Offer 19. 正则表达式匹配 2022.7.31
https://www.bilibili.com/video/BV1jd4y1U7kE/?spm_id_from=pageDriver&vd_source=28639105a09afd80125a0dc2b769abba
https://www.iamshuaidi.com/275.html
https://offer.iamshuaidi.com/84.html
class Solution {
public boolean isMatch(String s, String p) {
int n = s.length();
int m = p.length();
boolean[][] dp = new boolean[n + 1][m + 1]; // 默认是 false
dp[0][0] = true;
for (int j = 2; j <= m; j++) {
if (p.charAt(j - 1) == '*') { // * 可以等于 0
dp[0][j] = dp[0][j - 2];
}
}
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
if (p.charAt(j - 1) != '*') {
if (p.charAt(j - 1) == '.' || p.charAt(j - 1) == s.charAt(i - 1)) {
dp[i][j] = dp[i - 1][j - 1];
} else {
dp[i][j] = false;
}
} else { // 当 j = 1 时 由于第一位不可能为 '*' 故不会进入这个逻辑, j - 2 就不会越界
if (p.charAt(j - 2) != s.charAt(i - 1) && p.charAt(j - 2) != '.') {
dp[i][j] = dp[i][j - 2];
} else {
dp[i][j] = dp[i][j - 2] || dp[i - 1][j - 1] || dp[i - 1][j];
}
}
}
}
return dp[n][m];
}
}
剑指 Offer 58 - II. 左旋转字符串 2022.7.31
最优解
class Solution {
public String reverseLeftWords(String s, int n) {
StringBuilder sb = new StringBuilder();
int len = s.length();
for (int i = n; i < len + n; i++) {
sb.append(s.charAt(i % len));
}
return sb.toString();
}
}
方法一:
class Solution {
public String reverseLeftWords(String s, int n) {
int len = s.length();
StringBuilder sb = new StringBuilder(s);
reverseString(sb,0,n-1);
reverseString(sb,n,len-1);
return sb.reverse().toString();
}
public void reverseString(StringBuilder sb, int start, int end) {
while (start < end) {
char temp = sb.charAt(start);
sb.setCharAt(start, sb.charAt(end));
sb.setCharAt(end, temp);
start++;
end--;
}
}
}
方法二:
class Solution {
public String reverseLeftWords(String s, int n) {
return s.substring(n) + s.substring(0, n);
}
}
剑指 Offer 57 - II. 和为s的连续正数序列 2022.7.31
class Solution {
public int[][] findContinuousSequence(int target) {
List<int[]> list = new LinkedList<>();
for (int l = 1, r = 1, sum = 0; r < target; r++) {
sum += r;
while (sum > target) {
sum -= l;
l++;
}
if (sum == target) {
int[] temp = new int[r - l + 1];
for (int i = 0; i < temp.length; i++) {
temp[i] = l + i;
}
list.add(temp);
}
}
return list.toArray(new int[list.size()][]); //初始化一个能存list.size()个一维数组的二维数组
/*
int[][] ans = new int[list.size()][];
for (int i = 0; i < list.size(); i++) {
ans[i] = list.get(i);
}
return ans;
*/
}
}
剑指 Offer 31. 栈的压入、弹出序列
class Solution {
public boolean validateStackSequences(int[] pushed, int[] popped) {
if (pushed == null || pushed.length == 0) {
return true;
}
Stack<Integer> sk = new Stack();
int k = 0;
for (int i = 0; i < pushed.length; i++) {
sk.push(pushed[i]);
while (!sk.isEmpty() && sk.peek() == popped[k]) {
sk.pop();
k++;
}
}
return sk.isEmpty();
}
}
剑指 Offer 35. 复杂链表的复制 2022.8.9
class Solution {
public Node copyRandomList(Node head) {
if (head == null) {
return null;
}
Node cur = head;
while (cur != null) {
Node newNode = cur.next;
cur.next = new Node(cur.val);
cur.next.next = newNode;
cur = newNode;
}
cur = head;
while (cur != null) {
cur.next.random = cur.random == null ? null : cur.random.next;
cur = cur.next.next;
}
cur = head;
Node headNode = cur.next;
Node curNode = cur.next;
while (cur != null) {
cur.next = cur.next.next;
cur = cur.next;
curNode.next = cur == null ? null : cur.next;
curNode = curNode.next;
}
return headNode;
}
}
剑指 Offer 58 - I. 翻转单词顺序 2022.8.9
最优解
class Solution {
public String reverseWords(String s) {
if (s.length() == 0 || s == null) {
return s;
}
s = s.trim();
StringBuilder sb = new StringBuilder();
int i = s.length() - 1, j = i;
while (i >= 0) {
while (i >= 0 && s.charAt(i) != ' ') i--;
sb.append(s.substring(i + 1, j + 1) + " ");
while (i >= 0 && s.charAt(i) == ' ') i--;
j = i;
}
return sb.toString().trim();
}
}
class Solution {
public String reverseWords(String s) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
if (c != ' ') {
sb.append(c);
} else if (!sb.isEmpty() && sb.charAt(sb.length() - 1) != ' ') {
sb.append(' ');
}
}
if (sb.length() > 1 && sb.charAt(sb.length() - 1) == ' ') {
sb.deleteCharAt(sb.length() - 1);
}
reverse(sb, 0, sb.length() - 1);
for (int i = 0; i < sb.length(); ) {
for (int j = i; j < sb.length(); j++) {
if (j + 1 == sb.length() || sb.charAt(j + 1) == ' ') {
reverse(sb, i, j);
i = j + 2;
break;
}
}
}
return sb.toString();
}
void reverse(StringBuilder sb, int i, int j) {
while (i < j) {
char temp = sb.charAt(i);
sb.setCharAt(i, sb.charAt(j));
sb.setCharAt(j, temp);
i++;
j--;
}
}
}
剑指 Offer 49. 丑数 2022.8.10
class Solution {
public int nthUglyNumber(int n) {
int[] dp = new int[n + 1];
dp[1] = 1;
int a = 1, b = 1, c = 1;
for (int i = 2; i < n + 1; i++) {
dp[i] = Math.min(Math.min(dp[a] * 2, dp[b] * 3), dp[c] * 5);
if (dp[a] * 2 == dp[i]) {
a++;
}
if (dp[b] * 3 == dp[i]) {
b++;
}
if (dp[c] * 5 == dp[i]) {
c++;
}
}
return dp[n];
}
}
剑指 Offer 66. 构建乘积数组 2022.8.10
// 前缀积
class Solution {
public int[] constructArr(int[] a) {
if (a.length == 0) {
return new int[0];
}
int[] prefix = new int[a.length];
prefix[0] = a[0];
for (int i = 1; i < a.length; i++) {
prefix[i] = prefix[i - 1] * a[i];
}
int[] postfix = new int[a.length];
postfix[a.length - 1] = a[a.length - 1];
for (int j = a.length - 2; j >= 0; j--) {
postfix[j] = postfix[j + 1] * a[j];
}
int[] b = new int[a.length];
b[0] = postfix[1];
b[b.length - 1] = prefix[b.length - 2];
for (int k = 1; k < b.length - 1; k++) {
b[k] = prefix[k - 1] * postfix[k + 1];
}
return b;
}
}
剑指 Offer 61. 扑克牌中的顺子 2022.8.10
方法一
//集合 Set + 遍历
class Solution {
public boolean isStraight(int[] nums) {
Set<Integer> repeat = new HashSet<>();
int max = 0, min = 14;
for(int num : nums) {
if(num == 0) continue; // 跳过大小王
max = Math.max(max, num); // 最大牌
min = Math.min(min, num); // 最小牌
if(repeat.contains(num)) return false; // 若有重复,提前返回 false
repeat.add(num); // 添加此牌至 Set
}
return max - min < 5; // 最大牌 - 最小牌 < 5 则可构成顺子
}
}
方法二
class Solution {
public boolean isStraight(int[] nums) {
Arrays.sort(nums);
int joker = 0;
for (int i = 0; i < 4; i++) {
if (nums[i] == 0) {
joker++;
} else if ( nums[i] == nums[i + 1]) {
return false;
}
}
return nums[4] - nums[joker] < 5;
}
}
剑指 Offer 51. 数组中的逆序对 2022.8.22
// 归并排序(也可以用冒泡,但是冒泡时间复杂度太高)
class Solution {
public int reversePairs(int[] nums) {
int[] temp = new int[nums.length];
return mergeSort(nums, temp, 0, nums.length - 1);
}
int mergeSort(int[] nums, int[] temp, int lo, int hi) {
if (lo >= hi) {
return 0;
}
int middle = (hi - lo) / 2 + lo;
int x1 = mergeSort(nums, temp, lo, middle);
int x2 = mergeSort(nums, temp, middle + 1, hi);
int x3 = merge(nums, temp, middle, lo, hi);
return x1 + x2 + x3;
}
int merge(int[] nums, int[] temp, int middle, int lo, int hi) {
int count = 0;
int i = lo, j = middle + 1;
for (int k = lo; k <= hi; k++) {
if (i > middle) {
temp[k] = nums[j++];
} else if (j > hi) {
temp[k] = nums[i++];
} else if (nums[i] <= nums[j]) { // 边界条件
temp[k] = nums[i++];
} else {
count += middle + 1 - i;
temp[k] = nums[j++];
}
}
for (int p = lo; p <= hi; p++) {
nums[p] = temp[p];
}
return count;
}
}
剑指 Offer 47. 礼物的最大价值 2022.8.22
// 二维数组
class Solution {
public int maxValue(int[][] grid) {
int m = grid.length;
int n = grid[0].length;
int[][] dp = new int[m][n];
dp[0][0] = grid[0][0];
for (int i = 1; i < m; i++) {
dp[i][0] = dp[i - 1][0] + grid[i][0];
}
for (int j = 1; j < n; j++) {
dp[0][j] = dp[0][j - 1] + grid[0][j];
}
for (int i = 1; i < m; i++) {
for (int j = 1; j < n; j++) {
dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]) + grid[i][j];
}
}
return dp[m - 1][n - 1];
}
}
优化
// 一维数组:因为只用到上一行
class Solution {
public int maxValue(int[][] grid) {
int m = grid.length;
int n = grid[0].length;
int[] dp = new int[n];
dp[0] = grid[0][0];
for (int j = 1; j < n; j++) {
dp[j] = dp[j - 1] + grid[0][j];
}
for (int i = 1; i < m; i++) {
dp[0] = dp[0] + grid[i][0];
for (int j = 1; j < n; j++) {
dp[j] = Math.max(dp[j - 1], dp[j]) + grid[i][j];
}
}
return dp[n - 1];
}
}
剑指 Offer 45. 把数组排成最小的数 2022.8.22
// 快排 + 字符->字典序
class Solution {
public String minNumber(int[] nums) {
String[] strs = new String[nums.length];
for (int i = 0; i < nums.length; i++) {
strs[i] = String.valueOf(nums[i]);
}
quickSort(strs, 0, nums.length - 1);
StringBuilder sb = new StringBuilder();
for (int i = 0; i < strs.length; i++) {
sb.append(strs[i]);
}
return sb.toString();
}
void quickSort(String[] strs, int lo, int hi) {
if (lo >= hi) {
return;
}
int i = partition(strs, lo, hi);
quickSort(strs, lo, i - 1);
quickSort(strs, i + 1, hi);
}
int partition(String[] strs, int lo, int hi) {
String pivot = strs[lo];
int i = lo + 1, j = hi;
while (i <= j) {
while (j > lo && (strs[j] + pivot).compareTo(pivot + strs[j]) >= 0) {
j--;
}
while (i < hi && (strs[i] + pivot).compareTo(pivot + strs[i]) <= 0) {
i++;
}
if (i >= j) {
break;
}
swap(strs, i, j);
}
swap(strs, lo, j);
return j;
}
void swap(String[] strs, int i, int j) {
String temp = strs[i];
strs[i] = strs[j];
strs[j] = temp;
}
}
剑指 Offer 37. 序列化二叉树 2022.8.23
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
public class Codec {
// Encodes a tree to a single string.
public String serialize(TreeNode root) {
if (root == null) {
return "";
}
StringBuilder sb = new StringBuilder();
Queue<TreeNode> q = new LinkedList<>();
q.offer(root);
while (!q.isEmpty()) {
TreeNode t = q.poll();
if (t != null) {
q.add(t.left);
q.add(t.right);
sb.append(t.val + ",");
} else {
sb.append("null,");
}
}
return sb.toString();
}
// Decodes your encoded data to tree.
public TreeNode deserialize(String data) {
if (data == null || data.length() <= 0) {
return null;
}
String[] s = data.split(",");
Queue<TreeNode> q = new LinkedList<>();
TreeNode root = new TreeNode(Integer.parseInt(s[0]));
q.offer(root);
int i = 1;
while (!q.isEmpty()) {
TreeNode t = q.poll();
// 构建左节点
if (!s[i].equals("null")) {
TreeNode left = new TreeNode(Integer.parseInt(s[i]));
t.left = left;
q.offer(left);
}
i++;
// 构建右节点
if (!s[i].equals("null")) {
TreeNode right = new TreeNode(Integer.parseInt(s[i]));
t.right = right;
q.offer(right);
}
i++;
}
return root;
}
}
739. 每日温度 2022.7.27
class Solution {
public int[] dailyTemperatures(int[] temperatures) {
int[] res = new int[temperatures.length];
Stack<Integer> sk = new Stack<>(); //单调栈
for (int i = temperatures.length - 1; i >= 0; i--) {
while (!sk.isEmpty() && temperatures[sk.peek()] <= temperatures[i]) {
sk.pop();
}
res[i] = sk.isEmpty() ? 0 : sk.peek() - i;
sk.push(i);
}
return res;
}
}
15. 三数之和 2022.7.27
力扣算法 Java 刷题笔记【nSum 问题】hot100(一)团灭 nSum 3
class Solution {
public List<List<Integer>> threeSum(int[] nums) {
Arrays.sort(nums);
return nSum(nums, 3, 0, 0);
}
List<List<Integer>> nSum(int[] nums, int n, int start, int target) {
List<List<Integer>> res = new LinkedList<>();
if (n < 2 || nums.length < n) {
return res;
}
if (n == 2) {
int l = start, r = nums.length - 1;
while (l < r) {
int left = nums[l], right = nums[r];
int sum = left + right;
if (sum > target) {
r--;
}
if (sum < target) {
l++;
}
if (sum == target) {
res.add(Arrays.asList(left, right)); // Arrays.asList()
while (l < r && nums[l] == left) { // 剪枝
l++;
}
while (l < r && nums[r] == right) {
r--;
}
}
}
}
if (n > 2) {
for (int i = start; i < nums.length; i++) {
List<List<Integer>> tuples = nSum(nums, n - 1, i + 1, target - nums[i]);
for (List<Integer> tuple : tuples) {
List<Integer> t = new LinkedList<>(tuple); // 注意实例化
t.add(nums[i]);
res.add(t);
}
while (i < nums.length - 1 && nums[i] == nums[i + 1]) {
i++;
}
}
}
return res;
}
}
93. 复原 IP 地址 2022.8.5
class Solution {
static final int SEG_COUNT = 4;
List<String> ans = new ArrayList<String>();
int[] segments = new int[SEG_COUNT];
public List<String> restoreIpAddresses(String s) {
dfs(s, 0, 0);
return ans;
}
public void dfs(String s, int segId, int segStart) {
// 如果找到了 4 段 IP 地址并且遍历完了字符串,那么就是一种答案
if (segId == SEG_COUNT) {
if (segStart == s.length()) {
StringBuffer ipAddr = new StringBuffer();
for (int i = 0; i < SEG_COUNT; i++) {
ipAddr.append(segments[i]);
if (i != SEG_COUNT - 1) {
ipAddr.append('.');
}
}
ans.add(ipAddr.toString());
}
return;
}
// 如果还没有找到 4 段 IP 地址就已经遍历完了字符串,那么提前回溯
if (segStart == s.length()) {
return;
}
// 由于不能有前导零,如果当前数字为 0,那么这一段 IP 地址只能为 0
if (s.charAt(segStart) == '0') {
segments[segId] = 0;
dfs(s, segId + 1, segStart + 1);
}
// 一般情况,枚举每一种可能性并递归
int addr = 0;
for (int segEnd = segStart; segEnd < s.length(); segEnd++) {
addr = addr * 10 + (s.charAt(segEnd) - '0');
if (addr > 0 && addr <= 0xFF) {
segments[segId] = addr;
dfs(s, segId + 1, segEnd + 1);
} else {
break;
}
}
}
}
71. 简化路径 2022.7.27
class Solution {
public String simplifyPath(String path) {
String[] parts = path.split("/");
Stack<String> stk = new Stack<>();
// 借助栈计算最终的文件夹路径
for (String part : parts) {
if (part.isEmpty() || part.equals(".")) {
continue;
}
if (part.equals("..")) {
if (!stk.isEmpty()) stk.pop();
continue;
}
stk.push(part);
}
// 栈中存储的文件夹组成路径
String res = "";
while (!stk.isEmpty()) {
res = "/" + stk.pop() + res;
}
return res.isEmpty() ? "/" : res;
}
}
42. 接雨水 2022.8.5
class Solution {
public int trap(int[] height) {
int left = 0, right = height.length - 1;
int left_max = 0, right_max = 0;
int res = 0;
while (left < right) {
left_max = Math.max(left_max, height[left]);
right_max = Math.max(right_max, height[right]);
if (left_max < right_max) {
res += left_max - height[left];
left++;
} else {
res += right_max - height[right];
right--;
}
}
return res;
}
}
189. 轮转数组 2022.8.5
class Solution {
public void rotate(int[] nums, int k) {
int n = nums.length;
k %= n;
reverse(nums, 0, n - 1);
reverse(nums, 0, k - 1);
reverse(nums, k, n - 1);
}
private void reverse(int[] nums, int start, int end) {
for (int i = start, j = end; i < j; i++, j--) {
int temp = nums[j];
nums[j] = nums[i];
nums[i] = temp;
}
}
}
143. 重排链表 2022.8.6
class Solution {
public void reorderList(ListNode head) {
Deque<ListNode> dq = new LinkedList<>();
ListNode cur = head;
while (cur != null) {
dq.addLast(cur);
cur = cur.next;
}
while (!dq.isEmpty()) {
if (cur == null) {
cur = dq.pollFirst();
} else {
cur.next = dq.pollFirst();
cur = cur.next;
}
cur.next = dq.pollLast();
cur = cur.next;
}
if (cur != null) {
cur.next = null;
}
}
}
136. 只出现一次的数字 2022.8.6
class Solution {
public int singleNumber(int[] nums) {
int res = 0;
for (int n : nums) {
res ^= n;
}
return res;
}
}
5. 最长回文子串 2022.8.7
力扣算法 Java 刷题笔记【链表篇】hot100(四)如何判断回文链表(简单) 3
class Solution {
public String longestPalindrome(String s) {
String res = "";
for (int i = 0; i < s.length(); i++) {
String String1 = palindrom(s, i, i + 1);
String String2 = palindrom(s, i, i);
res = res.length() > String1.length() ? res : String1;
res = res.length() >String2.length() ? res : String2;
}
return res;
}
String palindrom(String s, int start, int end) {
while (start >= 0 && end < s.length() && s.charAt(start) == s.charAt(end)) {
start--;
end++;
}
return s.substring(start + 1, end);
}
}
234. 回文链表 2022.8.7
class Solution {
public boolean isPalindrome(ListNode head) {
ListNode fast = head, slow = head;
while (fast != null && fast.next != null) {
slow = slow.next;
fast = fast.next.next;
}
if (fast != null) {
slow = slow.next;
}
ListNode newNode = reverse(slow);
while (newNode != null) {
if (newNode.val != head.val) {
return false;
}
newNode = newNode.next;
head = head.next;
}
return true;
}
ListNode reverse(ListNode head) {
if (head == null || head.next == null) {
return head;
}
ListNode last = reverse(head.next);
head.next.next = head;
head.next = null;
return last;
}
}
160. 相交链表 2022.8.7
public class Solution {
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
ListNode a = headA, b = headB;
while (a != b) {
if (a == null) {
a = headB;
} else {
a = a.next;
}
if (b == null) {
b = headA;
} else {
b = b.next;
}
}
return a;
}
}
215. 数组中的第K个最大元素 2022.8.7
方法一:二叉堆(优先队列)【推荐】
class Solution {
public int findKthLargest(int[] nums, int k) {
PriorityQueue<Integer> pq = new PriorityQueue<>();
for (int e : nums) {
pq.offer(e);
if (pq.size() > k) {
pq.poll();
}
}
return pq.peek();
}
}
方法二:快排
class Solution {
public int findKthLargest(int[] nums, int k) {
shuffle(nums);
int lo = 0, hi = nums.length - 1;
k = nums.length - k;
while (lo <= hi) {
int p = partition(nums, lo, hi);
if (p < k) {
lo = p + 1;
} else if (p > k) {
hi = p - 1;
} else {
return nums[p];
}
}
return -1;
}
// void sort(int[] nums, int lo, int hi) {
// if (lo >= hi) {
// return;
// }
// int p = partition(nums, lo, hi);
// sort(nums, lo, p - 1);
// sort(nums, p + 1, hi);
// }
int partition(int[] nums, int lo, int hi) {
int pivot = nums[lo];
int i = lo + 1, j = hi;
while (i <= j) {
while (j > lo && nums[j] > pivot) {
j--;
}
while (i < hi && nums[i] <= pivot) {
i++;
}
if (i >= j) {
break;
}
swap(nums, i, j);
}
swap(nums, lo, j);
return j;
}
void shuffle(int[] nums) {
Random rand = new Random();
int n = nums.length;
for (int i = 0; i < n; i++) {
swap(nums, i, i + rand.nextInt(n - i));
}
}
void swap(int[] nums, int i, int j) {
int temp = nums[i];
nums[i] = nums[j];
nums[j] = temp;
}
}
25. K 个一组翻转链表 2022.8.7
class Solution {
public ListNode reverseKGroup(ListNode head, int k) {
// 新建两个结点
ListNode a = head, b = head;
for (int i = 0; i < k; i++) {
if (b == null) {
return a;
}
b = b.next;
}
ListNode newNode = reverse(a, b);
a.next = reverseKGroup(b, k);
return newNode;
}
ListNode reverse(ListNode a, ListNode b) {
ListNode pre = null, cur = a, nxt = a;
// 循环条件记得改为 b
while (cur != b) {
nxt = cur.next;
cur.next = pre;
pre = cur;
cur = nxt;
}
return pre;
}
}
62. 不同路径 2022.8.8
class Solution {
int[][] memo = null;
public int uniquePaths(int m, int n) {
memo = new int[m][n];
return dp(m - 1, n - 1);
}
int dp(int i, int j) {
if (i == 0 || j == 0) {
return 1;
}
if (memo[i][j] != 0) {
return memo[i][j];
}
memo[i][j] = dp(i - 1, j) + dp(i, j - 1);
return memo[i][j];
}
}
322. 零钱兑换 2022.8.8
public class Solution {
public int coinChange(int[] coins, int amount) {
int[] dp = new int[amount + 1];
// 这里我们还得给dp[i]赋予一个尽量大的初始值
for(int i = 0; i <= amount; i++) {
dp[i] = amount + 1;
}
dp[0] = 0;
for (int i = 1; i <= amount; i++) {
// 计算机 dp[i] 所需要的最少硬币数量
for (int j = 0; j < coins.length; j++) {
if (coins[j] <= i) {
dp[i] = Math.min(dp[i], dp[i - coins[j]] + 1);
}
}
}
return dp[amount] > amount ? -1 : dp[amount];
}
}
146. LRU 缓存 2022.8.8
class LRUCache {
int cap;
LinkedHashMap<Integer, Integer> cache = new LinkedHashMap<>();
public LRUCache(int capacity) {
this.cap = capacity;
}
public int get(int key) {
if (!cache.containsKey(key)) {
return -1;
}
// 将 key 变为最近使用
makeRecently(key);
return cache.get(key);
}
public void put(int key, int val) {
if (cache.containsKey(key)) {
// 修改 key 的值
cache.put(key, val);
// 将 key 变为最近使用
makeRecently(key);
return;
}
if (cache.size() >= this.cap) {
// 链表头部就是最久未使用的 key
int oldestKey = cache.keySet().iterator().next();
cache.remove(oldestKey);
}
// 将新的 key 添加链表尾部
cache.put(key, val);
}
private void makeRecently(int key) {
int val = cache.get(key);
// 删除 key,重新插入到队尾
cache.remove(key);
cache.put(key, val);
}
}
// 详细解析参见:
// https://labuladong.github.io/article/?qno=146
198. 打家劫舍
class Solution {
public int rob(int[] nums) {
if(nums.length == 0)
return 0;
int [] dp = new int[nums.length];
dp[0] = nums[0];
// 每次做数组判定时都需要做数组边界判定,防止越界
if(nums.length < 2)
return nums[0];
dp[1] = (nums[0]>nums[1]) ? nums[0] : nums[1];
for(int i = 2; i<nums.length; i++)
dp[i] = ((nums[i] + dp[i-2]) > dp[i-1]) ? (nums[i]+dp[i-2]) : dp[i-1];
return dp[nums.length-1];
}
}
199. 二叉树的右视图
class Solution {
List<Integer> res = new ArrayList<>();
// 记录递归的层数
int depth = 0;
public List<Integer> rightSideView(TreeNode root) {
traverse(root);
return res;
}
// 二叉树遍历函数
void traverse(TreeNode root) {
if (root == null) {
return;
}
// 前序遍历位置
depth++;
if (res.size() < depth) {
// 这一层还没有记录值
// 说明 root 就是右侧视图的第一个节点
res.add(root.val);
}
// 注意,这里反过来,先遍历右子树再遍历左子树
// 这样首先遍历的一定是右侧节点
traverse(root.right);
traverse(root.left);
// 后序遍历位置
depth--;
}
}
958. 二叉树的完全性检验
class Solution {
public boolean isCompleteTree(TreeNode root) {
Queue<TreeNode> q = new LinkedList<>();
q.offer(root);
// 遍历完所有非空节点时变成 true
boolean end = false;
// while 循环控制从上向下一层层遍历
while (!q.isEmpty()) {
int sz = q.size();
// for 循环控制每一层从左向右遍历
for (int i = 0; i < sz; i++) {
TreeNode cur = q.poll();
if (cur == null) {
// 第一次遇到 null 时 end 变成 true
// 如果之后的所有节点都是 null,则说明是完全二叉树
end = true;
} else {
if (end) {
// end 为 true 时遇到非空节点说明不是完全二叉树
return false;
}
// 将下一层节点放入队列,不用判断是否非空
q.offer(cur.left);
q.offer(cur.right);
}
}
}
return true;
}
}
1109. 航班预订统计 2022.8.10
写法一:
// 差分数组
class Solution {
public int[] corpFlightBookings(int[][] bookings, int n) {
int[] res = new int[n];
Difference df = new Difference(res);
for (int[] booking : bookings) {
int i = booking[0] - 1;
int j = booking[1] - 1;
int val = booking[2];
df.increment(i, j, val);
}
return df.result();
}
class Difference {
private int[] diff;
public Difference(int[] nums) {
assert nums.length > 0;
diff = new int[nums.length];
diff[0] = nums[0];
for (int i = 1; i < nums.length; i++) {
diff[i] = nums[i] - nums[i - 1];
}
}
public void increment(int i, int j, int val) {
diff[i] += val;
if (j + 1 < diff.length) {
diff[j + 1] -= val;
}
}
public int[] result() {
int[] res = new int[diff.length];
res[0] = diff[0];
for (int i = 1; i < diff.length; i++) {
res[i] = res[i - 1] + diff[i];
}
return res;
}
}
}
写法二:
class Solution {
public int[] corpFlightBookings(int[][] bookings, int n) {
int[] res = new int[n];
int[] diff = new int[n];
for (int[] booking : bookings) {
int i = booking[0] - 1;
int j = booking[1] - 1;
int val = booking[2];
for (int k = i; k <= j; k++) {
diff[k] += val;
}
}
return diff;
}
写法三:
class Solution {
public int[] corpFlightBookings(int[][] bookings, int n) {
int[] res = new int[n];
int[] diff = new int[n];
for (int[] booking : bookings) {
int i = booking[0] - 1;
int j = booking[1] - 1;
int val = booking[2];
diff[i] += val;
if (j + 1 < n) {
diff[j + 1] -= val;
}
}
res[0] = diff[0];
for (int i = 1; i < n; i++) {
res[i] = res[i - 1] + diff[i];
}
return res;
}
2022/2 - 7/27
算法思想:以最左边的数字为基准数,两个哨兵分别位于数组的左右两端,右端的先出发,遇到比基准数小或相等的就停下,此时左边哨兵出发,遇到比基准数大的的就停下,然后交换两个数字的,重复这个过程,直到两个哨兵相遇,此时将相遇时的数字与基准数交换,这样基准数左边都是比基准数小或相等的,右边都是比基准数大的,分别在左右两部分做递归操作即可;
void sort(int[] nums, int lo, int hi) {
if (lo >= hi) {
return;
}
int p = partition(nums, lo, hi);
sort(nums, lo, p - 1);
sort(nums, p + 1, hi);
}
int partition(int[] nums, int lo, int hi) {
int pivot = nums[lo];
int i = lo + 1, j = hi;
while (i <= j) { // 注意边界
while (j > lo && nums[j] > pivot) {
j--;
}
while (i < hi && nums[i] <= pivot) {
i++;
}
if (i >= j) {
break;
}
swap(nums, i, j);
}
swap(nums, lo, j);
return j;
}
void shuffle(int[] nums) {
Random rand = new Random();
int n = nums.length;
for (int i = 0; i < n; i++) {
swap(nums, i, i + rand.nextInt(n - i));
}
}
void swap(int[] nums, int i, int j) {
int temp = nums[i];
nums[i] = nums[j];
nums[j] = temp;
}
复杂度分析:
partition 执行的次数是二叉树节点的个数,每次执行的复杂度就是每个节点代表的子数组 nums[lo…hi] 的长度,所以总的时间复杂度就是整棵树中「数组元素」的个数。
假设数组元素个数为 N,那么二叉树每一层的元素个数之和就是 O(N);分界点分布均匀的理想情况下,树的层数为 O(logN),所以理想的总时间复杂度为 O(NlogN)。
空间复杂度: 递归堆栈的深度(树高) O(logN)
快速排序的效率存在一定随机性,如果每次 partition 切分的结果都极不均匀:
快速排序就退化成选择排序了,树高为 O(N),每层节点的元素个数从 N 开始递减,总的时间复杂度为:
N + (N - 1) + (N - 2) + … + 1 = O(N^2)
快速排序理想情况的时间复杂度是 O(NlogN),空间复杂度 O(logN),极端情况下的最坏时间复杂度是 O(N^2),空间复杂度是 O(N)。
力扣算法 Java 刷题笔记【快速排序】hot100(一)快速排序 快速选择算法 1
归并排序是建立在归并操作的一种高效的排序方法,该方法采用了分治的思想,比较适用于处理较大规模的数据,但比较耗内存
2022/2/22 - 8/5
class Solution {
public int[] sortArray(int[] nums) {
int[] temp = new int[nums.length];
mergeSort(nums, temp, 0, nums.length - 1);
return nums;
}
void mergeSort(int[] nums, int[] temp, int lo, int hi) {
if(lo == hi) {
return;
}
if (lo < hi) {
int middle = lo + (hi - lo) / 2;
mergeSort(nums, temp, lo, middle);
mergeSort(nums, temp, middle + 1, hi);
merge(nums, temp, middle, lo, hi);
}
}
void merge(int[] nums, int[] temp, int middle, int lo, int hi) {
int i = lo, j = middle + 1;
for (int k = lo; k <= hi; k++) {
if (i > middle) {
temp[k] = nums[j];
j++;
} else if (j > hi) {
temp[k] = nums[i];
i++;
} else if (nums[i] <= nums[j]) {
temp[k] = nums[i];
i++;
} else {
temp[k] = nums[j];
j++;
}
}
for (int p = lo; p <= hi; p++) {
nums[p] = temp[p];
}
}
}
适用处理数据量比较少或者部分有序的数据
2022/7 - 8/5
class Solution {
public int[] sortArray(int[] nums) {
for (int i = 1; i < nums.length; i++) {
insertSort(nums, i);
}
return nums;
}
void insertSort(int[] nums, int i) {
int temp = nums[i];
int j = i - 1;
for (; j >= 0 && nums[j] > temp; j--) {
nums[j + 1] = nums[j];
}
nums[j + 1] = temp;
}
}
从第一个石子开始,让它和右边相邻的石子进行比较,如果左边的石子大于右边的石子,那么就交换两个石子的位置,(也可以左小于右交换,这里采用大于交换),这样每比较一次,大的就跑到右边,直到跑到最右边
2022/8/22
class Solution {
public int[] sortArray(int[] nums) {
bubbleSort(nums);
return nums;
}
void bubbleSort(int[] nums) {
if (nums == null || nums.length < 2) {
return;
}
int temp = 0;
for (int i = 0; i < nums.length - 1; i++) {
boolean flag = true;
for (int j = 0; j < nums.length - 1 - i; j++) {
if (nums[j] > nums[j + 1]) {
temp = nums[j];
nums[j] = nums[j + 1];
nums[j + 1] = temp;
flag = false;
}
}
if (flag) {
break;
}
}
}
}