刷题小结

思路

问题类型

  • 解决

双指针

  • 遍历数组
  • 寻找链表的环
  • 节约遍历时间
  • 反转,回文

位运算

异或运算

  • 利用 x ^ 1s = ~x 的特点,可以将位级表示翻转;利用 x ^ x = 0 的特点,可以将三个数中重复的两个数去除,只留下另一个数。
  • 利用 x & 0s = 0 和 x & 1s = x 的特点,可以实现掩码操作。一个数 num 与 mask:00111100 进行位与操作,只保留 num 中与 mask 的 1 部分相对应的位。
  • 利用 x | 0s = x 和 x | 1s = 1s 的特点,可以实现设值操作。一个数 num 与 mask:00111100 进行位或操作,将 num 中与 mask 的 1 部分相对应的位都设置为 1。

位与运算

  • n&(n-1) 去除 n 的位级表示中最低的那一位。例如对于二进制表示 10110100,减去 1 得到 10110011,这两个数相与得到 10110000。
  • n&(-n) 得到 n 的位级表示中最低的那一位。-n 得到 n 的反码加 1,对于二进制表示 10110100,-n 得到 01001100,相与得到 00000100。
  • n-n&(~n+1) 去除 n 的位级表示中最高的那一位。

字符串

  • 使用长度为 256 的整型数组来统计每个字符出现的个数,每个字符有偶数个可以用来构成回文字符串。

动态规划

​ 后面的状态依赖于前面已知的状态,根据前面已知,以及最新的数字,可以对后面状态进行推导

0-1背包问题

​ 题目描述:有一个容量为 N 的背包,要用这个背包装下物品的价值最大,这些物品有两个属性:体积 w 和价值 v。

  • 将容量N,价值MaxV的问题,分解到了求在容量1-N下,价值MaxV的问题

  • dp[i][j] 表示前 i 件物品体积不超过 j 的情况下能达到的最大价值。设第 i 件物品体积为 w,价值为 v,根据第 i 件物品是否添加到背包中,可以分两种情况讨论:

    • 第 i 件物品没添加到背包,总体积不超过 j 的前 i 件物品的最大价值就是总体积不超过 j 的前 i-1 件物品的最大价值,dpij = dp(i-1)j。
    • 第 i 件物品添加到背包中,dpij= dp(i-1)(j-w) + v。

    img

    ​ 在程序实现时可以对 0-1 背包做优化。观察状态转移方程可以知道,前 i 件物品的状态仅与前 i-1 件物品的状态有关,因此可以将 dp 定义为一维数组,其中 dp[j] 既可以表示 dp(i-1)j 也可以表示 dpij。此时,

    img

    ​ 因为 dp[j-w] 表示 dp[i-1][j-w],因此不能先求 dp[i][j-w],以防将 dp[i-1][j-w] 覆盖。也就是说要先计算 dp[i][j] 再计算 dp[i][j-w],在程序实现时需要按倒序来循环求解。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    public int knapsack(int W, int N, int[] weights, int[] values) {
    int[] dp = new int[W + 1];
    for (int i = 1; i <= N; i++) {
    int w = weights[i - 1], v = values[i - 1];
    for (int j = W; j >= 1; j--) {
    if (j >= w) {
    dp[j] = Math.max(dp[j], dp[j - w] + v);
    }
    }
    }
    return dp[W];
    }

完全背包问题

​ 0-1 背包和完全背包在实现上的不同之处是,0-1 背包对物品的迭代是在最外层,而完全背包对物品的迭代是在最里层。

数据结构相关

链表

  • 如果是两个链表找交点,找环,一般都是走两个指针

总结

算法是对数据的加工技巧。

参考

  1. CyC2018