1、lintcode375克隆二叉树(简单)
代码:
class Solution {
public:
/**
* @param root: The root of binary tree
* @return: root of new tree
*/
TreeNode * cloneTree(TreeNode * root) {
// write your code here
if(root==NULL) return NULL;
TreeNode* newTree=new TreeNode(root->val);
//以相同方法对左子树和右子树继续进行复制
newTree->right=cloneTree(root->right);
newTree->left=cloneTree(root->left);
return newTree;
}
};
解析:
本题的核心在于深度拷贝,即要new一个新的空间出来存放新的二叉树,并且新的二叉树和原二叉树保持一致。采用递归,先对当前节点开辟空间,然后左右子节点用相同的方法向下递归即可。
2、lintcode2尾部的0(简单)
代码:
class Solution {
public:
/**
* @param n: A long integer
* @return: An integer, denote the number of trailing zeros in n!
*/
long long trailingZeros(long long n) {
// write your code here
//递归结束条件
if(n==0) return 0;
//继续判断有几个5
return n/5+trailingZeros(n/5);
}
};
解析:
写本题时要注意思维的巧妙转换,虽然题目要求计算的是n的阶乘中末尾0的个数,但其实n的阶乘可以不用直接计算,因为末尾0的个数只取决于式中含5的个数。因为5和任意偶数相乘可以得10,而分析易知偶数的个数肯定比5的个数要多,换句话说也就是任意的5都能找到偶数和它匹配,所以这里只用统计阶乘的式子中5的个数。故我们只用从n到1,依次判断是否可以被5整除,将结果进行累加即可。
3、lintcode92背包问题(简单)
代码:
① 迭代做法,设置数组dp,通过动态规划的记忆化方法实现
class Solution {
public:
/**
* @param m: An integer m denotes the size of a backpack
* @param a: Given n items with size A[i]
* @return: The maximum size
*/
int backPack(int m, vector<int> &a) {
// write your code here
int len=a.size();
//滚动数组dp[j]:容量为j的情况下的最大装载量
vector<int> dp(m+1,0);
//从头遍历每一个物体
for(int i=0;i<len;i++)
{
//遍历背包容量的每种状态
for(int j=m;j>=a[i];j--)
{
//更新(不放or放)
dp[j]=max(dp[j],dp[j-a[i]]+a[i]);
}
}
return dp[m];
}
};
②递归做法
class Solution {
//声明全局数组dp
//dp的作用:避免重复计算子问题
vector<vector<int>> dp;
int len;
public:
/**
* @param m: An integer m denotes the size of a backpack
* @param a: Given n items with size A[i]
* @return: The maximum size
*/
int backPack(int m, vector<int> &a) {
// write your code here
len=a.size();
//初始化dp的大小
dp=vector<vector<int>>(len+1,vector<int>(m+1,-1));
//目标容量:m
return dfs(a,0,m);
}
//i:第i个物品,j:背包容量
int dfs(vector<int> &v,int i,int j)
{
//说明该组合已经计算过,直接返回
if(dp[i][j]!=-1) return dp[i][j];
//异常情况
//避免后面一致重复计算异常情况
if(i>=len||j<0)
{
dp[i][j]=0;
return 0;
}
int max1=0,max2=0;
//可以容纳v[i]
if(j-v[i]>=0) max1=dfs(v,i+1,j-v[i])+v[i];
//不能容纳,和前一个物品保持一致,代表不选
//注意这里不要写成s
//因为无论怎样都要考虑不选的情况
//而不是只有背包容纳不下才不选
max2=dfs(v,i+1,j);
//选取最大的
dp[i][j]= max(max1,max2);
return dp[i][j];
}
};
解析:
1、为什么可以使用 i + 1?
在递归函数中,i 表示当前正在考虑的物品索引。使用 i + 1 意味着我们在递归调用中考虑下一个物品。这种方式实际上是在问:
当前选择:对于第 i 个物品,我们有两个选择:
选中当前物品:将当前物品的重量加入总重量,然后在剩余容量 j - v[i] 下,考虑下一个物品 i + 1
不选当前物品:直接在容量 j 下,考虑下一个物品 i + 1。
这种方法的本质是自顶向下的递归,每个状态依赖于其 后续状态。虽然这看起来有些反直觉,但在递归树中,每个节点的选择都会影响其子节点,而不是父节点。
2、与使用 i - 1 的区别
当你使用 i - 1 时,表示你从最后一个物品开始,向前遍历。这种方法的逻辑是:
当前选择:对于第 i 个物品,我们有两个选择:
选中当前物品:将当前物品的重量加入总重量,然后在剩余容量 j - v[i] 下,考虑前一个物品 i - 1。
不选当前物品:直接在容量 j 下,考虑前一个物品 i - 1。
这种方法是自底向上的递归,每个状态依赖于其 前序状态。
3、两种方法的等价性
无论你选择从前往后(i + 1)还是从后往前(i - 1),最终的结果应该是一致的。关键在于:
初始调用和终止条件需要与遍历方向匹配。
状态转移方程需要正确地反映物品的选择和剩余容量的变化。
4、举例说明
假设我们有物品集合 A = [2, 3, 5, 7],背包容量 m = 10。
使用 i + 1:
从物品索引 i = 0 开始,考虑物品 A[0] = 2。
递归调用 dfs(v, i + 1, j),表示考虑下一个物品 A[1]。
每个递归层级向下,索引增加,直到超过物品数量。
使用 i - 1:
从物品索引 i = len - 1 开始,考虑物品 A[3] = 7。
递归调用 dfs(v, i - 1, j),表示考虑前一个物品 A[2]。
每个递归层级向下,索引减少,直到小于 0。