最基础的“穷竭搜索”“

一、递归函数

//计算斐波那契数列的函数
int fib(int n){
	if(n <= 1) return n;
	return fib(n-1) + fib(n-2)
}

实际使用时,即使是 fib(40) 这样n较小时的结果,也要花费 相当长的时间;
在这里插入图片描述
在这个斐波那契数列中,如果 fib(n) 的n是一定的,多次调用都会得到同样的结果。如果计算一次之后,用数列储存起来,便可优化之后的计算。
这种方法是出于 记忆化搜索 或者 动态规划 的想法。

二、栈(Stack)和 队列(Queue)

栈(Stack)通过push和pop在栈的顶端存取数据
LIFO:Last In First Out,即后进先出。

队列(Queue)通过offer和poll在队列的底端存取数据
FIFO:First In Frst Out,即先进先出。

三、深度优先搜索(DFS)

  • 深度优先,就是以深度为准则。
  1. 递归下去(先一条路走到底,直到达到目标)
  2. 回溯上来(没有达到目标又无路可走了,则退回到上一步的状态,走其他路)
    根据DFS的特点,采用 递归 函数实现比较简单

#01 部分求和问题

题目

给定整数a1 、a2、…、a2,判断是否可以从中选出若干数,使它们的和恰好为 k 。

样例

n = 4
a = { l, 2, 4, 7 }
k = 13

Yes ( 13 = 2 + 4 + 7 )

思路

  • 从 a1 开始按顺序决定每个数加或不加,在全部n个数都决定后再判断它们的和是不是 k 即可。因为状态数是 2n+1 ,所以复杂度是 O(2n) 。
  • 注意α的下标与题目描述中的下标偏移了1 。在程序中使用的是 0 起始的下标规则,题目描述中则是 1 开始的,这一点要注意避免搞混。

在这里插入图片描述

递归函数的作用:深度遍历所有分支,判断是否存在和为 k 的组合
递归结束条件:遍历到最后一个数,判断和是否为 k( if i == n,return sum == k)
遍历顺序:后序遍历

代码

static int n, k;
//若使用全局数组不能动态分配空间
static List<Integer> list=new ArrayList<>();

public static void main(String[] args) {
	Scanner scan=new Scanner(System.in);
	n=scan.nextInt();
	for(int i=0;i<n;i++)
		list.add(scan.nextInt());
	k=scan.nextInt();
	scan.close();
	
	if(dfs(0, 0)) System.out.println("Yes");
	else System.out.println("No");
}

