递归函数的参数设置
最近在刷dfs、回溯时,发现不同的题解中用了不同的参数设置,导致算法的流程的不同,在此记录一下。
全局变量
在回溯过程中,每次递归之后必须撤销对变量的修改,才能保证下次递归时变量返回正确的状态。
class Solution
{
private:
vector<vector<int>> ans;
vector<int> tmp;
public:
void backtrace(vector<int> &nums, int ind)
{
// 结束条件.
if (ind == nums.size())
{
ans.emplace_back(tmp);
return;
}
// 跳过该元素并进入下一层.
dfs(nums, ind + 1);
// 选择该元素.
tmp.emplace_back(nums[ind]);
// 进入下一层.
dfs(nums, ind + 1);
// 撤销选择.
tmp.pop_back();
}
vector<vector<int>> combination(vector<int> &nums)
{
dfs(nums, 0);
return ans;
}
};
引用传递
相当于使用全局变量,在回溯时需要撤销选择。
对引用变量做的任何修改都是对它所引用的变量内存位置中数据的修改,所以数据只有一份。递归结束读取栈中的存取的返回地址和局部变量,而局部变量是内存数据的引用,而引用的本质就是所引用对象的地址,地址没有改变,那么内存中的数据也不会改变,递归做的修改仍然存在,需要手动撤销选择。
class Solution
{
public:
void backtrace(vector<int> &nums, vector<vector<int>> &ans, vector<int> &tmp, int ind)
{
// 结束条件.
if (ind == nums.size())
{
ans.emplace_back(tmp);
return;
}
// 跳过该元素并进入下一层.
dfs(nums, ans, tmp, ind + 1);
// 选择该元素.
tmp.emplace_back(nums[ind]);
// 进入下一层.
dfs(nums, ans, tmp, ind + 1);
// 撤销选择.
tmp.pop_back();
}
vector<vector<int>> combination(vector<int> &nums)
{
// 需要先定义递归用到的变量.
vector<vector<int>> ans;
vector<int> tmp;
// 将变量以引用传递的方式传递给形参.
dfs(nums, ans, tmp, 0);
return ans;
}
};
值传递
退回到上次保存的调用堆栈中保存的状态,所以在递归结束时,变量状态回到上次调用保存的状态,本次对变量做的修改会消失。
不需要手动恢复。
在回溯中,不能使用值传递的方式保存需要最后返回的元素,例如最后的结果 ans
和存储路径分支的 tmp
,因为在递归结束后它们会返回最初的状态,丢失所有的信息。
但是记录递归层数的 ind
适合用值传递的方式使用,当回退当上一层时,读取调用堆栈中的内存,ind
将自动恢复。