搜索算法基础

news2024/12/24 9:46:49

一 DFS

  深度优先搜索算法(Depth First Search,简称DFS):一种用于遍历或搜索树或图的算法。 沿着树的深度遍历树的节点,尽可能深的搜索树的分支当节点v的所在边都己被探寻过或者在搜寻时结点不满足条件,搜索将回溯到发现节点v的那条边的起始节点。整个进程反复进行直到所有节点都被访问为止。属于盲目搜索,最糟糕的情况算法时间复杂度为O(!n)。
  这个算法一开始仅仅只是图还有树这种数据结构的遍历方法,但是这种方法可以用来搜索,枚举一个问题的所有解,因为求出一个问题的解的过程就是一个层次递进的过程。下面就用全排列举一个例子:
在这里插入图片描述
  全排列的所有解都可以看成一个叶子节点。全排列的第一个数字就是树的第一层,因此可见DFS递归函数都是从0开始的,这样代表搜索从这个解空间的树的根节点开始。搜到树的叶子节点的时候就回溯,回溯的同时要记得恢复现场。下面是一个全排列用DFS来解的代码:
下面是几个经典问题的代码:
(1)全排列(搜索范围是n大小的数组,策略就是每一次搜出一个数)

#include <iostream>
using namespace std;
const int N=10 ;
int path[N];
bool array[N];
int n;//这个n要设置为全局变量,因为这是判断十分搜索到叶子节点的关键
void dfs(int u)
{   int i;
	if(u==n) {//判断此时的递归是否已经搜到叶子节点
		for(i=0;i<n;i++) printf("%d",path[i]);
		printf("\n");
		return;//搜索到叶子节点的时候结束递归 
	}
	
	for(i=1;i<=n;i++) //遍历所有可能情况
	{
		if(!array[i])//满足条件就存进结果
		{
			path[u]=i;
			array[i]=true;//做DFS的时候要有两个结构,一个用来记录状态,一个用来存储搜索的结果
			dfs(u+1);//继续往下搜索 
			array[i]=false;//回溯的时候还原现场,就写在递归调用的后面
		}
	}
	
}
int main(int argc, char** argv) {
	cin>>n;
	dfs(0);//表示从根节点开始搜索
	return 0;
}

(2)八皇后问题(策略是按行来搜索,一行一行找一个合法的)

#include <iostream>
using namespace std;
const int N = 20; 

// bool数组用来判断搜索的下一个位置是否可行
// col列,dg对角线,udg反对角线
// g[N][N]用来存路径

int n;
char g[N][N];
bool col[N], dg[N], udg[N];

void dfs(int u) {
    // u == n 表示已经搜了n行,故输出这条路径
    if (u == n) {
        for (int i = 0; i < n; i ++ ) puts(g[i]);   // 等价于cout << g[i] << endl;
        puts("");  // 换行
        return;
    }

    //对n个位置按行搜索
    for (int i = 0; i < n; i ++ )
        // 剪枝(对于不满足要求的点,不再继续往下搜索)  
        // udg[n - u + i],+n是为了保证下标非负
        if (!col[i] && !dg[u + i] && !udg[n - u + i]) {
            g[u][i] = 'Q';
            col[i] = dg[u + i] = udg[n - u + i] = true;
            dfs(u + 1);
            col[i] = dg[u + i] = udg[n - u + i] = false; // 恢复现场 这步很关键
            g[u][i] = '.';

        }
}

int main() {
    cin >> n;
    for (int i = 0; i < n; i ++ )
        for (int j = 0; j < n; j ++ )
            g[i][j] = '.';

    dfs(0);

    return 0;
}   

(3)小猫爬山(策略是一只一只小猫安排,直到安排完为止)
  翰翰和达达饲养了 N 只小猫,这天,小猫们要去爬山。经历了千辛万苦,小猫们终于爬上了山顶,但是疲倦的它们再也不想徒步走下山。翰翰和达达只好花钱让它们坐索道下山。索道上的缆车最大承重量为 W,而 N 只小猫的重量分别是 C1、C2……CN。当然,每辆缆车上的小猫的重量之和不能超过 W。每租用一辆缆车,翰翰和达达就要付 1 美元,所以他们想知道,最少需要付多少美元才能把这 N 只小猫都运送下山
代码如下:

