函數遞歸調用時變量的變化情況

以一道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;
}

因此,遞歸函數中的變量用函數形參會省去很多麻煩。

另外,當形參爲引用類型時,也需要恢復原樣,因爲對每層遞歸都是使用同一個變量,所以效果上類似於使用全局變量

修改於2020/3/4~

再看此題,寫是先存儲所有路徑,再輸出的方法:

#include<bits/stdc++.h>
using namespace std;
struct node{
	int val;
	vector<int> child; 
}no[100];
vector<vector<int>> path;
vector<int> temppath;
void dfs(int id,int sum,int &s){
	temppath.push_back(no[id].val);
	sum+=no[id].val;
	if(no[id].child.empty()){
		if(sum==s) path.push_back(temppath);
		temppath.pop_back();
		return;
	}
	for(int i=0;i<no[id].child.size();i++){
		dfs(no[id].child[i],sum,s);
	}
	temppath.pop_back();
}
int main() {
	int n,m,s;
	cin>>n>>m>>s;
	for(int i=0;i<n;i++) scanf("%d",&no[i].val);
	while(m--){
		int id,k,temp;cin>>id>>k;
		while(k--){
			scanf("%d",&temp);
			no[id].child.push_back(temp);
		}
	}
	dfs(0,0,s);
	sort(path.begin(),path.end());
	for(auto it1=path.rbegin();it1!=path.rend();it1++){
		auto it2=it1->begin();
		for(int i=0;i<it1->size();i++){
			if(i!=0) printf(" ");
			printf("%d",*(it2+i));
		}
		printf("\n");
	}
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章