深度优先算法DFS(Depth First Search)

一、基本思想

不撞南墙不回头

  1. 为了求得问题的解,先选择某一种可能情况向前探索;
  2. 在探索过程中,一旦发现原来的选择是错误的,就退回一步重新选择,继续向前探索;
  3. 如此反复进行,直至得到解或证明无解。

二、算法原理

深度优先搜索(Depth First Search),是图遍历算法的一种。用一句话概括就是:"一直往下走,走不通回头,换条路再走,直到无路可走"。

假设我们有一个二叉树,共有10个节点,以下是DFS的简单示范:

从根节点开始向下搜索

然后搜索到2号节点

继续不断向深层处的节点搜索,搜索到4号节点

最后搜索到7号节点

当搜索到7号节点后,我们发现无路可走了,因为7号节点是当前这条路径下最深处的节点,因此,我们需要进行回溯操作

当回溯到4号节点时,我们发现4号节点并没有另一条路,也就是说从4号节点向下搜索的话,只能搜索到7号节点,但是可是刚刚才从7号节点回溯上来诶,我们总不可能又搜索到7号,然后又回溯到4号无限下去吧......所以,我们得再次回溯,也就是跳到2号节点上。

当再次跳到2号节点上时,我们发现从2号节点开始,还有另一条路可以走。那我们就走下去!

此时又有两条路可以走,我们先去往8号

走到8号,我们发现又走到头了,那就再对它使用回溯吧!!!

这次我们选择另一条路,走到9号

然后我们发现又双走到头了,因此,再次回溯,从9号跳到5号,再跳到2号,然后再跳到1号(因为5号,2号向下的路我们已经走过了,但我们发现1号节点向下的路还有一条是我们没走过的)

接下来搜索类似,我们走到3号,然后走到6号,然后走到10号

当10号走完后,这颗树的每个节点都被搜索过了,最后回溯到根节点

三、模板

1、C模板

int a[510];   //存储每次选出来的数据
int book[510];   //标记是否被访问
int ans = 0; //记录符合条件的次数

void DFS(int cur){
	if(cur == k){ //k个数已经选完,可以进行输出等相关操作 
		for(int i = 0; i < cur; i++){
			printf("%d ", a[i]);
		} 
		ans++;
		return ;
	}
	
	for(int i = 0; i < n; i++){ //遍历 n个数,并从中选择k个数 
		if(!book[i]){ //若没有被访问 
			book[i] = 1; //标记已被访问 
			a[cur] = i;  //选定本数,并加入数组 
			DFS(cur + 1);  //递归,cur+1 
			book[i] = 0;  //释放,标记为没被访问,方便下次引用 
		}
	}
}

2、C++模板

vector<int> a; // 记录每次排列 
vector<int> book; //标记是否被访问 

void DFS(int cur, int k, vector<int>& nums){
    if(cur == k){ //k个数已经选完,可以进行输出等相关操作 
        for(int i = 0; i < cur; i++){
			printf("%d ", a[i]);
		} 
        return ;
    }
    for(int i = 0; i < k; i++){ //遍历 n个数,并从中选择k个数 
        if(book[nums[i]] == 0){ //若没有被访问
            a.push_back(nums[i]); //选定本输,并加入数组 
            book[nums[i]] = 1; //标记已被访问 
            DFS(cur + 1, n, nums); //递归,cur+1 
            book[nums[i]] = 0; //释放,标记为没被访问,方便下次引用 
            a.pop_back(); //弹出刚刚标记为未访问的数
        }
    }
}

四、例题(持续更新)

1、例题一(全排列)

设有n个整数的集合{1,2,...,n},从中取出任意r个数进行排列(1<=r<n<=10),试列出所有的排列。

示例:

输入:n = 4, r = 3
输出:
1 2 3
1 2 4
1 3 2
1 3 4
1 4 2
1 4 3
2 1 3
2 1 4
2 3 1
2 3 4
2 4 1
2 4 3
3 1 2
3 1 4
3 2 1
3 2 4
3 4 1
3 4 2
4 1 2
4 1 3
4 2 1
4 2 3
4 3 1
4 3 2
24

思路:

1)定义两个数组 a[] 与 book[] ,其中数组a保存每次的排列数据,数组book用来标记 i 这个数是否被访问;

2)初始化相关数据;

3)递归填数并判断第i个数填入是否合法:

合法:填数,并判断是否已经到达环的终点。如果到达终点,打印结果;否则,继续填下一个 数;

不合法:选择下一种可能。

代码:

#include<iostream>

using namespace std;

int n, r, ans;  //r个数进行全排列  ans为排列个数 
int book[510]; //标记是否被访问
int a[510]; //记录每次的排列数据

void DFS(int cur){ //从{1,2,...,n}中取r个数构成的排列
	if(cur == r){ //已经去够r个数 
		for(int i = 0; i < cur; i++){ //循环输出 
			cout << a[i] << ' ';
		}
		cout << endl;
		ans++;  //数量加1 
		return ;
	}
	
	for(int i = 1; i <= n; i++){  //循环遍历保证不漏 
		if(!book[i]){ //若没访问过 
			book[i] = 1; //标记已访问
			a[cur] = i; //i符合条件加入
			DFS(cur + 1); //寻找一个数字 
			book[i] = 0; //回溯:清除标记
		}
	} 
} 

int main(){
	cin >> n >> r;
	DFS(0);
	
	cout << ans << endl;
	return 0; 
}
相关推荐
懒惰才能让科技进步17 分钟前
从零学习大模型(十二)-----基于梯度的重要性剪枝(Gradient-based Pruning)
人工智能·深度学习·学习·算法·chatgpt·transformer·剪枝
Ni-Guvara31 分钟前
函数对象笔记
c++·算法
泉崎1 小时前
11.7比赛总结
数据结构·算法
你好helloworld1 小时前
滑动窗口最大值
数据结构·算法·leetcode
AI街潜水的八角2 小时前
基于C++的决策树C4.5机器学习算法(不调包)
c++·算法·决策树·机器学习
白榆maple2 小时前
(蓝桥杯C/C++)——基础算法(下)
算法
JSU_曾是此间年少2 小时前
数据结构——线性表与链表
数据结构·c++·算法
此生只爱蛋3 小时前
【手撕排序2】快速排序
c语言·c++·算法·排序算法
咕咕吖4 小时前
对称二叉树(力扣101)
算法·leetcode·职场和发展
九圣残炎4 小时前
【从零开始的LeetCode-算法】1456. 定长子串中元音的最大数目
java·算法·leetcode