算法相关数据结构总结:
序号 | 数据结构 | 文章 |
---|---|---|
1 | 动态规划 | 动态规划之背包问题——01背包 动态规划之背包问题——完全背包 动态规划之打家劫舍系列问题 动态规划之股票买卖系列问题 动态规划之子序列问题 算法(Java)——动态规划 |
2 | 数组 | 算法分析之数组问题 |
3 | 链表 | 算法分析之链表问题 算法(Java)——链表 |
4 | 二叉树 | 算法分析之二叉树 算法分析之二叉树遍历 算法分析之二叉树常见问题 算法(Java)——二叉树 |
5 | 哈希表 | 算法分析之哈希表 算法(Java)——HashMap、HashSet、ArrayList |
6 | 字符串 | 算法分析之字符串 算法(Java)——字符串String |
7 | 栈和队列 | 算法分析之栈和队列 算法(Java)——栈、队列、堆 |
8 | 贪心算法 | 算法分析之贪心算法 |
9 | 回溯 | Java实现回溯算法入门(排列+组合+子集) Java实现回溯算法进阶(搜索) |
10 | 二分查找 | 算法(Java)——二分法查找 |
11 | 双指针、滑动窗口 | 算法(Java)——双指针 算法分析之滑动窗口类问题 |
文章目录
前面整理了01背包,在leetcode题库中主要就是01背包和完全背包问题,所以在这里整理一下完全背包的知识点。
一、完全背包问题
有N件物品和一个最多能背重量为W的背包。第i件物品的重量是weight[i],得到的价值是value[i] 。每件物品都有无限个(也就是可以放入背包多次),求解将哪些物品装入背包里物品价值总和最大。
完全背包和01背包问题唯一不同的地方就是,每种物品有无限件。
注:leetcode上没有纯完全背包问题,都是需要完全背包的各种应用,需要转化成完全背包问题。
01背包和完全背包唯一不同就是体现在遍历顺序上,所以针对遍历顺序进行分析。其它动规五部曲参考01背包。
二、完全背包遍历顺序
首先回顾一下01背包的遍历顺序:
for(int i = 0; i < weight.size(); i++) {
// 遍历物品
for(int j = bagWeight; j >= weight[i]; j--) {
// 遍历背包容量
dp[j] = max(dp[j], dp[j - weight[i]] + value[i]);
}
}
我们知道01背包内嵌的循环是从大到小遍历,为了保证每个物品仅被添加一次。
而完全背包的物品是可以添加多次的,所以要从小到大去遍历,即:
// 先遍历物品,再遍历背包
int[] dp = new int[bagWeight + 1];
for (int i = 0; i < weight.length; i++){
for (int j = 1; j <= bagWeight; j++){
if (j - weight[i] >= 0){
dp[j] = Math.max(dp[j], dp[j - weight[i]] + value[i]);
}
}
}
为什么遍历物品在外层循环,遍历背包容量在内层循环?
在01背包中二维dp数组的两个for遍历的先后循序是可以颠倒,一维dp数组的两个for循环先后循序一定是先遍历物品,再遍历背包容量。
在完全背包中,对于一维dp数组来说,其实两个for循环嵌套顺序同样无所谓。
因为dp[j]
是根据 下标j之前所对应的dp[j]
计算出来的。 只要保证下标j之前的dp[j]
都是经过计算的就可以。完全背包中,两个for循环的先后循序,都不影响计算dp[j]所需要的值。
先遍历被背包在遍历物品,代码如下:
// 先遍历背包,再遍历物品
for(int j = 0; j <= bagWeight; j++) {
// 遍历背包容量
for(int i = 0; i < weight.size(); i++) {
// 遍历物品
if (j - weight[i] >= 0) dp[j] = max(dp[j], dp[j - weight[i]] + value[i]);
}
}
注:全文说的都是对于纯完全背包问题,其for循环的先后循环是可以颠倒的!但如果题目稍稍有点变化,就会体现在遍历顺序上。
如果问装满背包有几种方式的话? 那么两个for循环的先后顺序就有很大区别了,而leetcode上的题目都是这种稍有变化的类型。这些将根据具体的题目进行分析。