//已经从前 i 项得到了和 sum ,然后对于 i 项之后的进行分支(后序遍历)
public static boolean dfs(int i, int sum) {
	//结束条件:遍历到最后一个,判断sum和k是否相等(根节点)
	if(i==n) return sum==k;
	
	//不加a[i]的情况(左子树)
	if(dfs(i+1, sum)) return true;
	
	//加上a[i]的情况(右子树)
	if(dfs(i+1, sum + list.get(i))) return true;

四、广度优先搜索(BFS)

  • 广度优先搜索旨在面临一个路口时,把所有的岔路口都记下来,然后选择其中一个进入,然后将它的分路情况记录下来,然后再返回来进入另外一个岔路,并重复这样的操作。最后找出所有可行路径。
  • 在BFS中,关键点则是状态的 选取标记

#03 迷宫的最短路径

题目

给定一个大小为 N × M 的迷宫。迷宫由通道和墙壁组成,每一步可以向邻接的上下左右四格的通道移动。请求出从起,点到终点所需的最小步数。请注意,本题假定从起点一定可以移动到终点。

样例

10 10
在这里插入图片描述

22

思路

  • BFS按照距开始状态 由近及远 的顺序进行搜索,因此可以很容易地用来求 最短路径最少操作 之类问题的答案。
  • 要将已经访问过的状态用标记管理起来,就可以很好地做到由近及远的搜索。(用数组把最短距离保存起来。初始时用充分大的常数INF来初始化它,这样尚未到达的位置就是INF ,也就同时起到了标记的作用。)
  • 虽然到达终点时就会停止搜索,可如果继续下去直到队列为空的话,就可以计算出到各个位置的最短距离。此外,如果搜索到最后, d依然为INF的话,便可得知这个位置就是无法从起点到达的位置。
  • *这个问题中,状态仅仅是目前所在位置的坐标,因此可以用数组来表示状态。当状态更加复杂时,就需要封https://600s.com/attach_download/?f_id=cd6d4ac8205401a9e9aadff2419ea092&f_name=92EF5A11-3049-484E-86C8-0CDE20199CEE.jpeg
    装成一个类来表示状态了。

步骤

  1. 需要的变量:
    map 地图数据
    startgoal :起点和终点的坐标
    distance 每个点到起点的最小距离,初始化最大值,起点初始为0
    Queue 待遍历的节点,FIFO 初始保存起点
    tx, ty 保存新位置的坐标
  2. 先遍历 队列 里的节点
    取出底端的节点(current)进行遍历四个方向:
    如果符合条件,则算出它到起点的距离( current 到起点的距离+1)
    并将其加入队列,等待下一轮遍历

代码

public static void main(String[] args) {
	Scanner scan = new Scanner(System.in);

	int n = scan.nextInt(); // 行
	int m = scan.nextInt(); // 列
	scan.nextLine(); // 吸收上一行输入的\n
	char[][] map = new char[n][m]; // 地图数据
	int[] start = new int[2]; // 起点
	int[] goal = new int[2]; // 终点

	// 写入地图数据到map
	for (int i = 0; i < n; i++) {
		String s = scan.nextLine();
		map[i] = s.toCharArray();
		// 给起点赋值
		if (s.contains("S")) {
			start[0] = i;
			start[1] = s.indexOf("S");
		}
		// 给重点赋值
		if (s.contains("G")) {
			goal[0] = i;
			goal[1] = s.indexOf("G");
		}
	}

	scan.close();
	
	System.out.println(bfs(map, start, goal));
}

//
public static int bfs(char[][] map, int[] start, int[] goal) {

	// 设置四个方向
	int[] dx = { 1, 0, -1, 0 };
	int[] dy = { 0, 1, 0, -1 };
	//记录起点到每个可能的点的距离
	int[][] d = new int[map.length][map[0].length];
	//队列保存待遍历的节点
	Queue<int[]> que = new LinkedList<>();

	for (int i = 0; i < d.length; i++)
		for (int j = 0; j < d[0].length; j++)
			d[i][j] = Integer.MAX_VALUE;
	
	//把起点加入队列,并设置距离为0
	que.offer(start);
	d[start[0]][start[1]] = 0;

	while (!que.isEmpty()) {
		
		//取出最底端的节点
		int[] current = que.poll();

		//如果找到则跳出循环
		//如果有多条路通往终点,先找到的肯定是最近的
		if (current[0] == goal[0] && current[1] == goal[1])
			break;

		// 遍历四个方向
		//计算所有移动的点到起点的距离(上一个点到起点的距离+1)
		//将可能的节点加入队列(可能加入多个节点)
		for (int i = 0; i < 4; i++) {

			//新位置的坐标
			int tx = current[0] + dy[i];
			int ty = current[1] + dx[i];

			// 新位置必须在下标范围内,不能为墙(#),且未被赋值(即不能为初始值MAX_VALUE)
			if (ty >= 0 && ty < d[0].length && tx >= 0 && tx < d.length && map[tx][ty] != '#'
					&& d[tx][ty] == Integer.MAX_VALUE) {
				//新位置等于上一个节点的距离加1
				d[tx][ty] = d[current[0]][current[1]] + 1;
				//将下一个节点放入队列
				int[] c = { tx, ty };
				que.offer(c);
			}
		}
	}
	
	//返回终点到起点的距离
	return d[goal[0]][goal[1]];
}

五、DFS和BFS的使用

  1. BFS是用来搜索 最短径路 的解是比较合适的,比如求最少步数的解,最少交换次数的解,因为BFS搜索过程中遇到的解一定是离根最近的,所以遇到一个解,一定就是最优解,此时搜索算法可以终止。
    这个时候不适宜使用DFS,因为DFS搜索到的解不一定是离根最近的,只有全局搜索完毕,才能从所有解中找出离根的最近的解。(当然这个DFS的不足,可以使用迭代加深搜索ID-DFS去弥补)

  2. 空间优劣上,DFS是有优势的,DFS不需要保存搜索过程中的状态,而BFS在搜索过程中需要保存搜索过的状态,而且一般情况需要一个队列来记录。

  3. DFS适合搜索 全部的解 ,因为要搜索全部的解,那么BFS搜索过程中,遇到离根最近的解,并没有什么用,也必须遍历完整棵搜索树,DFS搜索也会搜索全部,但是相比DFS不用记录过多信息,所以搜素全部解的问题,DFS显然更加合适。

六、全排列模板(整数数组)

确定第一位的可能:1,2,3

循环
第一位为 1 时,确定第二位的可能(递归):2,3
第一位为 2 时,确定第二位的可能 f ( k+1 ) :1,3
第一位为 3 时,确定第二位的可能 f ( k+1 ) :1,2

static int arr[] = { 1, 2, 3, 4};
public static void main(String[] args) {
	f(0);
}

private static void f(int k) {				//k:确定排列的第k位
	if (k == arr.length) { 					//全部确认
		check();
		return;
	}
	for (int i = k; i < arr.length; i++) {
		
		//将第i位和第k位交换(第k位的数字做第i位的情况)
		int t = arr[i];
		arr[i] = arr[k];
		arr[k] = t;

		f(k + 1); 							// 移交下一层去确认k+1位

		// 回溯(换回来,避免出现错误)
		t = arr[i];
		arr[i] = arr[k];
		arr[k] = t;
	}
}

//具体检查区
static boolean check(){
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值