树
考察以下两点:
- 二叉树的遍历、序列化
- 二叉搜索树
class Solution {
public TreeNode pruneTree(TreeNode root) {
if (root == null) {
return root;
}
TreeNode left = null;
if (root.left != null) {
left = pruneTree(root.left);
}
TreeNode right = null;
if (root.right != null) {
right = pruneTree(root.right);
}
if (root.val == 0 && left == null && right == null) {
return null;
}
root.left = left;
root.right = right;
return root;
}
}
// 前序遍历
public class Codec {
StringBuilder sb = new StringBuilder();
String NULL = "#";
String SPLIT = ",";
// Encodes a tree to a single string.
public String serialize(TreeNode root) {
serializeTree(root);
return sb.toString();
}
public void serializeTree(TreeNode root) {
if (root == null) {
sb.append(NULL).append(SPLIT);
return;
}
sb.append(root.val).append(SPLIT);
serializeTree(root.left);
serializeTree(root.right);
}
// Decodes your encoded data to tree.
public TreeNode deserialize(String data) {
String[] nodeArray = data.split(SPLIT);
LinkedList<String> nodeList = new LinkedList<>();
for (String node : nodeArray) {
nodeList.add(node);
}
return deserialize(nodeList);
}
public TreeNode deserialize(LinkedList<String> nodes) {
if (nodes.isEmpty()) {
return null;
}
String first = nodes.removeFirst();
if (NULL.equals(first)) {
return null;
}
TreeNode head = new TreeNode(Integer.parseInt(first));
head.left = deserialize(nodes);
head.right = deserialize(nodes);
return head;
}
}
剑指 Offer II 049. 从根节点到叶节点的路径数字之和
//深度优先遍历
class Solution {
public int sumNumbers(TreeNode root) {
return dfs(root, 0);
}
public int dfs(TreeNode root, int preSum) {
if (root == null) {
return 0;
}
int sum = preSum * 10 + root.val;
if (root.left == null && root.right == null) {
return sum;
} else {
return dfs(root.left, sum) + dfs(root.right, sum);
}
}
}
//leetcode submit region end(Prohibit modification and deletion)
// 广度优先
class Solution {
public int sumNumbers(TreeNode root) {
if (root == null) {
return 0;
}
return bfs(root);
}
public int bfs(TreeNode root) {
Queue<TreeNode> queue = new LinkedList<>();
Queue<Integer> nums = new LinkedList<>();
queue.offer(root);
nums.add(0);
int sum = 0;
while (!queue.isEmpty()) {
TreeNode cur = queue.poll();
int val = cur.val + nums.poll() * 10;
if (cur.left != null) {
queue.offer(cur.left);
nums.offer(val);
}
if (cur.right != null) {
queue.offer(cur.right);
nums.offer(val);
}
if (cur.left == null && cur.right == null) {
// 找到根节点了
sum += val;
}
}
return sum;
}
}
// 回溯,把所有可能的路径都列出来
class Solution {
public int pathSum(TreeNode root, int targetSum) {
if (root == null) {
return 0;
}
int ret = rootSum(root, targetSum);
ret += pathSum(root.left, targetSum);
ret += pathSum(root.right, targetSum);
return ret;
}
public int rootSum(TreeNode root, long targetSum) {
int ret = 0;
if (root == null) {
return 0;
}
int val = root.val;
if (val == targetSum) {
ret++;
}
ret += rootSum(root.left, targetSum - val);
ret += rootSum(root.right, targetSum - val);
return ret;
}
}
// 前缀和
class Solution {
public int pathSum(TreeNode root, int targetSum) {
if (root == null) {
return 0;
}
Map<Long, Integer> map = new HashMap<>();
// 0也是一种可能性
map.put(0L, 1);
return dfs(root, map, 0, targetSum);
}
public int dfs(TreeNode root, Map<Long, Integer> prefix, long curr, int targetSum) {
if (root == null) {
return 0;
}
curr += root.val;
int ret = prefix.getOrDefault(curr - targetSum, 0);
prefix.put(curr, prefix.getOrDefault(curr, 0) + 1);
ret += dfs(root.left, prefix, curr, targetSum);
ret += dfs(root.right, prefix, curr, targetSum);
prefix.put(curr, prefix.getOrDefault(curr, 0) - 1);
return ret;
}
}
class Solution {
int max = Integer.MIN_VALUE;
public int maxPathSum(TreeNode root) {
maxRoot(root);
return max;
}
// 经过当前节点的最长路径
public int maxRoot(TreeNode root) {
if (root == null) {
return 0;
}
int leftMax = Math.max(maxRoot(root.left), 0);
int rightMax = Math.max(maxRoot(root.right), 0);
int priceNewpath = root.val + leftMax + rightMax;
max = Math.max(max, priceNewpath);
return root.val + Math.max(leftMax, rightMax);
}
}
// 方法一:先保存中序遍历二叉搜索树的结果
class Solution {
public TreeNode increasingBST(TreeNode root) {
TreeNode p = new TreeNode(-1);
TreeNode pre = p;
List<Integer> ans = new ArrayList<>();
dfs(root, ans);
for (int v : ans) {
p.right = new TreeNode(v);
p = p.right;
}
return pre.right;
}
public void dfs(TreeNode root, List<Integer> ans) {
if (root == null) {
return;
}
dfs(root.left, ans);
ans.add(root.val);
dfs(root.right, ans);
}
}
// 方法二:用一个全局的node,不断的改变node的指向
class Solution {
private TreeNode resNode;
public TreeNode increasingBST(TreeNode root) {
TreeNode dummyNode = new TreeNode(-1);
resNode = dummyNode;
inorder(root);
return dummyNode.right;
}
public void inorder(TreeNode node) {
if (node == null) {
return;
}
inorder(node.left);
// 在中序遍历的过程中修改节点指向
resNode.right = node;
node.left = null;
resNode = node;
inorder(node.right);
}
}
// 方法一:记录pre,要记住中序遍历的迭代写法
class Solution {
public TreeNode inorderSuccessor(TreeNode root, TreeNode p) {
Deque<TreeNode> stack = new ArrayDeque<TreeNode>();
TreeNode prev = null, curr = root;
while (!stack.isEmpty() || curr != null) {
while (curr != null) {
stack.push(curr);
curr = curr.left;
}
curr = stack.pop();
if (prev == p) {
return curr;
}
prev = curr;
curr = curr.right;
}
return null;
}
}
// 方法二:使用搜索树的性质
class Solution {
public TreeNode inorderSuccessor(TreeNode root, TreeNode p) {
TreeNode successor = null;
// p有右子树,那要寻找右子树里最左的,就是中序后继
if (p.right != null) {
successor = p.right;
while (successor.left != null) {
successor = successor.left;
}
return successor;
}
// p没有右子树,p可能是树中的一个节点,也可能是树的最后一个节点
TreeNode node = root;
while (node != null) {
// node比p大,说明后继肯定出现在node的左子树或者自己
if (node.val > p.val) {
successor = node;
node = node.left;
} else {
// node比p小,肯定是在右子树
// node等于p,那也是在右子树
node = node.right;
}
}
return successor;
}
}
class Solution {
int sum = 0;
public TreeNode convertBST(TreeNode root) {
// 大于等于节点的值肯定出现在自己和右子树
// 如果按照右、中、左来遍历,那比自己大的元素都在自己之前遍历过了
TreeNode head = root;
dfs(root);
return head;
}
public void dfs(TreeNode node) {
if (node == null) {
return;
}
dfs(node.right);
int val = node.val;
sum += val;
node.val = sum;
dfs(node.left);
}
}
class BSTIterator {
TreeNode cur = null;
TreeNode resNode = null;
public BSTIterator(TreeNode root) {
cur = new TreeNode(-1);
resNode = cur;
dfs(root);
}
public void dfs(TreeNode node) {
if (node == null) {
return;
}
dfs(node.left);
resNode.right = node;
node.left = null;
resNode = node;
dfs(node.right);
}
public int next() {
TreeNode next = cur.right;
cur = cur.right;
return next.val;
}
public boolean hasNext() {
return cur != null && cur.right != null;
}
}
class Solution {
public boolean findTarget(TreeNode root, int k) {
List<Integer> vals = new ArrayList<>();
dfs(root, vals);
int left = 0;
int right = vals.size() - 1;
while (left < right) {
int sum = vals.get(left) + vals.get(right);
if (sum == k) {
return true;
} else if (sum < k) {
left++;
} else {
right--;
}
}
return false;
}
public void dfs(TreeNode root, List<Integer> vals) {
if (root == null) {
return;
}
dfs(root.left, vals);
vals.add(root.val);
dfs(root.right, vals);
}
}
剑指 Offer II 057. 值和下标之差都在给定的范围内
堆
class KthLargest {
int k;
PriorityQueue<Integer> queue;
public KthLargest(int k, int[] nums) {
this.k = k;
// [4, 5, 8, 2] 3,5,10,9,4
// 顺序,从小到大
this.queue = new PriorityQueue<>();
if (nums.length > 0) {
for (int num : nums) {
add(queue, num);
}
}
}
private void add(PriorityQueue<Integer> queue, int val) {
if (queue.isEmpty() || queue.size() < k) {
queue.offer(val);
return;
}
int head = queue.peek();
if (val > head) {
queue.poll();
queue.offer(val);
}
}
public int add(int val) {
add(queue, val);
// 返回队头
return queue.size() < k ? -1 : queue.peek();
}
}
剑指 Offer II 060. 出现频率最高的 k 个数字
class Solution {
public int[] topKFrequent(int[] nums, int k) {
Map<Integer, Integer> map = new HashMap<>();
for (int num : nums) {
map.put(num, map.getOrDefault(num, 0) + 1);
}
// 从大到小排
PriorityQueue<Model> queue = new PriorityQueue<>((o1, o2) -> {
return o1.count - o2.count;
});
for (Map.Entry<Integer, Integer> entry : map.entrySet()) {
if (queue.size() < k) {
queue.offer(new Model(entry.getKey(), entry.getValue()));
continue;
}
int head = queue.peek().count;
if (entry.getValue() > head) {
queue.poll();
queue.offer(new Model(entry.getKey(), entry.getValue()));
}
}
int size = queue.size();
int[] ans = new int[size];
for (int i = 0; i < size; i++) {
ans[i] = queue.poll().val;
}
return ans;
}
public class Model {
int val;
int count;
public Model(int val, int count) {
this.val = val;
this.count = count;
}
}
}
简单,懒得写。
二分查找
class Solution {
public int searchInsert(int[] nums, int target) {
int left = 0;
int right = nums.length - 1;
// 找到它或者它的左边界
while (left < right) {
int mid = left + (right - left) / 2;
if (nums[mid] == target) {
return mid;
} else if (nums[mid] < target) {
left = mid + 1;
} else {
right = mid - 1;
}
}
return nums[left] < target ? left + 1 : left;
}
}
二分也可以用来比较相邻位置。
class Solution {
public int peakIndexInMountainArray(int[] arr) {
// 找到峰顶
int left = 0;
int right = arr.length - 1;
while (left < right) {
int mid = left + (right - left) / 2;
if (arr[mid] > arr[mid + 1]) {
right = mid;
} else {
left = mid + 1;
}
}
return right;
}
}
剑指 Offer II 070. 排序数组中只出现一次的数字
class Solution {
public int singleNonDuplicate(int[] nums) {
int low = 0, high = nums.length - 1;
while (low < high) {
int mid = (high - low) / 2 + low;
if (nums[mid] == nums[mid ^ 1]) {
low = mid + 1;
} else {
high = mid;
}
}
return nums[low];
}
}
class Solution {
public int singleNonDuplicate(int[] nums) {
int low = 0, high = nums.length - 1;
while (low < high) {
int mid = (high - low) / 2 + low;
mid -= mid & 1;
if (nums[mid] == nums[mid + 1]) {
low = mid + 2;
} else {
high = mid;
}
}
return nums[low];
}
}
class Solution {
int[] preSums;
int sum = 0;
Random random;
public Solution(int[] w) {
int n = w.length;
preSums = new int[n];
random = new Random();
for (int i = 0; i < n; i++) {
sum += w[i];
preSums[i] = sum;
}
}
public int pickIndex() {
return find(preSums, random.nextInt(sum) + 1);
}
public int find(int[] nums, int target) {
int left = 0, right = nums.length;
while (left < right) {
int mid = left + (right - left) / 2;
if (nums[mid] >= target) {
right = mid;
} else {
left = mid + 1;
}
}
return left;
}
}
class Solution {
public int mySqrt(int x) {
// 找到左边界
int left = 0;
int right = x;
int ans = 1;
while (left <= right) {
int mid = left + (right - left) / 2;
long sqr = (long) mid * mid;
if (sqr <= x) {
ans = mid;
left = mid + 1;
} else {
right = mid - 1;
}
}
return ans;
}
}
class Solution {
public int minEatingSpeed(int[] piles, int h) {
// 速度越快,花费的时间越少,所以是一个倒序数组
int left = 1;
int right = 0;
for (int pile : piles) {
right = Math.max(right, pile);
}
int ans = -1;
while (left <= right) {
int mid = left + (right - left) / 2;
int cost = cost(piles, mid);
if (cost > h) {
// 花费的时间比当前要求的多,加快速度
left = mid + 1;
} else {
ans = mid;
// 花费的时间比当前要求的少,减慢速度
right = mid - 1;
}
}
return ans;
}
public int cost(int[] piles, int k) {
int cost = 0;
for (int pile : piles) {
cost += pile / k;
if (pile % k > 0) {
cost++;
}
}
return cost;
}
}
排序
class Solution {
public int[][] merge(int[][] intervals) {
// 升序排序
Arrays.sort(intervals, (o1, o2) -> {
// 第一个元素相等,第二个元素升序
if (o1[0] == o2[0]) {
return o2[1] - o2[1];
}
return o1[0] - o2[0];
});
LinkedList<int[]> merged = new LinkedList<>();
merged.add(intervals[0]);
for (int i = 1; i < intervals.length; i++) {
int[] cur = intervals[i];
int[] last = merged.getLast();
// 不相交,是另一个区间了
if (cur[0] > last[1]) {
merged.add(cur);
} else {
int val = Math.max(cur[1], last[1]);
last[1] = val;
merged.set(merged.size() - 1, last);
}
}
int[][] ans = new int[merged.size()][2];
for (int i = 0; i < merged.size(); i++) {
ans[i] = merged.get(i);
}
return ans;
}
}
回溯
class Solution {
List<List<Integer>> ans = null;
public List<List<Integer>> subsets(int[] nums) {
ans = new ArrayList<>();
backtrace(0, nums, new LinkedList<>());
return ans;
}
public void backtrace(int idx, int[] nums, LinkedList<Integer> path) {
ans.add(new ArrayList<>(path));
for (int i = idx; i < nums.length; i++) {
path.add(nums[i]);
backtrace(i + 1, nums, path);
path.removeLast();
}
}
}
//leetcode submit region begin(Prohibit modification and deletion)
class Solution {
List<List<Integer>> ans = null;
public List<List<Integer>> combine(int n, int k) {
ans = new ArrayList<>();
backtrace(k, 1, n, new LinkedList<>());
return ans;
}
public void backtrace(int k, int idx, int n, LinkedList<Integer> path) {
if (path.size() == k) {
ans.add(new ArrayList<>(path));
}
for (int i = idx; i <= n; i++) {
path.add(i);
backtrace(k, i + 1, n, path);
path.removeLast();
}
}
}
//leetcode submit region end(Prohibit modification and deletion)
class Solution {
List<List<Integer>> ans;
public List<List<Integer>> combinationSum(int[] candidates, int target) {
ans = new ArrayList<>();
backtrace(0, candidates, target, 0, new LinkedList<Integer>());
return ans;
}
public void backtrace(int idx, int[] candidates, int target, int sum, LinkedList<Integer> path) {
if (sum == target) {
ans.add(new ArrayList<Integer>(path));
return;
}
// 已经超过目标,不需要再加了
if (sum > target) {
return;
}
for (int i = idx; i < candidates.length; i++) {
path.add(candidates[i]);
sum += candidates[i];
backtrace(i, candidates, target, sum, path);
path.removeLast();
sum -= candidates[i];
}
}
}
class Solution {
List<List<Integer>> ans;
boolean[] visited;
public List<List<Integer>> combinationSum2(int[] candidates, int target) {
ans = new ArrayList<>();
visited = new boolean[candidates.length];
// 必须排序后才能用i和i-1的比较来剪枝
Arrays.sort(candidates);
backtrace(candidates, target, 0, 0, new LinkedList<>());
return ans;
}
public void backtrace(int[] candidates, int target, int sum, int idx, LinkedList<Integer> path) {
if (sum == target) {
ans.add(new ArrayList<>(path));
return;
}
if (sum > target) {
return;
}
for (int i = idx; i < candidates.length; i++) {
if (i > 0 && !visited[i - 1] && candidates[i] == candidates[i - 1]) {
continue;
}
visited[i] = true;
path.add(candidates[i]);
sum += candidates[i];
backtrace(candidates, target, sum, i + 1, path);
sum -= candidates[i];
path.removeLast();
visited[i] = false;
}
}
}
class Solution {
List<List<Integer>> ans;
boolean[] used;
public List<List<Integer>> permute(int[] nums) {
ans = new ArrayList<>();
used = new boolean[nums.length];
backtrace(nums, new LinkedList<Integer>());
return ans;
}
public void backtrace(int[] nums, LinkedList<Integer> path) {
if (nums.length == path.size()) {
ans.add(new ArrayList<Integer>(path));
return;
}
for (int i = 0; i < nums.length; i++) {
if (used[i]) {
continue;
}
path.add(nums[i]);
used[i] = true;
backtrace(nums, path);
path.removeLast();
used[i] = false;
}
}
}
//leetcode submit region begin(Prohibit modification and deletion)
class Solution {
List<List<Integer>> ans;
boolean[] used;
public List<List<Integer>> permuteUnique(int[] nums) {
ans = new ArrayList<>();
used = new boolean[nums.length];
Arrays.sort(nums);
backtrace(nums, new LinkedList<Integer>());
return ans;
}
public void backtrace(int[] nums, LinkedList<Integer> path) {
if (nums.length == path.size()) {
ans.add(new ArrayList<Integer>(path));
return;
}
for (int i = 0; i < nums.length; i++) {
if (used[i]) {
continue;
}
if (i > 0 && !used[i - 1] && nums[i - 1] == nums[i]) {
continue;
}
path.add(nums[i]);
used[i] = true;
backtrace(nums, path);
path.removeLast();
used[i] = false;
}
}
}
//leetcode submit region end(Prohibit modification and deletion)
//leetcode submit region begin(Prohibit modification and deletion)
class Solution {
List<String> ans;
public List<String> generateParenthesis(int n) {
// 右括号的数量不能超过左括号
// 当左右括号的数量相等并且等于n的时候,就是有效的括号
ans = new ArrayList<>();
backtrace(0, 0, n, new StringBuilder());
return ans;
}
public void backtrace(int left, int right, int n, StringBuilder path) {
if (right > left || left > n || right > n) {
return;
}
if (right == left && left == n) {
ans.add(path.toString());
return;
}
path.append("(");
left++;
backtrace(left, right, n, path);
left--;
path.deleteCharAt(path.length() - 1);
path.append(")");
right++;
backtrace(left, right, n, path);
right--;
path.deleteCharAt(path.length() - 1);
}
}
//leetcode submit region end(Prohibit modification and deletion)
class Solution {
boolean[][] f;
List<List<String>> tmp = new ArrayList<List<String>>();
List<String> ans = new ArrayList<String>();
int n;
public String[][] partition(String s) {
n = s.length();
f = new boolean[n][n];
for (int i = 0; i < n; ++i) {
Arrays.fill(f[i], true);
}
for (int i = n - 1; i >= 0; --i) {
for (int j = i + 1; j < n; ++j) {
f[i][j] = (s.charAt(i) == s.charAt(j)) && f[i + 1][j - 1];
}
}
dfs(s, 0);
int rows = tmp.size();
String[][] ret = new String[rows][];
for (int i = 0; i < rows; ++i) {
int cols = tmp.get(i).size();
ret[i] = new String[cols];
for (int j = 0; j < cols; ++j) {
ret[i][j] = tmp.get(i).get(j);
}
}
return ret;
}
public void dfs(String s, int i) {
if (i == n) {
tmp.add(new ArrayList<String>(ans));
return;
}
for (int j = i; j < n; ++j) {
if (f[i][j]) {
ans.add(s.substring(i, j + 1));
dfs(s, j + 1);
ans.remove(ans.size() - 1);
}
}
}
}
class Solution {
List<String> ans;
int n;
public List<String> restoreIpAddresses(String s) {
ans = new ArrayList<>();
n = s.length();
backtrace(s, 0, new LinkedList<String>());
return ans;
}
public void backtrace(String s, int i, LinkedList<String> tmp) {
if (tmp.size() > 4 || i > n) {
return;
}
if (tmp.size() == 4 && i == n) {
StringBuilder sb = new StringBuilder();
for (String ss : tmp) {
sb.append(ss).append(".");
}
sb.deleteCharAt(sb.length() - 1);
ans.add(sb.toString());
return;
}
for (int j = i; j < n; j++) {
if (isSubCode(s, i, j)) {
tmp.add(s.substring(i, j + 1));
backtrace(s, j + 1, tmp);
tmp.removeLast();
}
}
}
public boolean isSubCode(String s, int i, int j) {
if (j - i > 2) {
return false;
}
if (s.charAt(i) == '0' && j > i) {
return false;
}
int val = Integer.parseInt(s.substring(i, j + 1));
return val >= 0 && val <= 255;
}
}
动态规划
class Solution {
public int minCostClimbingStairs(int[] cost) {
// f(i)为到达该位置所需要的花费
int len = cost.length;
int[] f = new int[len + 1];
for (int i = 2; i <= len; i++) {
f[i] = Math.min(f[i - 2] + cost[i - 2], f[i - 1] + cost[i - 1]);
}
return f[len];
}
}
class Solution {
public int rob(int[] nums) {
int len = nums.length;
if (len == 1) {
return nums[0];
}
// 偷完第i家所偷到的最大金额
int[] f = new int[len];
f[0] = nums[0];
f[1] = Math.max(nums[0], nums[1]);
int max = Math.max(f[0], f[1]);
for (int i = 2; i < len; i++) {
f[i] = Math.max(f[i - 2] + nums[i], f[i - 1]);
max = Math.max(max, f[i]);
}
return max;
}
}
class Solution {
public int rob(int[] nums) {
int length = nums.length;
if (length == 1) {
return nums[0];
} else if (length == 2) {
return Math.max(nums[0], nums[1]);
}
return Math.max(robRange(nums, 0, length - 2), robRange(nums, 1, length - 1));
}
public int robRange(int[] nums, int start, int end) {
int first = nums[start], second = Math.max(nums[start], nums[start + 1]);
for (int i = start + 2; i <= end; i++) {
int temp = second;
second = Math.max(first + nums[i], second);
first = temp;
}
return second;
}
}
class Solution {
public int minCost(int[][] costs) {
// dp[i] 第i个房子选择第j个颜色后的最少花费
// dp[i][j] = costs[i][j] + Math.min(costs[i][k]...);
int len = costs.length;
int[][] dp = new int[len][3];
dp[0][0] = costs[0][0];
dp[0][1] = costs[0][1];
dp[0][2] = costs[0][2];
for (int i = 1; i < len; i++) {
dp[i][0] = Math.min(dp[i - 1][1], dp[i - 1][2]) + costs[i][0];
dp[i][1] = Math.min(dp[i - 1][0], dp[i - 1][2]) + costs[i][1];
dp[i][2] = Math.min(dp[i - 1][0], dp[i - 1][1]) + costs[i][2];
}
return Math.min(dp[len - 1][0], Math.min(dp[len - 1][1], dp[len - 1][2]));
}
}
class Solution {
public int minFlipsMonoIncr(String s) {
//dp[i][2] 使i位翻转过后变成递增子串的最小翻转次数
int len = s.length();
int[][] dp = new int[len][2];
if (s.charAt(0) == '0') {
dp[0][1] = 1;
} else {
dp[0][0] = 1;
}
for (int i = 1; i < len; i++) {
if (s.charAt(i) == '0') {
// 无需翻转,并且不能由dp[i-1][1]得来
dp[i][0] = dp[i - 1][0];
// 必须翻转
dp[i][1] = Math.min(dp[i - 1][0], dp[i - 1][1]) + 1;
} else {
// 无需翻转
dp[i][1] = Math.min(dp[i - 1][0], dp[i - 1][1]);
// 必须翻转,且只能由dp[i - 1][0]得来
dp[i][0] = dp[i - 1][0] + 1;
}
}
return Math.min(dp[len - 1][0], dp[len - 1][1]);
}
}
class Solution {
public int lenLongestFibSubseq(int[] arr) {
// 这个问题的关键是要记录三个元素,k,j,i,只用两个元素是无法确定斐波那契数列的
int n = arr.length;
int[][] dp = new int[n][n];
// 先把元素暂存起来,方便快速查找
Map<Integer, Integer> map = new HashMap<>();
for (int i = 0; i < n; i++) {
map.put(arr[i], i);
}
int max = 0;
for (int i = 0; i < n; i++) {
for (int j = i - 1; j >= 0 && arr[j] * 2 > arr[i]; j--) {
int k = map.getOrDefault(arr[i] - arr[j], -1);
if (k >= 0) {
// 要使得一个斐波那契数列成立,必须最少3个元素
dp[j][i] = Math.max(dp[k][j] + 1, 3);
}
max = Math.max(max, dp[j][i]);
}
}
return max;
}
}
class Solution {
public int uniquePaths(int m, int n) {
// 只能向下或向右移动
// dp[i][j] = dp[i-1][j] + dp[i][j-1]
int[][] dp = new int[m][n];
// 给第一列赋值
for (int i = 0; i < m; i++) {
dp[i][0] = 1;
}
// 给第一行赋值
for (int j = 0; j < n; j++) {
dp[0][j] = 1;
}
for (int i = 1; i < m; i++) {
for (int j = 1; j < n; j++) {
dp[i][j] = dp[i - 1][j] + dp[i][j - 1];
}
}
return dp[m - 1][n - 1];
}
}
class Solution {
public int minPathSum(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.min(dp[i - 1][j], dp[i][j - 1]) + grid[i][j];
}
}
return dp[m - 1][n - 1];
}
}
class Solution {
public int minimumTotal(List<List<Integer>> triangle) {
int m = triangle.size();
if(m == 1){
return triangle.get(0).get(0);
}
int[][] dp = new int[m][];
dp[0] = new int[1];
dp[0][0] = triangle.get(0).get(0);
int min = Integer.MAX_VALUE;
for (int i = 1; i < m; i++) {
dp[i] = new int[i + 1];
for (int j = 0; j <= i; j++) {
if (j == 0) {
dp[i][j] = dp[i - 1][j];
} else if (j == i) {
dp[i][j] = dp[i - 1][j - 1];
} else {
dp[i][j] = Math.min(dp[i - 1][j], dp[i - 1][j - 1]);
}
dp[i][j] += triangle.get(i).get(j);
if (i == m - 1) {
min = Math.min(dp[i][j], min);
}
}
}
return min;
}
}
剑指 Offer II 101. 分割等和子集 剑指 Offer II 102. 加减的目标值
//动态规划的解法
class Solution {
public int findTargetSumWays(int[] nums, int target) {
int sum = 0;
for (int num : nums) {
sum += num;
}
int diff = sum - target;
// 说明无法得到target
if (diff < 0 || diff % 2 != 0) {
return 0;
}
// 问题转化为:在nums中选取N个元素使其总和为w,共有多少方案
int m = nums.length;
int W = diff / 2;
int[][] dp = new int[m + 1][W + 1];
dp[0][0] = 1;
for (int i = 1; i <= m; i++) {
for (int w = 0; w <= W; w++) {
dp[i][w] = dp[i - 1][w];
if (w >= nums[i - 1]) {
// 达到这个容量的方案数
dp[i][w] += dp[i - 1][w - nums[i - 1]];
}
}
}
return dp[m][W];
}
}
//leetcode submit region end(Prohibit modification and deletion)
// 回溯法
class Solution {
int total = 0;
public int findTargetSumWays(int[] nums, int target) {
backtrace(nums, target, 0, 0);
return total;
}
public void backtrace(int[] nums, int target, int idx, int sum) {
// 到最后了,需要退出
if (idx == nums.length) {
if (sum == target) {
total++;
}
return;
}
backtrace(nums, target, idx + 1, sum + nums[idx]);
backtrace(nums, target, idx + 1, sum - nums[idx]);
}
}
public class Solution {
public int coinChange(int[] coins, int amount) {
int max = amount + 1;
int[] dp = new int[amount + 1];
Arrays.fill(dp, max);
dp[0] = 0;
for (int i = 1; i <= amount; 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];
}
}