搜索详解

搜索

一.dfs和bfs简介

深度优先遍历(dfs)

本质:

遍历每一个点。

遍历流程:

从起点开始,在其一条分支上一条路走到黑,走不通了就往回走,只要当前有分支就继续往下走,直到将所有的点遍历一遍。

剪枝:

如果已经确定这条路没有我们想要的答案,那么就不用继续在这条路上走下去了,于是我们就开始走其他的分支或者往回走,这样节省时间的方法称之为剪枝。

回溯:

当我们一条路走到头,往回走时,就叫做回溯。

恢复现场:

当我们回溯的时候,原来这个图是什么样的,我们还要变回什么样。这样做的目的: 当我们遍历完这条分支,去遍历下一条分支的时候,我们需要保证当前图其他条件的一致性,也就是遍历每一条分支的时候,当前图的状态都是一样的。保证遍历每一条分支的时候都是公平的。

广度优先遍历(bfs)

遍历流程: 逐层逐层的遍历,先遍历第一层,再遍历第二层…,也就是遍历当前节点所能到达的所有子节点。直到遍历所有的点。不存在剪枝,回溯和恢复现场的操作。

对比dfs和bfs

时间复杂度:

dfs: 因为我们需要枚举每一个点,以及每一条边,所示它的时间复杂度为O(n + e) 即点的个数+边的个数

bfs:跟dfs时间复杂度一样,都为O(n + e) 不同的是对每个点的访问顺序是不一样的

用到的数据结构

dfs: stack

bfs: queue

空间复杂度

dfs: O(h) h为树的深度

bfs: O(2^h)

特性

dfs: 不具有最短性

bfs: 具有最短性

二. 树与图的深度优先遍历(dfs)

树与图的深度优先遍历:
树其实也是图的一种
图: 分为有向图和无向图
图的储存:

第一种:邻接矩阵,就是一个二维数组,缺点:当点和边特别多的时候,存不下,一般用的比较少,而且非常浪费空间
第二种:邻接表:由n个单链表组成,也可以用vector动态数组来实现,但vector有很大的缺点,当点和边非常大时,用vector动态数组的方法很容易超时,所以我们常用n个但链表的方式来存储图

邻接表如何存图呢:
假设有这样一个图:
在这里插入图片描述
那么我们可以给每个节点开一个单链表,如下图所示:
在这里插入图片描述
这样我们就把图用邻接表的方法存了下来
树与图深度优先遍历的大致流程:一条路走到黑,直到撞到南墙,走不通了,然后往回走,只要有分支就继续往下走

1.树与图的遍历模板:

邻接表以h数组为表头,使用 e 和 ne 数组分别存储边的终点和下一个节点

