函数递归调用时变量的变化情况

以一道PAT题为例,题目:点击这里1053. Path of Equal Weight (30)
题目大意:求从根节点到叶节点权值和等于给定数S的所有路径,输出权值。所有的路径按非递增输出。路径a小于路径b表示a输出的权值序列小于b(以第一个不同的值计算)。

思路:
1.考虑到每个节点可能对应多个子节点,用结构体表示节点,vector存放子节点的编号。
2.在读入子节点时,按权值对子节点排序,以便在遍历时先输出权值大的节点序列。
3.使用DFS算法,每次递归调用时,权值加上当前节点的权值,若节点为有效路径节点,则将其编号加入到path中,若
(1)sum>s,直接return;
(2)sum==s,判断是否为子节点;若为子节点,将其加入到path中,并依次输出path对应的权值。
(3)sum<s,将节点存入到path中,枚举当前节点的所有子节点,递归调用DFS。

:这里思考的问题即是如何存放path,可以想到两种方法:
(1)使用STL的vector,作为全局变量。但每次递归结束后必须对该变量修改,恢复原值,否则回溯到上一层时变量的值被改变,程序出错。
代码如下:

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
struct node {
	int w; //权值
	vector<int> child;
}no[105];
bool cmp(int a, int b) {
	return no[a].w > no[b].w;
}
vector<int> path;
int s;
void DFS(int id, int sum) {
	sum += no[id].w;
	if (sum > s) {
		return;
	}
	else if (sum == s) {
		if (no[id].child.size() == 0) {
			path.push_back(id);
			for (int i = 0; i < path.size(); i++) {
				if (i != path.size() - 1) cout << no[path[i]].w << " ";
				else cout << no[path[i]].w << endl;
			}
			path.pop_back();  //恢复原状
		}
		return;
	}
	else {
		path.push_back(id);
		for (int i = 0; i < no[id].child.size(); i++) {
			DFS(no[id].child[i], sum);
		}
		path.pop_back(); //恢复原状
	}
}
int main() {
	int n, m;
	cin >> n >> m >> s;
	for (int i = 0; i < n; i++) {
		cin >> no[i].w;
	}
	while (m--) {
		int id, k, c;
		cin >> id >> k;
		for (int i = 0; i < k; i++) {
			cin >> c;
			no[id].child.push_back(c);
		}
		sort(no[id].child.begin(), no[id].child.end(), cmp); //每次对子节点的编号根据权值大小进行排序
	}
	DFS(0, 0); //根节点编号为0
	return 0;
}

(2)同样使用vector存放,但将其作为函数参数。因为递归调用函数时,实际上,从内存分布上看,每一层调用都保存了该层函数的参数,因此递归返回上层时,不会影响原参数值。因此将其作为函数参数递归时无需恢复原状。

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
struct node {
	int w; //权值
	vector<int> child;
}no[105];
bool cmp(int a, int b) {
	return no[a].w > no[b].w;
}
int s;
void DFS(int id, int sum,vector<int> path) {
	sum += no[id].w;
	if (sum > s) {
		return;
	}
	else if (sum == s) {
		if (no[id].child.size() == 0) {
			path.push_back(id);
			for (int i = 0; i < path.size(); i++) {
				if (i != path.size() - 1) cout << no[path[i]].w << " ";
				else cout << no[path[i]].w << endl;
			}
			//path.pop_back();  //无需恢复原状
		}
		return;
	}
	else {
		path.push_back(id);
		for (int i = 0; i < no[id].child.size(); i++) {
			DFS(no[id].child[i], sum, path);
		}
		//path.pop_back(); //无需恢复原状
	}
}
int main() {
	int n, m;
	cin >> n >> m >> s;
	for (int i = 0; i < n; i++) {
		cin >> no[i].w;
	}
	while (m--) {
		int id, k, c;
		cin >> id >> k;
		for (int i = 0; i < k; i++) {
			cin >> c;
			no[id].child.push_back(c);
		}
		sort(no[id].child.begin(), no[id].child.end(), cmp); //每次对子节点的编号根据权值大小进行排序
	}
	vector<int> p;
	DFS(0, 0, p); //根节点编号为0
	return 0;
}

因此,递归函数中的变量用函数形参会省去很多麻烦。

另外,当形参为引用类型时,也需要恢复原样,因为对每层递归都是使用同一个变量,所以效果上类似于使用全局变量

发布了26 篇原创文章 · 获赞 5 · 访问量 414

猜你喜欢

转载自blog.csdn.net/weixin_43590232/article/details/104021550