#include <algorithm>
#include <iostream>
using namespace std;
const int N = 2e1;
int cat[N];//用于存储每一只小猫的重量
int cab[N];//用于所有缆车剩余的空间
int n, w;
int ans;
bool cmp(int a, int b) {
    return a > b;
}
void dfs(int now, int cnt)//两个参数,now表示当前要分配的小猫是第now个,cnt表示目前已经分配的缆车数量
{
    if (cnt >= ans) {
      return;//这是一个剪枝的过程,因为要求的是最小的,
             //所以在搜索的过程种如果比之前搜索的ans要大直接不用搜索了
    }
    if (now == n + 1) {
        ans = min(ans, cnt);
        return;
    }
    //尝试分配到已经租用的缆车上
    for (int i = 1; i <= cnt; i++) {  //分配到已租用缆车
        if (cab[i] + cat[now] <= w) {//表示分配到可以组用的缆车上去
            cab[i] += cat[now];
            dfs(now + 1, cnt);
            cab[i] -= cat[now];  //还原
        }
    }
    // 新开一辆缆车
    cab[cnt + 1] = cat[now];
    dfs(now + 1, cnt + 1);
    cab[cnt + 1] = 0;//还原
}

int main() {
    cin >> n >> w;
    for (int i = 1; i <= n; i++) {
        cin >> cat[i];
    }

    sort(cat + 1, cat + 1 + n, cmp);

    ans = n;

    dfs(1, 0);

    cout << ans << endl;

    return 0;
}

  DFS的模板如下:

void dfs()//参数用来表示状态
{
    if(到达终止状态)//递归出口
    {
        ...//根据题意来添加
        return}

    if(越界或者不合法状态)
        return;//可以用于剪枝
    else
    {
        for(扩展方式)
        {
            if(扩展方式所达到的合法状态)
            {
                ...//根据题意来添加
                vis[i]=1;//表示已经操作过了
                dfs();
                vis[i]=0;//还原操作
            }
        }
    }
}

  for循环是用来遍历所有拓展的方式(例如全排列当中所有的数字),而后写一个if语句判断情况是否可以拓展,如果情况满足条件就进行对应的操作和下层递归调用,递归调用完之后要还原现场

     for(扩展方式)
        {
            if(扩展方式所达到的合法状态)
            {
                ...//根据题意来添加
                vis[i]=1;//标记
                修改(剪枝)
                dfs();
                vis[i]=0;
                //是否还原标记根据题意来
                //如果加上还原标记,就是回溯算法
            }
        }

  通过这3个例子我们可以看出如果要做DFS题目的话一定要确定一个“搜索空间”。例如全排列是一个大小为n的数组,n皇后问题是原有的二维数组,小猫上山就是那个小猫数组。在这个覆盖全局的搜索空间制定搜索策略来找出符合条件的情况就是DFS编程的思路,而这样的搜索过程是一定可以枚举出所有可能性的

二 BFS

  宽度优先搜索算法(又称广度优先搜索)是最简便的图的搜索算法之一,这一算法也是很多重要的图的算法的原型。其别名又叫BFS,属于一种盲目搜寻法,目的是系统地展开并检查图中的所有节点,以找寻结果。换句话说,它并不考虑结果的可能位置,彻底地搜索整张图,直到找到结果为止。BFS的搜索还有一个性质:BFS搜出来的结果一定是“最短的”
  说通俗一点,以下面一个问题为例子:
  给定一个 n×m 的二维整数数组,用来表示一个迷宫,数组中只包含 0 或 1,其中 0 表示可以走的路,1 表示不可通过的墙壁。
  最初,有一个人位于左上角 (1,1) 处,已知该人每次可以向上、下、左、右任意一个方向移动一个位置。
  请问,该人从左上角移动至右下角 (n,m) 处,至少需要移动多少次。数据保证 (1,1) 处和 (n,m) 处的数字为 0,且一定至少存在一条通路。
  输入格式
  第一行包含两个整数 n 和 m。接下来 n 行,每行包含 m 个整数(0 或 1),表示完整的二维数组迷宫。目的是输出一个整数,表示从左上角移动至右下角的最少移动次数。
输入样例:
0    1    0    0    0 0    1    0    1    0 0    0    0    0    0 0    1    1    1    0 0    0    0    1    0 0 \; 1\; 0\; 0\; 0\\ 0\; 1\; 0\; 1\; 0\\ 0\; 0\; 0\; 0\; 0\\ 0 \; 1\; 1 \; 1\; 0\\ 0 \; 0\; 0\; 1 \; 0 0100001010000000111000010
代码如下:

