剑指Offer
栈与队列
剑指 Offer 09. 用两个栈实现队列 - 力扣(LeetCode)
class CQueue {
Stack<Integer> inQueue;
Stack<Integer> outQueue;
public CQueue() {
inQueue = new Stack<>();
outQueue = new Stack<>();
}
public void appendTail(int value) {
inQueue.push(value);
}
public int deleteHead() {
if(outQueue.isEmpty()) {
while (!inQueue.isEmpty()) {
outQueue.push(inQueue.pop());
}
}
if (outQueue.isEmpty()) return -1;
return outQueue.pop();
}
}
/**
* Your CQueue object will be instantiated and called as such:
* CQueue obj = new CQueue();
* obj.appendTail(value);
* int param_2 = obj.deleteHead();
*/
剑指 Offer 30. 包含min函数的栈 - 力扣(LeetCode)
class MinStack {
Stack<Node> stack;
/** initialize your data structure here. */
public MinStack() {
stack = new Stack<>();
}
public void push(int x) {
int min;
if (stack.isEmpty()) {
min = x;
} else {
min = Math.min(stack.peek().min, x);
}
stack.push(new Node(x, min));
}
public void pop() {
stack.pop();
}
public int top() {
return stack.peek().value;
}
public int min() {
return stack.peek().min;
}
}
class Node {
int value;
int min;
public Node(int value, int min){
this.value = value;
this.min = min;
}
}
/**
* Your MinStack object will be instantiated and called as such:
* MinStack obj = new MinStack();
* obj.push(x);
* obj.pop();
* int param_3 = obj.top();
* int param_4 = obj.min();
*/
剑指 Offer 59 - I. 滑动窗口的最大值 - 力扣(LeetCode)
class Solution {
public int[] maxSlidingWindow(int[] nums, int k) {
LinkedList<Integer> q = new LinkedList<>();
int[] res = new int[nums.length - k + 1];
for(int i = 0; i < nums.length; i++) {
if (!q.isEmpty() && q.peek() <= i - k) q.poll();
while (!q.isEmpty() && nums[q.peekLast()] < nums[i]) q.pollLast();
q.offer(i);
if (i >= k - 1) res[i - k + 1] = nums[q.peek()];
}
return res;
}
}
★ ★剑指 Offer 59 - II. 队列的最大值 - 力扣(LeetCode)
class MaxQueue {
LinkedList<Integer> q;
LinkedList<Integer> max;
public MaxQueue() {
q = new LinkedList<>();
max = new LinkedList<>();
}
public int max_value() {
if (q.isEmpty()) return -1;
return max.peek();
}
public void push_back(int value) {
q.offer(value);
while(!max.isEmpty() && value > max.peekLast()) max.pollLast();//这里的思想很重要
max.offer(value);
}
public int pop_front() {
if (q.isEmpty()) return -1;
if (q.peek().equals(max.peek())) max.poll();//这里如果用 == 那就错了
return q.poll();
}
}
链表
剑指 Offer 06. 从尾到头打印链表 - 力扣(LeetCode)
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public int[] reversePrint(ListNode head) {
List<Integer> list = new ArrayList<>();
while (head != null) {
list.add(head.val);
head = head.next;
}
int[] res = new int[list.size()];
for (int i = 0, j = list.size() - 1; i < res.length; i++) {
res[i] = list.get(j);
j--;
}
return res;
}
}
剑指 Offer 24. 反转链表 - 力扣(LeetCode)
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public ListNode reverseList(ListNode head) {
if (head == null) return head;
ListNode pre = null;
ListNode cur = head;
ListNode temp = new ListNode(0);
while (temp != null) {
temp = cur.next;
cur.next = pre;
pre = cur;
if (temp != null) cur = temp;
}
return cur;
}
}
★★剑指 Offer 35. 复杂链表的复制 - 力扣(LeetCode)
/*
// Definition for a Node.
class Node {
int val;
Node next;
Node random;
public Node(int val) {
this.val = val;
this.next = null;
this.random = null;
}
}
*/
// 思路是先复制出所有节点,然后根据节点直接的关系搭桥
class Solution {
public Node copyRandomList(Node head) {
Map<Node, Node> map = new HashMap<>();
Node cur = head;
while (cur != null) {
map.put(cur, new Node(cur.val));
cur = cur.next;
}
cur = head;
while (cur != null) {
map.get(cur).next = map.get(cur.next);
map.get(cur).random = map.get(cur.random);
cur = cur.next;
}
return map.get(head);
}
}
字符串
剑指 Offer 05. 替换空格 - 力扣(LeetCode)
class Solution {
public String replaceSpace(String s) {
StringBuffer res = new StringBuffer();
for (char c : s.toCharArray()) {
if (c == ' ') {
res.append("%20");
} else {
res.append(c);
}
}
return new String(res);
}
}
剑指 Offer 58 - II. 左旋转字符串 - 力扣(LeetCode)
class Solution {
public String reverseLeftWords(String s, int n) {
return s.substring(n, s.length()) + s.substring(0, n);
}
}
★ ★剑指 Offer 67. 把字符串转换成整数 - 力扣(LeetCode)
class Solution {
public int strToInt(String str) {
int res = 0, flag = Integer.MAX_VALUE / 10, len = str.length(), sign = 1;
if (len == 0) return 0;
int i = 0;
// 去空格
while(i < len && str.charAt(i) == ' ')i++;
if (i >= len) return 0;
// 确定符号
if (str.charAt(i) == '-') sign = -1;
if (str.charAt(i) == '-' || str.charAt(i) == '+') i++;
if (i >= len) return 0;
for (int j = i; j < len; j++) {
if (str.charAt(j) < '0' || str.charAt(j) > '9') break;
//判断溢出,最为精妙
if (res > flag || (res == flag && str.charAt(j) > '7')) {
return sign == 1 ? Integer.MAX_VALUE : Integer.MIN_VALUE;
}
res = 10 * res + str.charAt(j) - '0';
}
return res * sign;
}
}
★ ★ ★剑指 Offer 20. 表示数值的字符串 - 力扣(LeetCode)
class Solution {
public boolean isNumber(String s) {
char[] c = s.trim().toCharArray();
boolean isNum = false, isDot = false, isE = false;
// return false;
for (int i = 0; i < c.length; i++) {
if (c[i] >= '0' && c[i] <= '9') isNum = true;
else if (c[i] == '+' || c[i] == '-') {
if (!(i == 0) && c[i - 1] != 'E' && c[i - 1] != 'e') return false;
} else if (c[i] == '.') {
if (isDot || isE) return false;
isDot = true;
} else if (c[i] == 'e' || c[i] == 'E') {
if (!isNum || isE) return false;
isE = true;
isNum = false;
} else return false;
}
return isNum;
}
}
查找算法
剑指 Offer 03. 数组中重复的数字 - 力扣(LeetCode)哈希表
class Solution {
public int findRepeatNumber(int[] nums) {
Set<Integer> set = new HashSet<>();
for (int i : nums) {
if (set.contains(i)) {
return i;
}
set.add(i);
}
return 1;
}
}
// 大神算法
class Solution {
public int findRepeatNumber(int[] nums) {
int i = 0;
while(i < nums.length) {
if(nums[i] == i) {
i++;
continue;
}
if(nums[nums[i]] == nums[i]) return nums[i];
int tmp = nums[i];
nums[i] = nums[tmp];
nums[tmp] = tmp;
}
return -1;
}
}
剑指 Offer 53 - I. 在排序数组中查找数字 I - 力扣(LeetCode)二分查找
class Solution {
public int search(int[] nums, int target) {
if (nums.length == 0) return 0;
int l = 0, r = nums.length - 1;
while (l <= r) {
int mid = l + (r - l) / 2;
if (nums[mid] == target) {
int i = mid, j = mid;
while (i >= 0 && nums[i] == target) i--;
while (j <= nums.length - 1 && nums[j] == target) j++;
return j - i - 1;
}
else if (nums[mid] < target) l = mid + 1;
else if (nums[mid] > target) r = mid - 1;
}
return 0;
}
}
剑指 Offer 53 - II. 0~n-1中缺失的数字 - 力扣(LeetCode)二分
class Solution {
public int missingNumber(int[] nums) {
int l = 0, r = nums.length - 1;
while (l <= r) {
int mid = l + (r - l) / 2;
if (nums[mid] == mid) l = mid + 1;
else r = mid - 1;
}
return l;
}
}
剑指 Offer 04. 二维数组中的查找 - 力扣(LeetCode)
class Solution {
public boolean findNumberIn2DArray(int[][] matrix, int target) {
if (matrix.length == 0) return false;
int n = matrix.length, m = matrix[0].length;
if (n == 0 || m == 0) return false;
for (int i = 0; i < n; i++) {
int l = 0, r = m - 1;
if (matrix[i][l] > target || matrix[i][r] < target) continue;
while (l <= r) {
int mid = (l + r) / 2;
if (matrix[i][mid] > target) {
r = mid - 1;
} else if (matrix[i][mid] < target) {
l = mid + 1;
} else if (matrix[i][mid] == target) {
return true;
}
}
}
return false;
}
}
剑指 Offer 11. 旋转数组的最小数字 - 力扣(LeetCode)二分查找变式
class Solution {
public int minArray(int[] numbers) {
if (numbers.length == 1) return numbers[0];
int i = 0, j = numbers.length - 1;
while (i <= j) {
int mid = (i + j) / 2;
if (numbers[mid] > numbers[j]) i = mid + 1;
else if (numbers[mid] < numbers[j]) j = mid;
else j--;
}
return numbers[i];
}
}
剑指 Offer 50. 第一个只出现一次的字符 - 力扣(LeetCode)哈希表查找
class Solution {
public char firstUniqChar(String s) {
char res = ' ';
int resIndex = 50001;
int[] letters = new int[26], locations = new int[26];
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
letters[c - 'a']++;
if (letters[c - 'a'] == 1){
locations[c - 'a'] = i;
}
}
for (int i = 0; i < 26; i++) {
if (letters[i] == 1) {
resIndex = Math.min(resIndex, locations[i]);
}
}
res = resIndex == 50001? ' ' : s.charAt(resIndex);
return res;
}
}
搜索与回溯算法
剑指 Offer 32 - I. 从上到下打印二叉树 - 力扣(LeetCode)层序遍历
class Solution {
Deque<TreeNode> dq;
List<Integer> res;
int[] resArr;
public int[] levelOrder(TreeNode root) {
dq = new ArrayDeque<>();
res = new LinkedList<>();
if (root == null) return new int[]{};
dq.offer(root);
while(!dq.isEmpty()) {
int len = dq.size();
while (len > 0) {
TreeNode node = dq.poll();
res.add(node.val);
if (node.left != null) dq.offer(node.left);
if (node.right != null) dq.offer(node.right);
len--;
}
}
resArr = new int[res.size()];
int x = 0;
for (Integer i : res) {
resArr[x++] = i;
}
return resArr;
}
}
剑指 Offer 32 - II. 从上到下打印二叉树 II - 力扣(LeetCode)层序遍历
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public List<List<Integer>> levelOrder(TreeNode root) {
List<List<Integer>> res = new ArrayList<>();
Queue<TreeNode> dq = new ArrayDeque<>();
if (root == null) return res;
dq.offer(root);
while(!dq.isEmpty()) {
int len = dq.size();
List<Integer> temp = new ArrayList<>();
while (len > 0) {
TreeNode node = dq.poll();
temp.add(node.val);
if (node.left != null) dq.offer(node.left);
if (node.right != null) dq.offer(node.right);
len--;
}
res.add(temp);
}
return res;
}
}
剑指 Offer 32 - III. 从上到下打印二叉树 III - 力扣(LeetCode)层序遍历变式
class Solution {
public List<List<Integer>> levelOrder(TreeNode root) {
List<List<Integer>> res = new ArrayList<>();
ArrayDeque<TreeNode> dq = new ArrayDeque<>();
if (root == null) return res;
dq.offer(root);
boolean leftFlag = true;
while (!dq.isEmpty()) {
int len = dq.size();
ArrayDeque<TreeNode> nextDq = dq.clone();
List<Integer> temp = new ArrayList<>();
while(len > 0) {
TreeNode node;
if (leftFlag) node = dq.poll();
else node = dq.pollLast();
temp.add(node.val);
TreeNode node2 = nextDq.poll();
if (node2.left != null) nextDq.offer(node2.left);
if (node2.right != null) nextDq.offer(node2.right);
len--;
}
res.add(temp);
dq = nextDq;
leftFlag = !leftFlag;
}
return res;
}
}
★ ★ ★剑指 Offer 26. 树的子结构 - 力扣(LeetCode)双递归(主函数和辅助函数) + 二叉树
回溯三部曲
- 终止条件
- 返回值
- 单层循环逻辑
class Solution {
public boolean isSubStructure(TreeNode A, TreeNode B) {
if (A == null || B == null) return false;
return dfs(A, B) || isSubStructure(A.left, B) || isSubStructure(A.right, B);
}
// 同公共节点的判断
public boolean dfs(TreeNode a, TreeNode b) {
if (b == null) return true;
if (a == null) return false;
return a.val == b.val && dfs(a.left, b.left) && dfs(a.right, b.right);
}
}
★ ★ ★剑指 Offer 27. 二叉树的镜像 - 力扣(LeetCode)递归 + 二叉树
class Solution {
public TreeNode mirrorTree(TreeNode root) {
if (root == null) return root;
TreeNode temp = root.left;
root.left = mirrorTree(root.right);
root.right = mirrorTree(temp);
return root;
}
}
剑指 Offer 28. 对称的二叉树 - 力扣(LeetCode)递归+二叉树
class Solution {
public boolean isSymmetric(TreeNode root) {
if (root == null) return true;
return dfs(root.left, root.right);
}
public boolean dfs(TreeNode a, TreeNode b) {
if (a == null && b == null) return true;
else if (a == null || b == null) return false;
return a.val == b.val && dfs(a.left, b.right) && dfs(a.right, b.left);
}
}
中等
★ ★ ★剑指 Offer 12. 矩阵中的路径 - 力扣(LeetCode)
第二次主要是
class Solution {
boolean[][] used;
public boolean exist(char[][] board, String word) {
for (int i = 0; i < board.length; i++) {
for (int j = 0; j < board[0].length; j++) {
used = new boolean[board.length][board[0].length];// 每次搜索都重置地图使用标记
if (dfs(board, i, j, word, 0)) return true;
}
}
return false;
}
public boolean dfs(char[][]board, int x, int y, String s, int index) {
if (index == s.length()) return true;
if (x < 0 || x >= board.length || y < 0 || y >= board[0].length) return false;
char c = s.charAt(index);
if (board[x][y] != c || used[x][y]) return false;
used[x][y] = true;
boolean res = dfs(board, x - 1, y, s, index + 1) || dfs(board, x + 1, y, s, index + 1)
|| dfs(board, x, y - 1, s, index + 1) || dfs(board, x, y + 1, s, index + 1); // 这一步也非常巧妙
used[x][y] = false;//第三次忘记回溯
return res;
}
}
剑指 Offer 13. 机器人的运动范围 - 力扣(LeetCode)纯搜索
class Solution {
boolean[][] used;
int res;
public int movingCount(int m, int n, int k) {
used = new boolean[m][n];
dfs(0,0,m,n,k);
return res;
}
public void dfs(int x, int y, int m, int n, int k) {
if (sum(x) + sum(y) > k || x < 0 || x >= m || y < 0 || y >= n || used[x][y]) return;
else if (sum(x) + sum(y) <= k && !used[x][y]) {
res++;
used[x][y] = true;
}
dfs(x + 1, y, m, n, k);
dfs(x - 1, y, m, n, k);
dfs(x, y + 1, m, n, k);
dfs(x, y - 1, m, n, k);
}
public int sum(int n) {
int res = 0;
while (n > 0) {
res += n % 10;
n /= 10;
}
return res;
}
}
★剑指 Offer 34. 二叉树中和为某一值的路径 - 力扣(LeetCode)回溯
class Solution {
List<List<Integer>> res;
LinkedList<Integer> temp;
int sum = 0;
public List<List<Integer>> pathSum(TreeNode root, int target) {
res = new ArrayList<>();
temp = new LinkedList<>();
dfs(root, target);
return res;
}
public void dfs(TreeNode root, int target) {
if(root == null) return;
temp.add(root.val);
sum += root.val;
if(sum == target && root.right == null && root.left == null) {
res.add(new ArrayList<>(temp));
}
dfs(root.left, target);
dfs(root.right, target);
sum -= root.val;
temp.removeLast();
}
}
★ ★剑指 Offer 36. 二叉搜索树与双向链表 - 力扣(LeetCode)
class Solution {
Node pre, first;
public Node treeToDoublyList(Node root) {
if (root == null) return root;
dfs(root);
first.left = pre;
pre.right = first;
return first;
}
public void dfs(Node root) {
if (root == null) return;
dfs(root.left);
if (pre == null) {
first = root;
}
else {
pre.right = root;
root.left = pre;
}
pre = root;
dfs(root.right);
}
}
剑指 Offer 54. 二叉搜索树的第k大节点 - 力扣(LeetCode)回溯
class Solution {
int index, res;
public int kthLargest(TreeNode root, int k) {
dfs(root, k);
return res;
}
public void dfs(TreeNode root, int k) {
if (root == null) return;
dfs(root.right, k);
index++;
if (index == k) res = root.val;
dfs(root.left, k);
}
}
剑指 Offer 55 - I. 二叉树的深度 - 力扣(LeetCode)搜索
class Solution {
public int maxDepth(TreeNode root) {
if (root == null) return 0;
return Math.max(maxDepth(root.left), maxDepth(root.right)) + 1;
}
}
★ ★剑指 Offer 55 - II. 平衡二叉树 - 力扣(LeetCode)
class Solution {
public boolean isBalanced(TreeNode root) {
return dfs(root) != -1;
}
public int dfs(TreeNode root) {
if (root == null) return 0;
int right = dfs(root.left), left = dfs(root.right);
if (right == -1) return -1;
if (left == -1) return -1;
if (Math.abs(right - left) > 1) return -1;
return Math.max(right, left) + 1;
}
}
★ ★ ★剑指 Offer 64. 求1+2+…+n - 力扣(LeetCode)不能用判断语句
class Solution {
public int sumNums(int n) {
boolean x = n > 1 && (n += sumNums(n - 1)) > 1;
return n;
}
}
★剑指 Offer 68 - I. 二叉搜索树的最近公共祖先 - 力扣(LeetCode)
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
if (root == p) return p;
if (root == q) return q;
if (root == null) return null;
TreeNode left = lowestCommonAncestor(root.left, p, q);
TreeNode right = lowestCommonAncestor(root.right, p, q);
if (left != null && right != null) return root;
if (left == null && right != null) return right;
return left;
}
}
//第二次不同的想法
class Solution {
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
if (p.val > q.val) {
TreeNode tmp = q;
q = p;
p = tmp;
}
if(root.val < q.val && root.val > p.val) return root;
else if (root.val == p.val) return p;
else if (root.val == q.val) return q;
if (root.val > q.val) return lowestCommonAncestor(root.left, p, q);
return lowestCommonAncestor(root.right, p, q);
}
}
★ ★剑指 Offer 68 - II. 二叉树的最近公共祖先 - 力扣(LeetCode)
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
if (root == p) return p;
if (root == q) return q;
if (root == null) return null;
TreeNode left = lowestCommonAncestor(root.left, p, q);
TreeNode right = lowestCommonAncestor(root.right, p, q);
if (left != null && right != null) return root;
if (left == null && right != null) return right;
return left;
}
}
★ ★ ★ ★ ★剑指 Offer 37. 序列化二叉树 题解 - 力扣(LeetCode)
public class Codec {
String s;
TreeNode root;
// Encodes a tree to a single string.
public String serialize(TreeNode root) {
if (root == null) return "null,";
s = root.val + ",";
s += serialize(root.left);
s += serialize(root.right);
return s;
}
// Decodes your encoded data to tree.
public TreeNode deserialize(String data) {
Queue<String> q = new LinkedList<>();
String[] arr = data.split(",");
for (String i : arr) {
q.offer(i);
}
return dfs(q);
}
public TreeNode dfs(Queue<String> q){
String val = q.poll();
if (val.equals("null")) {
return null;
}
TreeNode root = new TreeNode(Integer.valueOf(val));
root.left = dfs(q);
root.right = dfs(q);
return root;
}
}
// 第二次
public class Codec {
// Encodes a tree to a single string.
public String serialize(TreeNode root) {
if (root == null) return "[]";
StringBuffer res = new StringBuffer();
res.append("[");
Queue<TreeNode> q = new LinkedList<>();
q.offer(root);
while(!q.isEmpty()) {
TreeNode n = q.poll();
if (n != null) {
res.append(n.val + ",");
q.offer(n.left);
q.offer(n.right);
}
else res.append("null,");
}
res.deleteCharAt(res.length() - 1);
res.append(']');
return res.toString();
}
// Decodes your encoded data to tree.
public TreeNode deserialize(String data) {
if (data.equals("[]")) return null;
String[] values = data.substring(1, data.length() - 1).split(",");
Queue<TreeNode> q = new LinkedList<>();
TreeNode res = new TreeNode(Integer.parseInt(values[0]));
q.offer(res);
int i = 1;
while(!q.isEmpty()) {
TreeNode node = q.poll();
if (!values[i].equals("null")) {
node.left = new TreeNode(Integer.parseInt(values[i]));
q.offer(node.left);
}
i++;
if (!values[i].equals("null")) {
node.right = new TreeNode(Integer.parseInt(values[i]));
q.offer(node.right);
}
i++;
}
return res;
}
}
★剑指 Offer 38. 字符串的排列 - 力扣(LeetCode)
class Solution {
StringBuffer sb;
List<String> res;
boolean[] used;
public String[] permutation(String s) {
sb = new StringBuffer();
res = new ArrayList<>();
used = new boolean[s.length()];
char[] c = s.toCharArray();
Arrays.sort(c);
dfs(c, c.length);
String[] arr = new String[res.size()];
int i = 0;
for (String str : res) {
arr[i++] = str;
}
return arr;
}
public void dfs(char[] c, int end) {
if (sb.length() == end) {
res.add(sb.toString());
}
for (int i = 0; i < end; i++) {
if (i > 0 && c[i] == c[i - 1] && !used[i - 1]) continue;
if (used[i]) continue;
sb.append(c[i]);
used[i] = true;
dfs(c, end);
used[i] = false;
sb.deleteCharAt(sb.length() - 1);
}
}
}
动态规划
剑指 Offer 10- I. 斐波那契数列 - 力扣(LeetCode)阶梯问题
class Solution {
public int fib(int n) {
if (n == 0) return 0;
if (n == 1) return 1;
int mod = 1000000007;
int[] dp = new int[n + 1];
dp[1] = 1;
for (int i = 2; i <= n; i++) {
dp[i] = dp[i - 1] + dp[i - 2];
dp[i] %= mod;
}
return dp[n];
}
}
剑指 Offer 10- II. 青蛙跳台阶问题 - 力扣(LeetCode)阶梯问题
class Solution {
public int numWays(int n) {
if (n == 0) return 1;
int mod = 1000000007;
int[] dp = new int[n + 1];
dp[1] = 1;
dp[0] = 1;
for (int i = 2; i <= n; i++) {
dp[i] = dp[i - 2] + dp[i - 1];
dp[i] %= mod;
}
return dp[n];
}
}
剑指 Offer 63. 股票的最大利润 - 力扣(LeetCode)
class Solution {
public int maxProfit(int[] prices) {
int n = prices.length;
if (n == 0) return 0;
int res = 0;
// dp[i] 代表第i天的最大利润
int dp[] = new int[n + 1];
// 代表前面的最低价格
int buy = -prices[0];
for (int i = 2; i <= n; i++) {
dp[i] = Math.max(dp[i], buy + prices[i - 1]);
buy = Math.max(buy, -prices[i - 1]);
res = Math.max(res, dp[i]);
}
return res;
}
}
剑指 Offer 42. 连续子数组的最大和 - 力扣(LeetCode)最大和问题,大神好解
class Solution {
public int maxSubArray(int[] nums) {
if (nums.length == 1) return nums[0];
//dp[i] 表示包含第i - 1个元素的连续子数组最大和
int[] dp = new int[nums.length + 1];
int res = nums[0];
for (int i = 1; i <= nums.length; i++) {
dp[i] = Math.max(nums[i - 1], dp[i - 1] + nums[i - 1]);
res = Math.max(res, dp[i]);
}
return res;
}
}
// 直接在数组修改,不用额外空间
class Solution {
public int maxSubArray(int[] nums) {
int res = nums[0];
for(int i = 1; i < nums.length; i++) {
nums[i] += Math.max(nums[i - 1], 0);
res = Math.max(res, nums[i]);
}
return res;
}
}
剑指 Offer 47. 礼物的最大价值 - 力扣(LeetCode)最大和,直接修改数组
class Solution {
public int maxValue(int[][] grid) {
for (int i = 0; i < grid.length; i++) {
for (int j = 0; j < grid[0].length; j++) {
if (i == 0 & j == 0) continue;
else if (i == 0) grid[i][j] += grid[i][j - 1];
else if (j == 0) grid[i][j] += grid[i - 1][j];
else grid[i][j] += Math.max(grid[i - 1][j], grid[i][j - 1]);
}
}
return grid[grid.length - 1][grid[0].length - 1];
}
}
剑指 Offer 46. 把数字翻译成字符串 - 力扣(LeetCode)阶梯问题
class Solution {
public int translateNum(int num) {
if (num < 10) return 1;
char[] c = String.valueOf(num).toCharArray();
int dp[] = new int[c.length + 1];
dp[0] = 1;
dp[1] = 1;
for (int i = 2; i <= c.length; i++) {
if (c[i - 2] != '0' && (c[i - 2] - '0') * 10 + (c[i - 1] - '0') < 26) {
dp[i] = dp[i - 1] + dp[i - 2]; //满足条件则可以将前一步阶梯加上前两步阶梯的可能
} else {
dp[i] = dp[i - 1];
}
}
return dp[c.length];
}
}
剑指 Offer 48. 最长不含重复字符的子字符串 - 力扣(LeetCode)动态规划 + 哈希表
class Solution {
public int lengthOfLongestSubstring(String s) {
if (s.length() == 0) return 0;
int[] loc = new int[128];
Arrays.fill(loc, -1);
int start = 0;
int res = 0;
for (int i = 0; i < s.length(); i++) {
int c = s.charAt(i) - ' ';
if (loc[c] != -1) {
start = Math.max(start, loc[c] + 1);
}
res = Math.max(res, i - start + 1);
loc[c] = i;
}
return res;
}
}
★ ★ ★剑指 Offer 19. 正则表达式匹配 - 力扣(LeetCode)
第二次死在出现0次的地方
class Solution {
public boolean isMatch(String s, String p) {
boolean[][] dp = new boolean[s.length() + 1][p.length() + 1];
dp[0][0] = true;
for (int j = 1; j <= p.length(); j++) {
if (p.charAt(j - 1) == '*' && dp[0][j - 2]) dp[0][j] = true;
}
for (int i = 1; i <= s.length(); i++) {
for (int j = 1; j <= p.length(); j++) {
if (p.charAt(j - 1) == '.' || p.charAt(j - 1) == s.charAt(i - 1)) dp[i][j] = dp[i - 1][j - 1];
else if (p.charAt(j - 1) == '*') {
if (p.charAt(j - 2) == s.charAt(i - 1) || p.charAt(j - 2) == '.') {
dp[i][j] = dp[i - 1][j]// 出现1次以上
|| dp[i][j - 2]; // 出现0次(第二次死在这)
}
else dp[i][j] = dp[i][j - 2];// 若无用到* 的功能,只能将他变0
}
}
}
return dp[s.length()][p.length()];
}
}
★ ★剑指 Offer 49. 丑数 - 力扣(LeetCode)
class Solution {
public int nthUglyNumber(int n) {
int p2 = 1, p3 = 1, p5 = 1;
int[] dp = new int[n + 1];
dp[1] = 1;
for (int i = 2; i <= n; i++) {
int num2 = dp[p2] * 2, num3 = dp[p3] * 3, num5 = dp[p5] * 5;
dp[i] = Math.min(Math.min(num2, num3), num5);
if (num2 == dp[i]) p2++;//这里用else if不行
if (num3 == dp[i]) p3++;
if (num5 == dp[i]) p5++;
}
return dp[n];
}
}
★ ★剑指 Offer 60. n个骰子的点数 - 力扣(LeetCode)
class Solution {
public double[] dicesProbability(int n) {
double[] dp = new double[6];
Arrays.fill(dp, 1.0 / 6.0);
for (int i = 2; i <= n; i++) {
double[] temp = new double[5 * i + 1];
for (int j = 0; j < dp.length; j++) {
for (int k = 0; k < 6; k++) {
temp[k + j] += dp[j] / 6.0;
}
}
dp = temp;
}
return dp;
}
}
双指针
剑指 Offer 18. 删除链表的节点 - 力扣(LeetCode)双指针+链表
class Solution {
public ListNode deleteNode(ListNode head, int val) {
ListNode dimmy = new ListNode(0);
dimmy.next = head;
ListNode pre = dimmy, cur = head;
while (cur.val != val) {
cur = cur.next;
pre = pre.next;
}
pre.next = cur.next;
return dimmy.next;
}
}
剑指 Offer 22. 链表中倒数第k个节点 - 力扣(LeetCode)双指针加链表
class Solution {
public ListNode getKthFromEnd(ListNode head, int k) {
ListNode cur = head;
int len = 0;
while (cur != null) {
len++;
cur = cur.next;
}
cur = head;
while(len - k > 0) {
cur = cur.next;
len--;
}
return cur;
}
}
剑指 Offer 25. 合并两个排序的链表 - 力扣(LeetCode)双指针+链表
class Solution {
public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
if(l1 == null) return l2;
if(l2 == null) return l1;
ListNode dimmy = new ListNode(0);
ListNode pre = dimmy;
while (l1 != null && l2 != null) {
if (l1.val < l2.val) {
dimmy.next = l1;
l1 = l1.next;
} else {
dimmy.next = l2;
l2 = l2.next;
}
dimmy = dimmy.next;
}
if (l1 != null) dimmy.next = l1;
if (l2 != null) dimmy.next = l2;
return pre.next;
}
}
剑指 Offer 52. 两个链表的第一个公共节点 - 力扣(LeetCode)
class Solution {
ListNode getIntersectionNode(ListNode headA, ListNode headB) {
ListNode curA = headA;
ListNode curB = headB, res = null;
int lenA = 0, lenB = 0;
while (curA != null) {
lenA++;
curA = curA.next;
}
while (curB != null) {
lenB++;
curB = curB.next;
}
if (lenA > lenB) {
curA = headA;
curB = headB;
} else {
curA = headB;
curB = headA;
}
int len = Math.abs(lenA - lenB);
while (len > 0) {
curA = curA.next;
len--;
}
while(curA != null) {
if (curA == curB) {
res = curA;
break;
}
curA = curA.next;
curB = curB.next;
}
return res;
}
}
剑指 Offer 21. 调整数组顺序使奇数位于偶数前面 - 力扣(LeetCode)
class Solution {
public int[] exchange(int[] nums) {
if (nums.length == 0) return nums;
int l = 0, r = nums.length - 1;
while (l < r) {
while (l < r && nums[l] % 2 == 1) l++;
while (l < r && nums[r] % 2 == 0) r--;
if (l < r) {
int temp = nums[l];
nums[l] = nums[r];
nums[r] = temp;
}
}
return nums;
}
}
剑指 Offer 57. 和为s的两个数字 - 力扣(LeetCode)
// 哈希表
class Solution {
public int[] twoSum(int[] nums, int target) {
int[] res = new int[2];
Map<Integer,Integer> map = new HashMap<>();
for (int i = 0; i < nums.length; i++) {
if (map.containsKey(nums[i])) {
res[0] = nums[i];
res[1] = map.get(nums[i]);
break;
}
map.put(target - nums[i], nums[i]);
}
return res[0] == 0 ? new int[]{} : res;
}
}
// 双指针
class Solution {
public int[] twoSum(int[] nums, int target) {
int l = 0, r = nums.length - 1;
while (l < r) {
if (nums[l] + nums[r] < target) l++;
if (nums[l] + nums[r] > target) r--;
if (nums[l] + nums[r] == target) return new int[]{nums[l], nums[r]};
}
return new int[]{};
}
}
剑指 Offer 58 - I. 翻转单词顺序 - 力扣(LeetCode)
class Solution {
public String reverseWords(String s) {
s = reverse(s.trim());
if (s.length() == 0) return "";
StringBuffer res = new StringBuffer();
int l = 0, r = 0;
while (r < s.length()) {
while (s.charAt(l) == ' ') l++;
r = l;
while (r < s.length() && s.charAt(r) != ' ') r++;
res.append(reverse(s.substring(l, r)) + ' ');
l = r;
}
res.deleteCharAt(res.length() - 1);
return res.toString();
}
public String reverse(String s) {
char[] c = s.toCharArray();
int l = 0, r = c.length - 1;
while (l < r) {
char tmp = c[l];
c[l] = c[r];
c[r] = tmp;
l++;
r--;
}
return new String(c);
}
}
排序
★ ★剑指 Offer 45. 把数组排成最小的数 - 力扣(LeetCode)
class Solution {
public String minNumber(int[] nums) {
String[] s = new String[nums.length];
for (int i = 0; i < nums.length; i++) {
s[i] = String.valueOf(nums[i]);
}
Arrays.sort(s, (a, b) -> (a + b).compareTo(b + a));
String res = "";
for (String o : s) {
res += o;
}
return res;
}
}
剑指 Offer 61. 扑克牌中的顺子 - 力扣(LeetCode)
class Solution {
public boolean isStraight(int[] nums) {
Arrays.sort(nums);
int zero = 0;
for (int i = 0; i < nums.length; i++) {
if (nums[i] == 0) {
zero++;
continue;
}
if (i > 0 && nums[i] - nums[i - 1] == 1 ) continue;
else if (i > 0 && nums[i - 1] != 0 && nums[i] == nums[i - 1]) return false;
else if (i > 0 && nums[i - 1] != 0 && nums[i] - nums[i - 1] > 1) {
zero -= nums[i] - nums[i - 1] - 1;
}
}
return zero >= 0;
}
}
★ ★剑指 Offer 40. 最小的k个数 - 力扣(LeetCode)快速排序
class Solution {
public int[] getLeastNumbers(int[] arr, int k) {
if (arr.length == 0) return new int[]{};
fastSort(0, arr.length - 1, arr);
return Arrays.copyOfRange(arr, 0, k);
}
// 实质上是逐次找到每个数最终的位置,从索引0开始
public void fastSort(int l, int r, int[] arr) {
if (l >= r) return;//数组长度为1的时候直接结束
int i = l, j = r;
while(i < j) {
while (i < j && arr[l] <= arr[j]) j--;//左边为哨兵,这两步分别找比他小的数,比他大的数
while (i < j && arr[l] >= arr[i]) i++;
swap(i, j, arr);//这两个数交换
}
swap(i, l, arr);//与哨兵交换,i的位置就是最终的位置
fastSort(l, i - 1, arr);// 这里要注意左端是l
fastSort(i + 1, r, arr);// 这里要注意右端是r
}
public void swap(int i, int j, int[] arr) {
int tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
}
剑指 Offer 41. 数据流中的中位数 - 力扣(LeetCode)优先队列
class MedianFinder {
PriorityQueue<Integer> dq1;
PriorityQueue<Integer> dq2;
/** initialize your data structure here. */
public MedianFinder() {
dq1 = new PriorityQueue<>((a, b) -> b - a);
dq2 = new PriorityQueue<>((a, b) -> a - b);
}
public void addNum(int num) {
if (dq1.isEmpty()) {
dq1.offer(num);
return;
}
if (dq1.peek() > num) {
dq1.offer(num);
if (dq1.size() - dq2.size() > 1) dq2.offer(dq1.poll());
} else if (num >= dq1.peek()) {
dq2.offer(num);
if (dq1.size() < dq2.size()) dq1.offer(dq2.poll());
}
}
public double findMedian() {
if (dq1.size() == dq2.size()) return ((double) dq1.peek() + dq2.peek()) / 2;
return dq1.peek();
}
}
/**
* Your MedianFinder object will be instantiated and called as such:
* MedianFinder obj = new MedianFinder();
* obj.addNum(num);
* double param_2 = obj.findMedian();
*/
分治算法
★ ★剑指 Offer 07. 重建二叉树 - 力扣(LeetCode)
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public TreeNode buildTree(int[] preorder, int[] inorder) {
if (preorder.length == 0) return null;//终止条件要清晰
TreeNode root = new TreeNode(preorder[0]);
int i = 0;
while(i < inorder.length && inorder[i] != preorder[0]) i++;
int len = i;
root.left = buildTree(Arrays.copyOfRange(preorder, 1, 1 + len), Arrays.copyOfRange(inorder, 0, i));
root.right = buildTree(Arrays.copyOfRange(preorder, 1 + len, preorder.length), Arrays.copyOfRange(inorder, i + 1, inorder.length));
return root;
}
}
★ ★ ★剑指 Offer 16. 数值的整数次方 - 力扣(LeetCode)快速幂
class Solution {
public double myPow(double x, int n) {
long N = n;
double res = 1.0;
if (N < 0) {
N = -N;
x = 1 / x;
}
while (N > 0) {
if ((N & 1) == 1) res *= x; //如果N是奇数先乘一次,到最后一次循环,N=1 乘上就是res
x *= x;
N >>= 1; //右移一位相当于除以2
}
return res;
}
}
剑指 Offer 33. 二叉搜索树的后序遍历序列 - 力扣(LeetCode)
class Solution {
public boolean verifyPostorder(int[] postorder) {
return dfs(postorder, 0, postorder.length - 1);
}
public boolean dfs(int[] postorder, int i, int j) {
if (i >= j) return true;
int p = i;
while (postorder[p] < postorder[j]) p++;
int l = p;// 左右子树分界点
while (postorder[p] > postorder[j]) p++;
return p == j && dfs(postorder, i, l - 1) && dfs(postorder, l, j - 1);
}
}
★ ★ ★剑指 Offer 51. 数组中的逆序对 - 力扣(LeetCode)归并排序
class Solution {
int[] tmp;
public int reversePairs(int[] nums) {
tmp = new int[nums.length];
return sort(nums, 0, nums.length - 1);
}
public int sort(int[]nums, int l, int r) {
if (l >= r) return 0;
int m = l + (r - l) / 2;
//分治算法
int res = sort(nums, l, m) + sort(nums, m + 1, r);
int i = l, j = m + 1;
for (int k = l; k <= r; k++) {
tmp[k] = nums[k];
}
for (int k = l; k <= r; k++) {
if (i > m) {
nums[k] = tmp[j++];
} else if (j > r || tmp[i] <= tmp[j]) {
nums[k] = tmp[i++];
} else {
nums[k] = tmp[j++];
res += m - i + 1;// 这一步忘了
}
}
return res;
}
}
位运算
★ ★剑指 Offer 15. 二进制中1的个数 - 力扣(LeetCode)
第二次,1. n != 0判断条件和>>>右移就是连符号一起移,>> 是不移符号
public class Solution {
// you need to treat n as an unsigned value
public int hammingWeight(int n) {
int res = 0;
while (n != 0) {// 负数的话进不了循环,所以不能用n > 0;
if ((n & 1) == 1) res++;
n >>>= 1;// java中无符号数要>>>进行右移,与>>有区别
}
return res;
}
}
★ ★剑指 Offer 65. 不用加减乘除做加法 - 力扣(LeetCode)
class Solution {
public int add(int a, int b) {
while (b != 0) {
int c = (a & b) << 1; // 进位
a ^= b;//非进位和
b = c;//进位还需要进行下一场进位和非进位和,直至为0;
}
return a;
}
}
★ ★剑指 Offer 56 - I. 数组中数字出现的次数 - 力扣(LeetCode)
class Solution {
public int[] singleNumbers(int[] nums) {
int x = 0, y = 0, n = 0, m = 1;// n = x ^ y, 辅助m
for (int i : nums) {
n ^= i;
}
while ((m & n) == 0) m <<= 1;// x和y肯定有一位是是不一样
for (int i : nums) {
if ((i & m) == 0) x ^= i;// 或者(i & m) != 0
else y ^= i;
}
return new int[]{x, y};
}
}
★ ★剑指 Offer 56 - II. 数组中数字出现的次数 II - 力扣(LeetCode)
/**这道题可以使用位运算来解决。我们可以考虑对于每一位,统计所有数字在该位上出现的次数之和,然后对3取余,剩下的就是只现一次的数字在该位上的值。最后将每一位的值拼接起来,就得到了只出现一次的数字。
体来说,我们可以使用两个变量 ones 和 twos 分别记录所有数字在每一位上出现一次和两次的情况。对当前数字 num,更新 ones 和 twos 的方法如下:
ones = ones ^ num & ~twos
twos = twos ^ num & ~ones
第一步中,ones ^= num 表示将 num 加入 ones 中,如果 num 已经在 ones 中出现过,则将其删除。num &twos 表示将 num 从 twos 中删除,因为如果 num 在 twos 中出现过,则说明 num 在该位上出现了两次,而我们只需要记录出现一次的数字。
第二步中,twos ^= num 表示将 num 加入 twos 中, num 已经在 twos 中出现过,则将其删除。num & ~ones 表示将 num 从 ones 中删除,因为如果 num 在 ones 中出现过,则说明 num 在该位上出现了两次,而我们只需要记录出现一次的数字。
最后,只出现一次的数字就是 ones。*/
class Solution {
public int singleNumber(int[] nums) {
int ones = 0, two = 0;
for(int i : nums) {
ones = ones ^ i & ~two;
two = two ^ i & ~ones;
}
return ones;
}
}
数学
★ ★剑指 Offer 39. 数组中出现次数超过一半的数字 - 力扣(LeetCode)摩尔投票
class Solution {
public int majorityElement(int[] nums) {
int vote = 0;
int res = 0;
for (int i = 0; i < nums.length; i++) {
if (vote == 0) res = nums[i];
vote += nums[i] == res ? 1 : -1;
}
return res;
}
}
★ ★剑指 Offer 66. 构建乘积数组 - 力扣(LeetCode)
class Solution {
public int[] constructArr(int[] a) {
int len = a.length;
if (len == 0) return new int[0];
int[] res = new int[len];
res[0] = 1;
int tmp = 1;
// a1 a2 a3
// 第一个for 1 a1 a1*a2
// 第二个for a2*a3 a1*a3 a1*a2
for (int i = 1; i < len; i++) {
res[i] = res[i - 1] * a[i - 1];
}
for (int i = len - 2; i >= 0; i--) {
tmp *= a[i + 1];
res[i] *= tmp;
}
return res;
}
}
★剑指 Offer 14- II. 剪绳子 II - 力扣(LeetCode)
class Solution {
public int cuttingRope(int n) {
int mod = 1000000007;
long res = 1;
if (n == 2) return 1;
if (n == 3) return 2;
if (n == 4) return 4;
while (n > 4) {
res *= 3;
n -= 3;
res %= mod;
}
res *= n;
res %= mod;
return (int)res;
}
}
剑指 Offer 57. 和为s的两个数字 - 力扣(LeetCode)
class Solution {
public int[] twoSum(int[] nums, int target) {
HashMap<Integer, Integer> map = new HashMap<>();
for (int i = 0; i < nums.length; i++) {
if (map.containsKey(nums[i])) {
return new int[]{nums[i], nums[(int)map.get(nums[i])]};
}
map.put(target - nums[i], i);
}
return new int[]{};
}
}
★ ★ ★剑指 Offer 62. 圆圈中最后剩下的数字 - 力扣(LeetCode)约瑟夫环
/**
*/
class Solution {
public int lastRemaining(int n, int m) {
int x = 0;
for (int i = 2; i <= n; i++) {
x = (x + m) % i;
}
return x;
}
}
★ ★ ★剑指 Offer 43. 1~n 整数中 1 出现的次数 - 力扣(LeetCode)位数动态规划
class Solution {
// 用2304 2314 2324举例分析
public int countDigitOne(int n) {
int digit = 1, high = n / 10, low = 0, cur = n % 10, res = 0;
while (high != 0 || cur != 0) {// 最后是两者都为0则不用运算
if (cur == 0) res += high * digit;
if (cur == 1) res += high * digit + low + 1;
if (cur > 1) res += (high + 1) * digit;
low += cur * digit;
digit *= 10;
cur = high % 10;
high /= 10;
}
return res;
}
}
★ ★ 剑指 Offer 44. 数字序列中某一位的数字 - 力扣(LeetCode)
/* 数字范围 数量 位数 占多少位
1-9 9 1 9
10-99 90 2 180
100-999 900 3 2700
1000-9999 9000 4 36000 ...
例如 2901 = 9 + 180 + 2700 + 12 即一定是4位数,第12位 n = 12;
数据为 = 1000 + (12 - 1)/ 4 = 1000 + 2 = 1002
定位1002中的位置 = (n - 1) % 4 = 3 s.charAt(3) = 2;
*/
// 第二次感悟,一定要用long型,不然肯定出错
class Solution {
public int findNthDigit(int n) {
int digit = 1; // n所在数字的位数
long start = 1; // 数字范围开始的第一个数
long count = 9; // 占多少位
while(n > count){
n -= count;
digit++;
start *= 10;
count = digit * start * 9;
}
long num = start + (n - 1) / digit;
return Long.toString(num).charAt((n - 1) % digit) - '0';
}
}
模拟
剑指 Offer 31. 栈的压入、弹出序列 - 力扣(LeetCode)
class Solution {
public boolean validateStackSequences(int[] pushed, int[] popped) {
Stack<Integer> stack = new Stack<>();
int i = 0, j = 0;
while(i < pushed.length && j < popped.length) {
stack.push(pushed[i]);
while (!stack.isEmpty() && stack.peek() == popped[j]) {
stack.pop();
j++;
}
i++;
}
return stack.isEmpty();
}
}
剑指 Offer 29. 顺时针打印矩阵 - 力扣(LeetCode)
class Solution {
public int[] spiralOrder(int[][] matrix) {
if (matrix.length == 0 || matrix[0].length == 0) return new int[]{};
int start = 0, m = matrix.length, n = matrix[0].length;
int loop = Math.min(m, n) / 2;
int[] res = new int[m * n];
int index = 0;
while (loop > 0) {
int x = start, y = start;
for (; y < n - 1 - start; y++) {
res[index++] = matrix[x][y];
}
for (; x < m - 1 - start; x++) {
res[index++] = matrix[x][y];
}
for (; y > start; y--) {
res[index++] = matrix[x][y];
}
for(; x > start; x--) {
res[index++] = matrix[x][y];
}
loop--;
start++;
}
if (m > n && n % 2 == 1) {
int x = start, y = start;
while (index < res.length) {
res[index++] = matrix[x++][y];
}
}
if (m < n && m % 2 == 1) {
int x = start, y = start;
while (index < res.length) {
res[index++] = matrix[x][y++];
}
}
if (m == n && n % 2 == 1) {
res[index] = matrix[start][start];
}
return res;
}
}