237.删除链表中节点(中等)
题目描述:有一个单链表,给定节点node,需要删除这个node节点
题解:删除节点node必须找到前置节点,根据题意无法获取head节点,即不能获得前置节点,换个思路删除节点,也可以通过覆盖实现。这个题目最大挑战是能理解题意。
ListNode p = node;
ListNode q = p.next;
while (q.next != null) {
p.val = q.val;
p = q;
q = q.next;
}
p.val = q.val;
p.next = null;
53.最大子数组和
题目描述:整数数组 nums ,请你找出一个具有最大和的连续子数组
思路:动态规划法。 状态转移: f(i)=max{f(i−1)+nums[i],nums[i]}
public int maxSubArray(int[] nums) {
int pre = 0, maxAns = nums[0];
for (int x : nums) {
pre = Math.max(pre + x, x);
maxAns = Math.max(maxAns, pre);
}
return maxAns;
}
152 乘积最大子数组
题目描述:整数数组 nums ,请你找出数组中乘积最大的非空连续子数组,返回乘积
题解:暴力解法O(N^3)。 优化解法:使用s[i] 表示从0->i,且固定第i个元素结尾的最大连续子数组,s[i]求解需要O[N^2]
// s[i] 表示第i元素结尾的最大乘积
int[] s = new int[n];
for (int j = n - 1; j >= 0; j--) {
int tmp = nums[j];
int max = tmp;
for (int i = j - 1; i >= 0; i--) {
tmp *= nums[i];
max = Math.max(max, tmp);
}
s[j] = max;
}
再优化解法: 动态规划,状态转移f[i], g[i] f[i] 表示0->i,以x[i]结尾的最大乘积 g[i] 表示0->i,以x[i]结尾的最小乘积
for (int i = 1; i < n; i++) {
// dp
f[i] = Math.max(nums[i] * f[i - 1], nums[i] * g[i - 1]);
f[i] = Math.max(nums[i], f[i]);
g[i] = Math.min(nums[i] * f[i - 1], nums[i] * g[i - 1]);
g[i] = Math.min(nums[i], g[i]);
res = Math.max(res, f[i]);
}
1.3 完全平方数 LC279
题目描述:给一个整数n,返回和为n的完全平方数最少数量
题解:动态规划,难点是状态转移
/**
* 动态规划解法
* f[i] 表示最少需要平方数,其和为i
* f[i] = 1 + min(f[i- j^2])
*/
public int numSquares(int n) {
int[] f = new int[n+1];
f[1] = 1;
for (int i = 2; i <= n; i++) {
int min = Integer.MAX_VALUE;
for (int j = 1; j <= Math.sqrt(i); j++) {
min = Math.min(min, f[i - j * j]);
}
f[i] = 1 + min;
// System.out.println("index: " + i + " f[i]: " + f[i]);
}
return f[n];
}
64.最小路径和
题目:非负整数的 m x n 网格 grid ,请找出一条从左上角到右下角的路径,使得路径上的数字总和为最小 每次只能向下或者向右移动一步
思路:动态规划。dp[i][j]表示从start 到[i,j]位置的最小路径
dp[i][j] = Math.min(dp[i - 1][j], dp[i][j-1]) + grid[i][j]; dp初始值状态:两个边界。
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];
}
1143.最长公共子序列
题目:两个字符串 text1 和 text2,返回这两个字符串的最长 公共子序列 的长度
思路:
dp[i][j] 表示序列text1[0..i] 和 text2[0..j]的最长公共子序列长度 状态转移:
if (t1[i] == t2[j])
dp[i][j] = dp[i-1][j-1] + 1
else
dp[i][j] = Max(dp[i-1][j], dp[i][j-1])
初始状态:
for (int i = 0; i < m; i++) {
dp[i][0] = 0;
}
for (int j = 0; j < n; j++) {
dp[0][j] = 0;
}
583.字符串删除操作
题目:给定两个单词 word1 和 word2 ,返回使得 word1 和 word2 相同所需的最小步数
思路:删除最小步数,即找到最长公共子序列,剩下字符都需要删除
res = m - lcs + n - lcs
787. K 站中转内最便宜的航班
思路:
核心:递归状态转移
final int INF = 10000 * 101 + 1;
// f[t][i]表示从src节点到i节点,通过t次航班,最小费用
for (int i = 0; i < k + 2; ++i) {
Arrays.fill(f[i], INF);
}
for (int t = 1; t <= k + 1; ++t) {
for (int[] flight : flights) {
int j = flight[0], i = flight[1], cost = flight[2];
f[t][i] = Math.min(f[t][i], f[t - 1][j] + cost);
}
}