#include <bits/stdc++.h>
using namespace std;
typedef pair<int, int> PII;
const int N = 1e2 + 7;
int g[N][N], d[N][N];
int n, m;
int bfs() {
    int dx[] = {-1, 0, 1, 0}, dy[] = {0, 1, 0, -1};
    queue <PII> q;
    for (auto &v : d) 
        for (auto &x : v) {
            x = - 1;
        }
    d[0][0] = 0;
    q.push({0, 0});
    while (!q.empty()) {
        auto t = q.front();
        q.pop();
        for (int i = 0; i < 4; i++) {
            int x = t.first + dx[i], y = t.second + dy[i];
            if (x >= 0 && x < n && y >= 0 && y < m && g[x][y] == 0 && d[x][y] == -1) {
                d[x][y] = d[t.first][t.second]  + 1;
                q.push({x, y});
            }
        }

    }
        return d[n - 1][m - 1];
}
int main() {
    cin >> n >> m;
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < m; j++) {
            cin >> g[i][j];
        }
    }

    cout << bfs() << endl;

    return 0;
}

  从代码可以知道BFS的步骤就是:
  ①把初始状态(起点)放到队列中。
  ②把起点搜索到的结果放入队列并记录结果,而后把起点出队列。
  ③执行与②相似的操作:把stage为i-1的结果出队列,把stage为i的结果入队列。(再题目中表示为把距离原点i-1的位置出队列,把距离原点i的位置入队列)。
  ④当搜索结果达到边界条件的时候就不会再有搜索的结果入队列了,而又有结果出队列,所以当队列为空的时候循环就结束了。
  这个步骤可以使用一个循环写出来,这个while循环过程可以根据以下准则划分成若干阶段:把stage为i-1的所有结果出队列,把stage为i的所有结果入队列,并且一个结果出队列以后就有一个结果入队列。(注意是所有结果)。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/194793.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

uboot的烧写及使用

目录 一、uboot概述 Bootloader Bootloader基本功能 常见的Bootloader 二、SD卡启动盘制作 三、uboot的使用 3.1uboot模式 3.2uboot帮助命令 3.3uboot环境变量命令 3.4常用环境变量 3.5网络传输命令 3.6u-boot访问存储器命令 3.7 u-boot自启动环境变量&#xff08…

人工智能趋势——2023 年综述

随着DALLE 2 于 2022 年 4 月的宣布&#xff0c;关于2022 年初第三个 AI 冬天——或 AI 撞墙——的预言过时得很快而且效果不佳&#xff0c;随后出现了更多主要由扩散模型驱动的文本到图像应用程序&#xff0c;这是一个非常多产的领域用于计算机视觉研究及其他领域。AI 的 2022…

SpringCloud五大核心组件

Consul 等&#xff0c;提供了搭建分布式系统及微服务常用的工具&#xff0c;如配置管理、服务发现、断路器、智能路由、微代理、控制总线、一次性token、全局锁、选主、分布式会话和集群状态等&#xff0c;满足了构建微服务所需的所有解决方案。 服务发现——Netflix Eureka …

六百亿流量、25万出场费,袁树雄身价是《汉川》杨语莲125倍

在文章开始之前&#xff0c;咱们不妨先看两组数据&#xff0c;第一组数据是六百个亿&#xff0c;第二组数据则是二十五万。乍一看并没有什么特别&#xff0c;但是要把这两组数据&#xff0c;和一名音乐人联系起来的话&#xff0c;就会产生非常神奇的效果。 《早安隆回》的创作者…

[oeasy]python0070_ 字体样式_下划线_中划线_闪动效果_反相_取消效果

字体样式 回忆上次内容 m 可以改变字体样式 0-10 之间设置的都是字体效果 0 复原1 变亮2 变暗 从3到10 又是什么效果 呢&#xff1f;&#xff1f;真的可以blink闪烁吗&#xff1f;&#x1f441; 3m 3m 实现斜体字的效果 4m 4m 对应着下划线 控制范围 通过控制字符串的位置…

DNS服务解析与原理笔记

引言DNS介绍DNS原理与解析DNS查询DNS服务搭建DNS劫持和污染计算机面试 or 真题DNS应用扩展参考与推荐引言 我想很多人遇到过这样一种情况&#xff0c;电脑突然上不了网了&#xff0c;或者说可以登陆QQ&#xff0c;但是进不了网页&#xff0c;任何网页都会出现如下的类似截图&a…

Android.mk文件编写

来源&#xff1a;https://www.jianshu.com/p/9aab51f4cd6f 1. Android.mk 介绍 Android.mk 是Android 提供的一种makefile 文件&#xff0c;注意用来编译生成 exe(二进制可执行文件)、so(动态库文件)、a(静态库文件)、jar(jar包) 和 apk 等文件。Android.mk和一个或多个.c的源…

PWN入门程序装载与虚拟内存 小白笔记

1.源代码到执行源代码test.c从磁盘中&#xff0c;通过gcc编译成a.out&#xff08;可执行文件&#xff09;。执行的时候&#xff0c;是将磁盘中的可执行文件&#xff08;a.out&#xff09;映像到内存中。2.节视图和段视图节视图&#xff08;磁盘中的可执行文件&#xff09;不同的…

采用消息中间件实现最终一致性的分布式事务

基于可靠消息服务的方案是通过消息中间件保证上下游应用数据操作的一致性。假设有A和B两个系统&#xff0c;分别可以处理任务A和任务B。此时存在一个业务流程&#xff0c;需要将任务A和任务B在同一个事务中处理。就可以使用消息中间件来实现这种分布式事务。 第一步&#xff…

一篇文章带你了解KendoReact DateRangePicker,让日期选择变得更酷炫!

Kendo UI致力于新的开发&#xff0c;来满足不断变化的需求。现在我们非常自豪地宣布&#xff0c;通过React框架的Kendo UI JavaScript封装来支持React Javascript框架。KendoReact能够为客户提供更好的用户体验&#xff0c;并且能够更快地构建更好的应用程序。理想情况下&#…

python中有哪些运算符,python里的运算符号

大家好&#xff0c;给大家分享一下python中有哪些运算符&#xff0c;很多人还不知道这一点。下面详细解释一下。现在让我们来看看&#xff01; 1、Python中的算术运算符有哪些呢&#xff1f; 1. 比较运算符&#xff1a; 如果比较式成立&#xff0c;返回True&#xff1b;不成立…

[Linux系列]linux bond详解

目录 背景 简介 bond分类 1. mode0(balance-rr) 2. mode1 (active-backup) 3. mode2&#xff08;balance-xor&#xff09; 4. mode3&#xff08;broadcast&#xff09; 5. mode4&#xff08;802.3ad&#xff09; 6. mode5&#xff08;balance-tlb&#xff09;…

STL模拟实现——stack、queue和priority_queue(适配器、仿函数、反向迭代器)

适配器 适配器是一种设计模式(设计模式是一套被反复使用的、多数人知晓的、经过分类编目的、代码设计经验的总 结)&#xff0c;该种模式是将一个类的接口转换成客户希望的另外一个接口。下列的这些stack、queue和priority_queue以及反向迭代器都是适配器。 stack介绍 1. sta…

数据结构-第八期——树状数组-原理区间和

目录 1、树状数组 2、基本应用 2.1、前缀和&#xff1a;不修改&#xff0c;只查询 2.2、树状数组&#xff1a;动态修改&#xff0c;求区间和 3、实现数组树状 3.1、神奇的lowbit (x)操作 3.2、tree[ ]数组 3.3、基于tree[ ]的计算 3.4 sum[]的计算 3.5、tree[]的更新…

CD3药物研发进展-销售数据-上市药品前景分析

CD3作为近几年的双抗热门靶点之一&#xff0c;目前在全球登记的CD3相关药物就有470个&#xff0c;上市的CD3药物有8款&#xff0c;Ⅰ期临床到Ⅲ期临床有116种&#xff0c;临床前及药物发现达200多种&#xff0c;其火热程度可想而知&#xff0c;笔者为方便大家更加清晰了解CD3在…

ESP-IDF:冒泡排序和选择排序测试代码

ESP-IDF:冒泡排序和选择排序测试代码 /冒泡排序/ void printarry16 (int arr[],int length) { for(int i0;i<length;i) { cout<<arr[i]<<" “; } cout<<endl; } void test16() { int arr[] {9, 8, 7, 6, 5, 4, 3, 2, 1, 0,9, 8, 7, 6, 5, 4, 3, 2…

【笔记】容器基础-容器与虚拟机

杂记&#xff1a; 容器技术的兴起源于 PaaS 技术的普及Docker 公司发布的 Docker 项目具有里程碑式的意义Docker 项目通过“容器镜像”&#xff0c;解决了应用打包这个根本性难题容器本身没有价值&#xff0c;有价值的是“容器编排”进程&#xff1a; 静态表现&#xff1a;程序…

Java NIO同步非阻塞编程原理解析及案例

Java NIO同步非阻塞编程原理解析及案例 文章目录Java NIO同步非阻塞编程原理解析及案例NIO介绍NIO和 BIO的比较NIO 三大核心原理示意图缓冲区(Buffer)基本介绍Buffer常用API介绍Buffer 类及其子类缓冲区对象创建缓冲区对象添加数据缓冲区对象读取数据通道(Channel)基本介绍Chan…

生物医药校招这么难,怎么锁定高端人才?

“每年招聘压力大&#xff0c;进校太难。”“各个事业部人才选用要求高&#xff0c;很难达到要求。”“企业、事业单位、高校三方‘围剿’&#xff0c;人才竞争实在太大&#xff01;”……人才招聘内卷&#xff0c;在生物医药行业可以说是体现得淋漓尽致。生物医药岗位及学历要…

【小白课程】以openKylin看图软件为例,浅谈图片编解码库—FreeImage

看图软件是openKylin操作系统上一款开源的图像查看软件&#xff0c;支持对图片进行基本操作,如&#xff1a;缩放、翻转、详情查看、复制、打印、重命名等&#xff0c;同时还可以对图片进行裁剪、存储、标注和ocr&#xff08;文字识别&#xff09;。 图1 看图软件界面 作为图像…