第四章 搜索基础

第四章 搜索基础

1、dfs

⭐️深度优先搜索
深度优先搜索(DepthFirst Search,DFs),在图论中是一种用于遍历或搜索树或图的算法。所谓深度优先,就是说每次都尝试向更深的节点走。
在搜素算法中,DFS是指利用递归函数方便地实现暴力枚举的算法,与图论中的DFS算法有一定相似之处,但并不完全相同。

⭐️回溯法
回溯法(Backtracking)是深度优先搜索(dfs)的一种。它系统地搜索所有可能的解,常用于解决组合问题、排列问题、子集问题以及图的遍历等。其基本思想是通过递归的方式尝试构建解,并在发现当前路径无法形成有效解时,及时“回溯”到上一个步骤,尝试其他可能的路径。
回溯法的基本思路
选择:在每一步选择一个可能的选项(例如,选择一个数字、字符或状态)。
约束:在选择后,检查当前状态是否满足约束条件(例如,是否满足题目要求)。
目标:如果当前状态满足目标条件,记录结果(即找到一个解)。
回溯:如果当前状态无法继续前进,撤回上一步选择,尝试其他可能的选项。

📖
在这里插入图片描述
📚

int func(int i){
	if(i == 1 || i == 2) return 1;
	return func(i-1) + func(i-2);
}

在用dfs实现代码时,新手最好对于每一题都画出递归树,了解dfs的运行顺序。从而更好掌握dfs。
例如对于斐波拉契数列,我们有递归公式f(n)=f(n-1)+f(n-2),我们可以画出递归树。
在这里插入图片描述
标记出递归树各函数执行完毕的顺序。

注意 执行完毕 与 执行的顺序 区分开来。
在这里插入图片描述
📖dfs实现指数型枚举
在这里插入图片描述
选择:在每一步选择一个可能的选项(例如,选择一个数字、字符或状态)。
对于每一个数字,我有选和不选两种方式。对于一个数字判定完后,我们需要判定下一个数字是否选和不选。
约束:在选择后,检查当前状态是否满足约束条件(例如,是否满足题目要求)。
选择后,判断当前数字选择了几位。
目标:如果当前状态满足目标条件,记录结果(即找到一个解)。
当我进入到第n+1位,代表前n位已经选择好了,则输出结果。
回溯:如果当前状态无法继续前进,撤回上一步选择,尝试其他可能的选项。
即我们完成了选择,或者该层递归代码实现完成,我们进行回溯到上一步,并恢复进入该层递归时的状态。

📚

static void dfs(int u){
	if(u>n)[
		for(int i=1;i<=n;i++){
			if(st[i]) System.out.print(i+" ");
		}
		System.out.println();
		return;
	}
	dfs(u+1);
	st[u]=true;
	dfs(u+1);
	st[u]=false;
}

在这里插入图片描述

2、二进制搜索

什么是二进制搜索?
在这里插入图片描述
在这里插入图片描述

⭐️二进制搜索

首先一个二进制数中只包含0,1,在二进制枚举中0代表不选当前元素,1代表选当前元素。
例如对于一个长度为5的集合{1,2,3,4,5},我们选择1,3,5这3个元素:
在这里插入图片描述

那么当前选择的状态就可以用二进制10101来表示,也就是十进制中的21。

在这里插入图片描述
在这里插入图片描述
什么是二进制搜索?
一种搜索所有可能组合的技巧,通常用于解决一些组合问题、子集问题或状态空间搜索问题。它利用二进制数对集合的子集进行标记,从而有效地生成所有可能的组合。比如:

  • 数字0的二进制是000,表示所有物品都不选。
  • 数字5的二进制是101,表示选第0和第2个物品,不选第1个。

这样可以枚举出所有可能的选择组合。

蛋糕的美味值

3、bfs

什么是BFS?
BFS(广度优先搜索,Breadth-First Search)是一种图或树的遍历算法。其一般用于求解边权相等的最短路以及最小操作数问题。该算法时间复杂度为O(点数+边数)

从一个节点开始,探索所有相邻的节点,然后以此向外扩展至更远的节点,逐层进行。

BFS算法我们用队列进行实现,一般情况下需要完成以下四步:

  • 初始化队列:将起始节点放入队列中。
  • 从队列中取出一个节点,访问该节点,并将其所有未访问的邻居节点加入队列。
  • 确保每个节点只被访问一次,以避免死循环(特别是在处理有环的图时)。
  • 重复上述步骤,直到队列为空。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

总体的复杂度时O(n+m),n是访问点的复杂度,m是访问边的复杂度。

BFS(graph, start):
	queue =空队列
	visited=空集合
	queue.add(start)
	visited[start]=true;#初始化
	#开始遍历
	while queue 不为空:
		#从队列中取出当前节点
		current = queue.poll()
		
		if(current=end){ #如果答案找到,直接退出函数
			return current;
		}
	 	for each neighbor in graph[current]:#处理current节点可以是打印、搜集信息等
			if neighbor 不在visited:
				queue.add(neighbor) #将邻居加入队列
				visited.add(neighbor) #标记为已访问
	return false #遍历结束,所有节点都已经访问过,均没有找到答案

📖走迷宫
在这里插入图片描述

第一个问题:能否看成一张图?将这个01迷宫看成是一张图,每个格子都能走到他上下左右四个格子,每个结点相连,最后形成一张图。

在这里插入图片描述

第二个问题,这张图是否为一张边权为1的图呢?从一个格子走到另一个格子,所花费步数是1,因此这是一张边权为1的图。
综上,启发我们用bfs解决问题。
但是我们还要解决几个问题:
1、怎么样得到当前结点的相邻结点?

在这里插入图片描述

2、怎么判断相邻的结点是合法的?

在这里插入图片描述
📚走迷宫

📖最少操作数
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
📚最少操作数

搜索基础代码模板

⭐️dfs

static void dfs(){
    if(递归终止条件){
        return;//结束递归
    }
    //更新状态进行下一次递归
    dfs();
    //回溯状态进行下一次递归
    dfs();
}

复杂度取决于递归树。

⭐️二进制搜索

复杂度为 O(2n),枚举只有两种状态(选/不选)的全部方案。

for(int i = 0 ; i <= (1<<n)-1 ; i ++){
	for(int j = 0 ; j < n; j++){ // 枚举0 ~ n-1 位
		if((i>>j)%2== 1){ // 判断i的第j 位是否是1,即是否被选择
			//demo
		}
	}
}

⭐️bfs
等权图最值问题最短路。

static void bfs(){
    Queue<自定义类型> q=new ArryaDeque<>();
    //初始化状态
    q.offer(起点);
    st[起点]=true;
    while(!q.isEmpty){
        自定义类型 t=q.poll();
        if(t==终点){
            结束bfs;
            return;
        }
        遍历当前点所能到达的其余点;
        for(int v:剩余点){
            if(!st[v]){//剩余点没访问过的入队
                q.offer(v);
                st[v]=true;
            }
        }
    }
    没有找到答案且题有需求输出-1
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值