Leetcode046全排列(不含重复元素)-----------回溯法简单应用
Acwing843(n-皇后问题)--------DFS(全排列)
Acwing846树的重心---------dfs(邻接表)
Acwing844走迷宫(最短路问题)----------(bfs)
深入研究DFS算法:
转载:
代码模板:
静态链表:
深度优先搜索DFS
#include <iostream>
#inclue <cstring>
#include <algorithm>
using namespace std;
const int N=10010,M=N*2;
//因为有N个头节点所以定义N个数组,单链表可以直接int h
int h[N],e[M],ne[M],idx;
bool stu[N];
void add(int a,int b){
//插入操作即在h[a]头结点后插入节点b
e[idx]=b;
ne[idx]=h[a];
h[a]=idx++;
}
//可以自定义从哪个节点开始搜索
int dfs(int u)
{
st[u] = true; // st[u] 表示点u已经被遍历过
for (int i = h[u]; i != -1; i = ne[i])
{
//当前点对应在图里的值
int j = e[i];
if (!st[j]) dfs(j);
}
}
int main(){
//初始化让所有的头结点指向空节点(下标为-1)
memset(h,-1,sizeof h);
dfs(1);
}
宽度优先搜索BFS
#include <iostream>
#inclue <cstring>
#include <algorithm>
using namespace std;
const int N=10010,M=N*2;
//因为有N个头节点所以定义N个数组,单链表可以直接int h
int h[N],e[M],ne[M],idx;
bool stu[N];
queue<int> q;
void add(int a,int b){
//插入操作即在h[a]头结点后插入节点b
e[idx]=b;
ne[idx]=h[a];
h[a]=idx++;
}
void bfs(){
st[1] = true; // 表示1号点已经被遍历过
q.push(1);
while (q.size())
{
int t = q.front();
q.pop();
for (int i = h[t]; i != -1; i = ne[i])
{
int j = e[i];
if (!st[j])
{
st[j] = true; // 表示点j已经被遍历过
q.push(j);
}
}
}
}
int main(){
//初始化让所有的头结点指向空节点(下标为-1)
memset(h,-1,sizeof h);
bfs(1);
}
王道:
/*边表节点*/
typedef struct ArcNode{
int adjvex;//弧指向的节点下标
struct ArcNode *next; //指向下一条弧的指针
}ArcNode;
/*顶点表节点*/
typedef char vertexType ;
typedef struct VNode {
vertexType data;
ArcNode* first;//指向一个边表节点
}VNode,AdjList[100];//顶点数组
/*图*/
typedef struct {
AdjList vertices;
int vecnum, arcnum;
}ALGraph;
vector<bool> visited(100,false);
void DFS_DG(ALGraph G, int i) {
cout << G.vertices[i].data << " ";
visited[i] = true;
for (ArcNode* p = G.vertices[i].first; p; p = p->next) {
//p->adjvex弧指向的节点下标
if (!visited[p->adjvex])DFS_DG(G, p->adjvex);
}
}
void DFS(ALGraph G, int i) {
stack<int> s;//只要进栈就要修改标记,保证所有的节点只有一次机会入栈,这样遍历节点才不会重复
int v;
s.push(i);
visited[i] = true;
while (!s.empty())
{
v = s.top();
s.pop();
cout << G.vertices[v].data << " ";//出栈时访问
for (ArcNode* p = G.vertices[v].first; p; p = p->next) {
if (!visited[p->adjvex]) {
s.push(p->adjvex);//栈中存放的是还未访问的
visited[p->adjvex] = true;
}
}
}
}
void BFS(ALGraph G, int i) {
queue<int> q;
cout << G.vertices[i].data << " ";
q.push(i);
visited[i] = true;
int v;
while (!q.empty())
{
v = q.front();
q.pop();
for (ArcNode* p = G.vertices[v].first; p; p = p->next) {
if (!visited[p->adjvex]) {
cout << G.vertices[p->adjvex].data << " ";
visited[p->adjvex] = true;
q.push(p->adjvex);//队列中存放的是已经访问过的
}
}
}
}
void Traverse() {
int n, m;
cout << "请输入有向图顶点数和边数" << endl;
cin >> n >> m;
ALGraph G;
create(&G, n, m);
cout << "递归深度搜索:" << endl;
for (int i= 0; i < G.vecnum; i++) {//如果没有这个给循环,非连通的图就只能深度遍历一个连通分量
if (!visited[i]) DFS_DG(G, i);
}
for (int i = 0; i < G.vecnum; i++) visited[i] = false;//将全局数组修改为原来的样子
cout << endl;
cout << "非递归深度搜索:" << endl;
for (int i = 0; i < G.vecnum; i++) {
if (!visited[i]) DFS(G, i);
}
for (int i = 0; i < G.vecnum; i++) visited[i] = false;//将全局数组修改为原来的样子
cout << endl;
cout << "广度搜索:" << endl;
for (int i = 0; i < G.vecnum; i++) {
if (!visited[i]) BFS(G, i);
}
}
int main()
{
Traverse();
}
例题 :
Leetcode046全排列(不含重复元素)-----------回溯法简单应用
描述:
代码:
#include <iostream>
using namespace std;
int n;
//1<=n<=7
const int N=8;
bool exist[N];
int temp[N];
//len代表在第几层
void dfs(int len){
if(len==n){
//依次输出每层上的数
for(int i=0;i<n;i++) cout<<temp[i]<<" ";
cout<<endl;
return;
}
for(int i=1;i<=n;i++){
if(!exist[i]){
exist[i]=true;
temp[len]=i;
dfs(len+1);
temp[len]=0;
exist[i]=false;
}
}
}
int main(){
cin>>n;
dfs(0);
return 0;
}
题解:
https://blog.csdn.net/qq_52934831/article/details/119570812
Acwing843(n-皇后问题)--------DFS(全排列)
描述:
代码:
#include <iostream>
using namespace std;
int n;
//1<=n<=9
const int N=10;
char qi[N][N];
//分别记录的是该位置的列,正对角线,斜对角线上是否放过皇后,若均没有皇后,填入皇后,并递归到下一行
//为什么正对角和反对角要2*N,因为列只有N条,而对角线有2*N条
bool lie[N],z[2*N],f[2*N];
//当前枚举的是第x行的Q应该放在第y列?
void dfs(int x){
//此时Q已经全部放完
if(x==n){
for(int i = 0; i < n; i++)
{
for(int j = 0; j < n; j++)
cout << qi[i][j];
cout << endl;
}
cout << endl;
return;
}
//y表示第y列
for(int y=0;y<n;y++){
//x-y+n和x+y怎么得来的见图解
if(!lie[y]&&!z[x-y+n]&&!f[x+y]){
lie[y]=z[x-y+n]=f[x+y]=true;
//要是它的同一条列,正对角和反对角都没有皇后,那么就在第x行第y列放上皇后
qi[x][y]='Q';
//递归去求下一行
dfs(x+1);
//回溯
lie[y]=z[x-y+n]=f[x+y]=false;
qi[x][y]='.';
}
}
}
int main(){
cin>>n;
//初始化棋盘
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
qi[i][j]='.';
}
}
dfs(0);
return 0;
}
题解:
Acwing846树的重心---------dfs(邻接表)
描述:
代码:
#include <cstring>
#include <iostream>
#include <climits>
using namespace std;
//首先写出dfs的模板
const int N=1e5+10;
//因为是无向图,所以每个节点至多对应2n-2条边
const int M=N*2;
//节点个数
int n;
int h[N],e[M],ne[M],idx;
bool stu[N];
//ans存储的是将重心删除后剩余各个连通块中点数的最大值
//根据重心的定义,将中心删除后,剩余各个连通块中点数的最大值最小,所以ans初始值要尽可能取大
int ans=INT_MAX;
void add(int a,int b){
e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}
//返回的是以u为根节点的子树大小
int dfs(int u){
stu[u]=true;
//sum记录当前以u为根的子树大小,res记录删除重心后各个联通块的最大值
int sum=1,res=0;
for(int i=h[u];i!=-1;i=ne[i]){
int j=e[i];
if(!stu[j]){
int s=dfs(j);//以j为根的子树大小
sum+=s;//将该子树添加到sum中,来计算出以u为根的子树完整大小
//以j为根的子树是以u为根的删除u后的联通块,所以要与res进行比较取出最大值
res=max(res,s);
}//if
}//for
//n-sum表示的是减掉u为根的子树,整个树剩下的点的数量
res=max(res,n-sum);
ans=min(res,ans);
return sum;
}
int main(){
//首先初始化使节点都指向空
memset(h,-1,sizeof h);
cin>>n;//由题得,边数=n-1;
for(int i=0;i<n-1;i++){
int a,b;
cin>>a>>b;
//因为是无向图,所以两个方向都要加
add(a,b),add(b,a);
}
dfs(1);//节点可任意选
cout<<ans;
}
题解:
Acwing844走迷宫(最短路问题)----------(bfs)
描述:
代码:
#include <iostream>
#include <cstring>
//因为bfs需要借助queue,所以加上头文件
#include <queue>
using namespace std;
int n,m;
const int N=1e2;
int g[N][N];//存放迷宫数组,来表示通或不通
//d[][]有两种用处:1、初始化为-1,来表示没有走过这个点,走过就更新为0
//2、保存该点与起点的距离
int d[N][N];
queue <pair<int,int>>q;
//注意一一对应
int dx[4]={-1,0,1,0},dy[4]={0,-1,0,1};
//返回从左上角移动至右下角的最少移动次数
int bfs(){
//首先将d[n][m]初始化为-1,表示没有走过
//注意使用memset函数初始化只能初始化为0或者-1
memset(d,-1,sizeof d);
//把起点入队,并将其状态改为0,表示走过一次
q.push({0,0});
d[0][0]=0;
while(!q.empty()){
//记录队头元素
auto temp=q.front();
q.pop();
for(int i=0;i<4;i++){
//从队头这个点,尝试往四个方向走,例如i=0
//x=temp.first-1,y=temp.second+0;
int x=temp.first+dx[i];
int y=temp.second+dy[i];
//尝试去走的这个点,没有超过边界,迷宫里这里是可以走的,且没走过,加入队头
if(x>=0&&x<n&&y>=0&&y<m&&g[x][y]==0&&d[x][y]==-1){
//这个点到起点的距离更新为上个点到起点的距离+1
d[x][y]=d[temp.first][temp.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();
return 0;
}
题解:
Acwing847图中点的层次-------bfs
描述:
代码:
#include <cstring>
#include <queue>
#include <iostream>
using namespace std;
int n,m;//n个点和m条边
const int N=1e5+10;
int d[N];
int h[N],e[N],ne[N],idx;
queue<int> q;
void add(int a,int b){
e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}
int bfs(){
//首先将d[N]都初始化为-1,表示没遍历过
memset(d,-1,sizeof d);
d[1]=0;
q.push(1);
while(!q.empty()){
int temp=q.front();
q.pop();
//拓展
for(int i=h[temp];i!=-1;i=ne[i]){
int j=e[i];
//该点没被遍历过
if(d[j]==-1){
d[j]=d[temp]+1;
q.push(j);
}
}
}
return d[n];
}
int main(){
cin>>n>>m;
memset(h,-1,sizeof d);
for(int i=0;i<m;i++){
int a,b;
cin>>a>>b;
add(a,b);
}
cout<<bfs();
return 0;
}
题解:
Acwing845(8数码)--------bfs
描述:
代码:
#include<iostream>
#include<queue>
#include<unordered_map>
using namespace std;
//保存字符串=>代表了当前棋盘的状态
queue<string> q;
//保存从起点到当前状态需要几步
unordered_map<string,int> d;
int dx[4]={-1,0,1,0},dy[4]={0,-1,0,1};
int bfs(string start){
//当字符串等于end时,说明整个棋盘也就是达到了最终的状态
string end="12345678x";
//bfs模板
q.push(start);
d[start]=0;
while(!q.empty()){
auto t=q.front();
q.pop();
int distance=d[t];
if(t==end) return distance;
//状态转移即x移动的过程
//首先找到x在字符串中位置
int l=t.find('x');
//将在字符串中位置转化为在棋盘的位置
int x=l/3,y=l%3;
for(int i=0;i<4;i++){
int a=x+dx[i],b=y+dy[i];
if(a>=0&&a<3&&b>=0&&b<3){
//将x与它移动到的字符进行交换
swap(t[l],t[a*3+b]);
//假如交换后的字符串没有在之前出现过
if(!d.count(t)) {
//这时已经交换了,虽然写的任然是t但是已经和之前不一样了
d[t]=distance+1;
q.push(t);
}//if
//注意这里一定要恢复原位,比如说i=0=>即向上走,之后一定要记得再次交换
//只有这样才能向下走一位回到原来的位置,这时再取i=1,向左走
swap(t[l],t[a*3+b]);
}//if
}//for
}//while
return -1;//找不到
}
int main(){
//输入初始状态的字符串
string start;
for(int i=0;i<9;i++){
char c;
cin>>c;
start+=c;
}
cout<<bfs(start);
return 0;
}
题解:
Acwing848有向图的拓扑序列---------bfs
描述:
代码:
#include <cstring>
#include <queue>
#include <iostream>
using namespace std;
int n,m;
const int N=1e5+10;
queue<int> q;
//d[N]存储节点的入度,这里不记录是否被遍历过!
int d[N];
int h[N],e[N],ne[N],idx;
//top[N]存储所有入度为0的节点,数组的存储顺序就是拓扑序列的顺序,sum是已经在top[N]里的节点数
int top[N],num=1;
void add(int a,int b){
e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}
bool topsort(){
//找到入度为0的节点入队
for(int i=1;i<=n;i++){
if(d[i]==0) q.push(i);
}
while(!q.empty()){
int temp=q.front();
q.pop();
top[num++]=temp;
for(int i=h[temp];i!=-1;i=ne[i]){
int j=e[i];
//j节点的入度-1
d[j]--;
if(d[j]==0) q.push(j);
}
}
//这里num初值是1, 后面num++了, 所以队列中元素是n的时候,num== n +1
//假如存在拓扑序列,那么所有点的入度都应该为0,top[N]应该包含所有节点,即num==n+1;
return num==n+1;
}
int main(){
cin>>n>>m;
memset(h,-1,sizeof h);
for(int i=0;i<m;i++){
int a,b;
cin>>a>>b;
add(a,b);
d[b]++;
}
if(topsort()){
for(int i=1;i<=n;i++){
cout<<top[i]<<" ";
}
}
else cout<<-1<<endl;
return 0;
}