#include<iostream>
#include<cstring>
using namespace std;
const int N  = 1e6 + 10;
int h[N], e[N], ne[N], idx, n;//这里跟单链表一样,只不过这里是N个头节点,H[N]
bool vis[N];  //判断是否遍历过
void add(int a, int b)  //邻接表存树与图
{e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}
void dfs(int cur)
{vis[cur] = true;for(int i = h[cur]; i != -1; i = ne[i]){  //遍历树int u = e[i];if(!vis[u]){dfs(u);}}
}
int main()
{int a, b;cin >> n;//初始化memset(h, -1, sizeof h);memset(vis, false, sizeof vis);for(int i = 0; i < n; i++){cin >> a >> b;//建树,双向图add(a, b);add(b, a);}dfs(1);return 0;
}

这样我们就遍历了每个点。

2.树的dfs序

​ 一般来讲,我们在对树进行深度优先遍历时,对于每个节点,在刚进入递归以后以及即将回溯前各记录一次该点的编号,最后产生的长度为2N的节点序列被称为树的dfs序

void dfs(int x)
{a[++m] = x;v[x] = 1;for(int i = h[x]; i != -1; i = ne[i]){int y = e[i];if(v[y])continue;dfs(y);}a[++m] = x;
}

3.树的深度

​ 我们已知根节点的深度为0.若节点x的深度为d[x],则它的子节点的深度为d[y] = d[x] + 1

void dfs(int x)
{v[x] = 1;for(int i = h[x]; i != -1; i = ne[i]){int y = e[i];if(v[y])continue;d[y] = d[x] + 1;dfs(y);}
}

4.连通图的划分

​ 假设从x点开始一次遍历,就会访问x能够到达的所有的点和边,因此,通过多次深度优先遍历,可以划分出一张无向图中的各个连通图。同理,对一个森林进行深度优先遍历,可以划分森林中每棵树

​ cnt表示无向图包含的连通块的个数, v数组标记了每一个点属于哪个连通块

void dfs(int x)
{v[x] = cnt;for(int i = h[x]; i != -1; i = ne[i]){int y = e[i];if(v[y])continue;dfs(y);}
}
for(int i = 1; i <= n; i++){if(!v[i]){cnt++;dfs(i);}
}

三.树与图的广度优先遍历

​ 树与图的广度优先遍历需要使用一个队列来实现。起初,队列中仅包含一个起点。在广度优先遍历过程中,我们不断从队头取出一个节点x,对于x面对的多条分支,将所有x能够达到的下一个节点插入队尾,重复执行上述过程直到队列为空

1.广度优先遍历模板

void dfs()
{memset(d, 0, sizeof d);queue<int> q;q.push(1);d[1] = 1;  //d数组表示节点的深度while(q.size()){    //只要队列不为空int x = q.front();  //取出队头q.pop();for(int i = h[x]; i != -1; i = ne[i]){  //遍历x能够到达的所有下一个节点int y = e[i];if(d[y])continue;  d[y] = d[x] + 1;    //深度+1q.push(y);}}
}

​ 在上面的代码中,d数组表示从起点 1 走到点 x 需要经过的最少点数. 广度优先遍历是一种按照层次顺序进行访问的方法, 它具有如下俩个重要的性质:
1.在访问完所有的第 i 层节点后,才会开始访问第 i + 1 层节点

2.广度优先遍历队列中的元素关于层次满足俩段性和单调性, 即队列中至多包含俩个层次的节点, 其中一部分属于第 i 层, 一部分属于 i + 1 层,并且所有的第 i 层节点都排在第 i + 1 层节点之前

2.拓扑排序

​ 给定你一个无向图,若一个由图中所有点构成的序列A满足:对于图中的每条边 (x, y),x 在A中都出现在y之前,则称A是该有向图的一个拓扑排序

入度: 在有向图中,以节点 x 为终点的有向边的条数被称为 x 的入度

出度: 在有向图中,以节点 x 为起点的有向边的条数被称为 x 的出度

​ 拓扑排序非常简单,我们只需要不断选择图中入度为0的节点 x , 然后把 x 连向的点的入度减1,我们可以结合广度优先遍历的框架来实现:

​ 1.建立空的拓扑排序A。

​ 2.预处理出所有点的入度deg[i],起初把所有入度为0的点入队

​ 3.取出队头节点x,把x加入拓扑序列A的末尾

​ 4.对于x出发的每条边(x, y)把 deg[y] 减 1 。若被减为0, 则把y入队

​ 5.重复3~4过程,直到队列为空,我们便求出了拓扑序列A

void add(int x, int y)  //建边
{e[cnt] = y, ne[cnt] = h[x], h[x] = cnt++;
}
void topsort()
{queue<int> q;for(int i = 1; i <= n; i++)if(deg[i] == 0)q.push(i);  //将入度为0的点加入到队列中while(q.size()){int x = q.front();q.pop();a[++t] = x;  //将x加入到拓扑序中for(int i = h[x]; i != -1; i = ne[i]){int y = e[i];deg[y]--;  //入度--if(deg[y] == 0)q.push(y);//如果入度为0,添加到队列中去}}
}
int main()
{cin >> n >> m;for(int i = 1; i <= m; i++){int x, y;cin >> x >> y;add(x, y);}topsort();for(int i = 1; i <= n; i++)//输出拓扑序printf("%d ", a[i]);cout << endl;
}

四.迭代加深

​ 深度优先搜索每次选定一个分支,不断深入,直至到达递归边界才回溯。这种策略带有一定的缺陷。如果搜索树每个节点的分枝数非常多, 而答案在某个较浅的节点上。如果深搜在一开始选错了分支,就很可能在不包含答案的深层子树上浪费太多的时间

​ 此时,我们可以从小到大限制搜索的深度如果在当前深度限制下找不到答案,就把深度限制增加,重新进行一次搜索,这就是迭代加深的思想。

例题:加成序列(poj2248)

​ 满足如下条件的序列X(序列中元素被标号为1、2、3…m)被称为“加成序列”:

​ 1、X[1]=1

​ 2、X[m]=n

​ 3、X[1]<X[2]<…<X[m-1]<X[m]

​ 4、对于每个 k(2 ≤ k ≤ m)都存在两个整数 i 和 j (1 ≤ i,j ≤ k−1,i 和 j 可相等),使得 X[k]=X[i]+X[j]。

​ 你的任务是:给定一个整数n,找出符合上述条件的长度m最小的“加成序列”。

​ 如果有多个满足要求的答案,只需要找出任意一个可行解。

搜索框架:依次搜索序列中的每个位置k, 枚举 i 和 j 作为分支,把 X[i] 和 X[j] 的和填到 X[k] 上,然后递归填写下一个位置。

​ 加入以下剪枝:

​ 1.优化搜索顺序:为了让序列中的数尽快逼近n,在枚举 i 和 j 时从大到小枚举

​ 2.排除等效冗余

​ 对于不同的 i 和 j ,X[i] 和 X[j] 可能是相等的,我们可以在枚举是用一个 bool 类型的数组对 X[i] 和 X[j] 进行判重,避免重复搜索同一个和

​ 3.我们可以采用迭代加深的方法进行搜索, 从1开始限制搜索深度,若搜索失败就增加深度限制重新搜索,直到找到一组解时即可输出答案

#include<iostream>
#include<cstring>
using namespace std;const int N = 110;
int a[N];
bool vis[N];
int n, k;
bool dfs(int u, int k)
{//如果达到搜索限制,判断a[u - 1]是否等于nif(u == k)return a[u - 1] == n;//遍历前边的元素for(int i = u - 1; i >= 0; i--){for(int j = i; j >= 0; j--){int s = a[i] + a[j];//如果和已经出现过,或者不满足要求,剪枝掉if(vis[s] || s > n || s <= a[u - 1])continue;a[u] = s;if(dfs(u + 1, k))return true;}}return false;
}
int main()
{a[0] = 1;while(cin >> n && n){int k = 1;while(!dfs(1, k)){ //不断增加搜索限制k直到得到正确的答案memset(vis, false, sizeof vis);k++;}for(int i = 0; i < k; i++)cout << a[i] << ' ';cout << endl;}return 0;
}

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

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

相关文章

特征工程总结

目录1 特征工程是什么&#xff1f; 2 数据预处理   2.1 无量纲化     2.1.1 标准化     2.1.2 区间缩放法     2.1.3 标准化与归一化的区别   2.2 对定量特征二值化   2.3 对定性特征哑编码   2.4 缺失值计算   2.5 数据变换 3 特征选择   3.1 Filter …

Jmeter测试并发https请求成功了

Jmeter2.4 如何测试多个并发https请求&#xff0c;终于成功了借此机会分享给大家 首先要安装jmeter2.4版本的&#xff0c;而且不建议大家使用badboy&#xff0c;因为这存在兼容性问题。对于安装&#xff0c;我就不讲了&#xff0c;我就说说如何测试https&#xff0c;想必大家都…

关系数据库——sql基础1定义

关系数据库标准语言SQL 基本概念 SQL语言是一个功能极强的关系数据库语言。同时也是一种介于关系代数与关系演算之间的结构化查询语言&#xff08;Structured Query Language&#xff09;&#xff0c;其功能包括数据定义、数据查询、数据操纵和数据控制。 SQL的特点&#xff…

大数据学习(09)--Hadoop2.0介绍

文章目录目录1.Hadoop的发展与优化1.1 Hadoop1.0 的不足与局限1.2 Hadoop2.0 的改进与提升2.HDFS2.0 的新特性2.1 HDFS HA2.2 HDFS Federation3. 新一代的资源管理器YARN3.1 MapReduce1.0 缺陷3.2 YARN的设计思路3.3 YARN 体系结构3.4 YARN工作流程3.5 YARN框架与MapReduce1.0框…

Java多线程常用方法

start()与run() start() 启动线程并执行相应的run()方法 run() 子线程要执行的代码放入run()方法 getName()和setName() getName() 获取此线程的名字 setName() 设置此线程的名字 isAlive() 是判断当前线程是否处于活动状态。活动状态就是已经启动尚未终止。 curren…

MachineLearning(2)-图像分类常用数据集

图像分类常用数据集1 CIFAR-102.MNIST3.STL_104.Imagenet5.L-Sun6.caltech-101在训练神经网络进行图像识别分类时&#xff0c;常会用到一些通用的数据集合。利用这些数据集合可以对比不同模型的性能差异。下文整理常用的图片数据集合&#xff08;持续更新中)。基本信息对比表格…

大数据学习(09)--spark学习

文章目录目录1.spark介绍1.1 spark介绍1.2 scale介绍1.3 spark和Hadoop比较2.spark生态系统3.spark运行框架3.1 基本概念3.2 架构的设计3.3 spark运行基本流程3.4 spark运行原理3.5 RDD运行原理3.5.1 设计背景3.5.2 RDD概念和特性3.5.3 RDD之间的依赖关系3.5.4 stage的划分3.5.…

机器学习中的聚类方法总结

聚类定义 定义 聚类就是对大量未知标注 的数据集&#xff0c;按数据 的内在相似性将数据集划分为多个类别&#xff0c;使 类别内的数据相似度较大而类别间的数据相 似度较小。是无监督的分类方式。 聚类思想 给定一个有N个对象的数据集&#xff0c;构造数据的k 个簇&#x…

关系数据库——关系数据语言

关系 域&#xff1a;一组具有相同数据类型的值的集合&#xff08;即取值范围&#xff09; 笛卡尔积&#xff1a;域上的一种集合运算。结果为一个集合&#xff0c;集合的每一个元素是一个元组&#xff0c;元组的每一个分量来自不同的域。 基数&#xff1a;一个域允许的不同取值…

机器学习问题总结(01)

文章目录1.请描述推荐系统中协同过滤算法CF的原理2.请描述决策树的原理、过程、终止条件&#xff0c;以及如何防止过拟合2.1决策树生成算法2.2 剪枝处理&#xff08;防止过拟合&#xff09;2.3 停止条件2.4 棵决策树的生成过程2.5 决策树的损失函数3.请描述K-means的原理&#…

Python实例讲解 -- 解析xml

Xml代码 <?xml version"1.0" encoding"utf-8"?> <info> <intro>信息</intro> <list id001> <head>auto_userone</head> <name>Jordy</name> <number&g…

python(22)--面向对象1-封装

python面向对象1面向过程/面向对象2面向对象核心概念-类3类的设计3.1类三要素-类名、属性、方法3.2面向对象基础语法3.2.1查看对象的常用方法3.2.2类定义3.2.3创建类对象3.2.4__init__()方法3.2.5 self参数3.2.6类内置方法和属性_del_()方法--销毁对象_str_()方法--定制化输出对…

机器学习问题总结(02)

文章目录1.stacking模型以及做模型融合的知识1.1 从提交结果中融合1.2 stacking1.3 blending2. 怎样去优化SVM算法模型的&#xff1f;2.1 SMO优化算法2.2 libsvm 和 Liblinear3.现有底层是tensorflow的keras框架&#xff0c;如果现在有一个tensorflow训练好的模型&#xff0c;k…

C/C++常见面试题(四)

C/C面试题集合四 目录 1、什么是C中的类&#xff1f;如何定义和实例化一个类&#xff1f; 2、请解释C中的继承和多态性。 3、什么是虚函数&#xff1f;为什么在基类中使用虚函数&#xff1f; 4、解释封装、继承和多态的概念&#xff0c;并提供相应的代码示例 5、如何处理内…

机器学习问题总结(03)

文章目录1.struct和class区别&#xff0c;你更倾向用哪个2.kNN&#xff0c;朴素贝叶斯&#xff0c;SVM的优缺点&#xff0c;各种算法优缺点2.1 KNN算法2.2 朴素贝叶斯2.3SVM算法2.4 ANN算法2.5 DT算法3. 10亿个整数&#xff0c;1G内存&#xff0c;O(n)算法&#xff0c;统计只出…

redis——新版复制

sync虽然解决了数据同步问题&#xff0c;但是在数据量比较大情况下&#xff0c;从库断线从来依然采用全量复制机制&#xff0c;无论是从数据恢复、宽带占用来说&#xff0c;sync所带来的问题还是很多的。于是redis从2.8开始&#xff0c;引入新的命令psync。 psync有两种模式&a…

Python(23)-面向对象2-继承,多态

面向对象基本概念2--继承、多态1.继承基本概念2.子类重写父类方法2.1完全重写2.2扩展父类方法--super()3.多继承4.新式类和旧式类5.多态基本概念6.类属性、类方法-classmethod6.1类属性6.2类方法classmethod7.静态方法staticmethod8.案例分析本系列博文来自学习《Python基础视频…

机器学习问题总结(04)

文章目录1、MLP的BP过程2、maxpool层BP怎么做的2.1 **mean pooling**2.2 max pooling3、opencv遍历像素的方式&#xff0c;讲两种&#xff1f;4、传统图像处理有了解过吗&#xff0c;比如去噪 特征提取5、问在linux下写过代码吗&#xff1f; 问用了什么软件工具6、LDA&#xff…

持续更新的Zookeeper知识总结

简介 Zookeeper为分布式应用 提供了高效且可靠的分布式协调服务&#xff0c;提供了诸如统一命名服务、发布订阅、负载均衡、配置管理和分布式锁等分布式的基础服务。 设计目标是将那些复杂且容易出错的分布式一致性服务封装起来&#xff0c;构成一个高效可靠的原语集&#xf…

机器学习问题总结(05)

文章目录1. Hadoop、Spark1.1 hadoop1.2 spark1.3 MapReduce1.3.1 概念1.3.1 MapReduce执行流程2、机器学习场景3、推荐系统&#xff08;预测电影等级&#xff09;4、CTR&#xff08;点击通过率 -> 广告&#xff09;5、SVM5.1 svm的原理5.2 SVM的核技巧6、K-means6.1 K-mean…