常见算法技巧
1、给定一个正数int型数组arr,和一个正数目标值target,随意从数组中取出任意个数字(不能重复取)求和,请问有多少种取法可以使总和刚好等于target
(1)普通动态规划
设置dp[m][n]数组,其中m为数组长度,n为target+1。dp[i][j]表示,使用从下标0~i这些位置上的数字,可以获得sum=j的取法。则有状态转移方程:
dp[i][j] = dp[i-1][j] + dp[i-1][j-arr[i]]
根据我们是否使用第i位置上的数字,dp[i][j]的状态由以下两种状态组成:
- 当不使用i位置上的数字时,获得sum=j的取法就等使用0~i-1这些位置上的数字,即dp[i-1][j]
- 当使用i位置上的数字时,把第i位置去掉,目标数字也减去第i位置上的值,则这种情况转化为使用第0~i-1位置上的数组,获得sum=j-arr[i]的取法数量,即dp[i-1][j-arr[i]]。
class Solution{
public int solve(int[] arr, int target) {
int[][] dp = new int[arr.length][target+1];
for (int i = 0; i < arr.length; i++) {
for (int j = 0; j <= target; j++) {
if (i == 0) {
if (arr[i] == j) {
dp[i][j] = 1;
}
}else {
if (j - arr[i] >= 0) {
dp[i][j] = dp[i - 1][j] + dp[i - 1][j - arr[i]];
}else {
dp[i][j] = dp[i - 1][j];
}
}
}
}
return dp[arr.length - 1][target];
}
}
(2)空间压缩型动态规划
从上面的动态转移方程来看,每个外层数组dp[i]都用到了上一个数组dp[i-1]的值,最终获取结果也是最后一个数组的最后一个数字,因此可以用一个数组来表示动态规划表,通过修改这张表来避免使用更多的空间。
class Solution{
public int solve(int[] arr, int target) {
int[] dp = new int[target + 1];
dp[0] = 1;
for (int n: arr) {
for (int i = target; i >= n; i--) {
dp[i] += dp[i - n];
}
}
return dp[target];
}
}
2、用位运算表示中位数的两个位置
class Solution{
public void solve(int[] arr) {
int len = arr.length;
int leftPosition = (len - 1) >> 1
int rightPosition = len >> 1;
// 中位数就等于 (arr[leftPosition] + arr[rightPosition]) / 2
}
}
3、使用位运算求两个数字的均值并取floor,其实就是中间位置的下标
class Solution{
public int solve(int n1, int n2) {
if (n1 > n2) {
return solve(n2, n1);
}
return n1 + ((n2 - n1) >> 1); // 等价于 Math.floor((n1 + n2) / 2)
}
}
4、前缀和(二维)
求随机某个区间或者某个块的和,可以通过前缀和预处理,来降低时间复杂度。
public class NumMatrix {
int[][] sum;
public NumMatrix(int[][] matrix) {
int n = matrix.length, m = n == 0 ? 0 : matrix[0].length;
// 与「一维前缀和」一样,前缀和数组下标从 1 开始,因此设定矩阵形状为 [n + 1][m + 1](模板部分)
sum = new int[n + 1][m + 1];
// 预处理除前缀和数组(模板部分)
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
sum[i][j] = sum[i - 1][j] + sum[i][j - 1] - sum[i - 1][j - 1] + matrix[i - 1][j - 1];
}
}
}
public int sumRegion(int x1, int y1, int x2, int y2) {
// 求某一段区域和 [i, j] 的模板是 sum[x2][y2] - sum[x1 - 1][y2] - sum[x2][y1 - 1] + sum[x1 - 1][y1 - 1];(模板部分)
// 但由于我们源数组下标从 0 开始,因此要在模板的基础上进行 + 1
x1++; y1++; x2++; y2++;
return sum[x2][y2] - sum[x1 - 1][y2] - sum[x2][y1 - 1] + sum[x1 - 1][y1 - 1];
}
}
5、数组,单点修改,区间求和问题
涉及数据结构和算法:树状数组、线段树。参考:https://leetcode-cn.com/problems/range-sum-query-mutable/

浙公网安备 33010602011771号