目录
图与树的转化
在树的介绍中我们曾讲到,任何一个数据结构,我们都可以把它转化成类似树的形式。
例如一维数组或者线性表链表,转化成树的形式,就叫做线段树或者叫树状数组(一种算法)
那么图,也可以转化为类似树的形式。
深搜
概念
深度优先搜索是一种在开发爬虫早期使用较多的方法。它的目的是要达到被搜索结构的叶结点(即那些不包含任何超链的HTML文件) 。在一个HTML文件中,当一个超链被选择后,被链接的HTML文件将执行深度优先搜索,即在搜索其余的超链结果之前必须先完整地搜索单独的一条链。深度优先搜索沿着HTML文件上的超链走到不能再深入为止,然后返回到某一个HTML文件,再继续选择该HTML文件中的其他超链。当不再有其他超链可选择时,说明搜索已经结束。
深搜,全称深度优先搜索,英文简称(dfs),其核心宗旨就是:不撞南墙不回头,不到死路绝不后退。 也就是沿着一条路,一直搜下去,直到尽头才返回。
代码实现
深搜有点类似于二叉树的先序遍历,但又不只是先序遍历,准确来说是先序中序后序的综合体。
深搜的核心思想是回溯法。回溯法介绍
回溯法模板:
void Backtrack(原参数区间/或者参数) {
if(达到目的/或者撞到“南墙”) {
存放结果;//或者输出结果,输出最好调用新函数
return;
}
进行一些操作(根据实际情况可省去);
for(int i=1;i<=子区间个数(换句话说,也就是父亲节点的度);i++) {//或者子参数个数
做记录;//已经搜过就不再搜了
Backtrack(子区间)//或者下个搜索的参数);//调用子区间或者参数
销毁记录;//搜完,或者撞到南墙,就销毁记录
}
}
但是请注意,不要把深搜和回溯划等号喔!!!深搜只是用到了回溯这种思想,运用回溯这种思想的也不止深搜,同样,深搜也不止用了回溯这一种思想。
深搜是一种偏暴力的算法了(深搜很容易TLE)
那么,图的深搜套用的回溯模板也就是
void Backtrack(当前节点) {
if(达到目的/或者撞到“南墙”) {
存放结果;//或者输出结果,输出最好调用新函数
return;
}
进行一些操作(根据实际情况可省去);
for(int i=1;i<=当前节点所连接的节点个数;i++) {
记录该节点;//下次就不搜这个节点了
Backtrack(下个节点);
销毁记录;//搜完,或者撞到南墙,就销毁记录
}
}
一般来说,记录节点用bool定义一个叫做vis的数组。
bool vis[10005];//打一个bool类型的vis数组
//vis[1]=0,表示节点1没被记录
//vis[3]=1,表示节点3已经被记录
图的节点,我们采用STL的储存方式(当然,二维数组也可以,STL是便于计数,更方便)
vector<int>G[10005];
//G[i][sum]=j 表示节点i与节点j有一条边相连,并且它是第sum+1条边(下标默认从0开始)
//例如G[1][0]=3 表示节点1与节点3有一条边相连,并且它是第1条边(下标默认从0开始)
//例如G[3][1]=2 表示节点3与节点2有一条边相连,并且它是第2条边(下标默认从0开始)
//例如G[1][2]=2 表示节点1与节点2有一条边相连,并且它是第3条边(下标默认从0开始)
//例如G[2][2]=4 表示节点2与节点4有一条边相连,并且它是第3条边(下标默认从0开始)
所以深搜套用下来也就是:
vector<int>G[N];
bool vis[N];
void Backtrack(int key) {
if(终止条件) {//根据题目要求,寻找终止条件
xxxx;
return;
}
for(int i=0;i<G[key].size();i++) {
//当该点没被搜过
if(!vis[G[key][i]]) {
vis[G[key][i]]=1;//做记录
Backtrack(G[key][i]);//搜索下个节点
vis[G[key][i]]=0;//搜完销毁记录
}
}
}
int main() {
前置代码;
vis[key]=1;
Backtrack(key);
return 0;
}
广搜
概念
宽度优先搜索算法(又称广度优先搜索)是最简便的图的搜索算法之一,这一算法也是很多重要的图的算法的原型。Dijkstra单源最短路径算法和Prim最小生成树算法都采用了和宽度优先搜索类似的思想。其别名又叫BFS,属于一种盲目搜寻法,目的是系统地展开并检查图中的所有节点,以找寻结果。换句话说,它并不考虑结果的可能位置,彻底地搜索整张图,直到找到结果为止。
广搜,全称广度优先搜索,又叫宽度优先搜索,英文简称(bfs),它就好比我们拓展城市,总是按照周围一圈一圈的拓展。也可以理解为往水中丢石头,荡起的波纹总是由内向外,一圈一圈从小到大。
代码实现
广搜有点类似于二叉树的层序遍历。所以代码实现也与层序遍历类似,均是打一个队列储存节点。
queue<int>ans;
ans.push(第一个节点);
vis[第一个节点]=1;
while(!ans.empty()) {
cout<<ans.front();
for(int i=0;i<G[ans.front()].size();i++) {
//当某节点没被搜过
if(vis[G[ans.front()][i]]==0) {
ans.push(G[ans.front()][i]);
vis[G[ans.front()][i]]=1;
}
}
ans.pop();
}
理论练手项目: