搜索
-
深度优先搜索DFS
-特点:
执着人,一路到头,再回头。
代码特点:回溯 剪枝 -
数据定义
数组path-存储路径
boolean数组-st存储当前的数有没有被用过
递归-状态改变
题目1:全排列问题
代码:
数据定义
int path[n]//保存路径
boolean st[N]//保存状态
代码实现
package 大学菜;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
public class 深度优先搜索遍历 {
//一条路走到死给定一个整数 n,将数字 1∼n 排成一排,将会有很多种排列方法。
//现在,请你按照字典序将所有的排列方法输出。
//3
//存储路径的
static int N = 100010;
static int[] path = new int[N];
static boolean[] st = new boolean[N];
static int n;
public static void dfs(int u){
//先考虑结束
if(u>n){
System.out.println();
for(int i=1;i<=n;i++){
System.out.print(path[i]);
}
}
//否则就迭代执行
for(int i=1;i<=n;i++){
if(st[i] == false){//渭北访问
//说明就放它
path[u] = i;
st[i] = true;
dfs(u+1);
st[i] = false;
}
}
}
public static void main(String[] args) throws IOException {
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
n = Integer.valueOf(reader.readLine());
//候选数字就是1-n
for(int i=1;i<=n;i++){
st[i] = false;
}
dfs(1);//从某一点进行
}
}
题目2:八皇后问题
解法1.
剪枝:
剩下的不要,直接剪枝,回溯
col[] //列
dg[] //正斜对角线
udg[]//反对角线技巧:
使用函数进行
判断的时候,进行3个的布尔判断。
图解:
代码:
package 大学菜;
import java.util.Scanner;
public class n皇后问题4 {
//第一步数据结构
static int N =10;
static int n;
static char[][] g = new char[N][N];
static boolean[] col = new boolean[N];
static boolean[] dg = new boolean[N];
static boolean[] udg = new boolean[N];
public static void dfs(int y){
//判断出口
if(y==n){
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
System.out.print(g[i][j]);
}
System.out.println();
}
System.out.println();
}
//循环
for(int x=0;x<n;x++){
if(col[x] == false&&dg[y+x]==false&&udg[y-x+n]==false){
g[y][x] = 'Q';
col[x] = true;
dg[y+x] = true;
udg[y-x+n] = true;
dfs(y+1);
col[x] = false;
dg[y+x] = false;
udg[y-x+n] = false;
g[y][x] = '.';
}
}
}
public static void main(String[] args) {
//接收数据
Scanner sc = new Scanner(System.in);
n = sc.nextInt();
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
g[i][j] = '.';
}
}
dfs(0);
}
}
注意
解法2:
```bash
row[]
col[] //行
dg[] //正斜对角线
udg[]//反对角线技巧:
每个格子只有两道选择,一个是放,一个是不放。
进行每个格子的枚举。结束条件:
n个皇后都摆好。
结束条件进行衡量。
暴力解决:
1.先看第一行,枚举法进行
2.在看第二行,枚举
package 大学菜;
import java.util.Scanner;
public class n皇后问题5 {
static int N =20;//对角线的二倍
static char[][] g = new char[N][N];
static boolean[] row = new boolean[N];
static boolean[] col = new boolean[N];
static boolean[] dg = new boolean[N];
static boolean[] udg = new boolean[N];
static int n;
public static void dfs(int x,int y,int s){
if(y==n){
y=0;x++;
}
if(x==n){
if(s==n){
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
System.out.print(g[i][j]);
}
System.out.println();
}
System.out.println();
}
//当这个地方不满足条件的时候,必须要return
return;///return必须要you
}
//出口均不满足
dfs(x,y+1,s);
if(row[x] == false && col[y] == false && dg[x+y]==false && udg[x-y+n] == false){
g[x][y] = 'Q';
row[x] =col[y] = dg[x+y] = udg[x-y+n]= true;
dfs(x,y+1,s+1);
row[x] =col[y] = dg[x+y] = udg[x-y+n]= false;
g[x][y] ='.';
}
}
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
n = sc.nextInt();
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
g[i][j] = '.';
}
}
dfs(0,0,0);
}
}
- 2.宽度优先搜索
- 最短路问题:属于DP问题
一圈圈进行求,先可以求到最短的那个。第一次搜到距离是最短的。
-层搜,会一次看很多条路,稳重人。扩展性。
代码特点:
- 数据定义:
queue-bfs-
g[][]//图
d[][]//juli
路径记录:prep[][]
题目:
走迷宫
数据结构
成对类
pair<>
queue
g[][]
d[][]
n,m
代码:
package 大学菜;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.LinkedList;
import java.util.Queue;
class pair<I extends Number, I1 extends Number> {
int x;
int y;
public void pair(int i, int j){
this.x=i;
this.y=j;
}
}
public class 走迷宫 {
static int N = 110;
static int[][] g = new int[N][N];
static int[][] d = new int[N][N];
static pair<Integer,Integer> p= new pair<Integer,Integer>();
static Queue<pair> queue = new LinkedList<pair>();
static int n;
static int m;
public static int bfs(){
//d初始化
for(int i=0;i<n;i++){
for(int j=0;j<m;j++){
d[i][j] =-1;
}
}
//方向向量
pair fir = new pair();
fir.x=0;
fir.y=0;
queue.add(fir);
d[0][0] = 0;
int[] dx = {-1,0,1,0};//上右下左
int[] dy = {0,1,0,-1};
//开始点:
while (!queue.isEmpty()){
p = queue.poll();//出队列
//看看四个方向
for(int i=0;i<4;i++){
int x = p.x+dx[i];//x方向
int y = p.y+dy[i];//y方向
if(x>=0 && x<n && y>=0 && y<m && g[x][y]==0 && d[x][y]==-1 ){
d[x][y] = d[p.x][p.y]+1;
///入队
pair a = new pair();
a.x=x;
a.y=y;
queue.offer(a);
}
}
}
return d[n-1][m-1];
}
public static void main(String[] args) throws IOException {
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
String[] str = reader.readLine().split(" ");
n = Integer.valueOf(str[0]);
m = Integer.valueOf(str[1]);
for(int i=0;i<n;i++){
String[] s= reader.readLine().split(" ");
for(int j=0;j<m;j++){
g[i][j] = Integer.valueOf(s[j]);
//System.out.println(g[i][j]);
}
}
System.out.println(bfs());
}
}
对比
对比:DFS VS BFS
方法:递归。 接替入站
数据结构:stack queue
题目:深搜 最短路径
时间:O(h) O(2^h)
空间性优势“DFS
最短路径优势:BFS
层搜:具有最短路径的性质。第一次搜到的距离一定是最短距离。
树和图的存储和遍历
树是一个特殊的图。无环连通图。
无向图是一种特殊的有向图。
- 有向图的存储-
存储方式
1.邻接矩阵
2.邻接表
代码:
数据结构:
h[N]
e[M]
next[M]
index[]
//初始化,头指向-1
- 树和图的遍历:
-
深度优先遍历
-
题目1:树的重心-找最深最多的点
代码:
package 大学菜;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
/*给定一颗树,树中包含 n 个结点(编号 1∼n)和 n−1 条无向边。
请你找到树的重心,并输出将重心删除后,剩余各个连通块中点数的最大值。
重心定义:重心是指树中的一个结点,如果将这个点删除后,剩余各个连通块中点数的最大值最小,那么这个节点被称为树的重心。*/
public class 树的中心 {
//需要找出每个连通块中的点的总共数量中的最小值
//第一行包含整数 n,表示树的结点数。
//树是特殊的图,所以可以用图存储-
//接下来 n−1 行,每行包含两个整数 a 和 b,表示点 a 和点 b 之间存在一条边。
//邻接表存储
static int N=100010;
static int[] h=new int[N];
static int[] e=new int[N*2];
static int[] next = new int[N *2];
static int index;
static int n;
static int ans =N;
static boolean st[] = new boolean[N];
public static void add(int a,int b){//建一条a-b的边
e[index] = b;
next[index] = h[a];
h[a] = index;
index++;
}
//最小的最大值
public static int dfs(int u){
st[u] = true;//访问过这个点就true;
int size =0;//当前这个的res
int sum = 1;
//出口
for(int i=h[u]; i !=-1;i=next[i]){
int j = e[i];
if(st[j]) continue;
int s = dfs(j);//到这里
size = Math.max(size,s);//返回到
sum = sum+s;//
}
//上半部分
size = Math.max(size,n-sum);
ans = Math.min(size,ans);//最大的最小。
return sum;
}
public static void main(String[] args) throws IOException {
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
n = Integer.valueOf(reader.readLine());
//建图
//初始化时,都指向-1
for (int i = 0; i < h.length; i++) {
h[i] = -1;
}
for (int i = 0; i < n - 1; i++) {
String[] str = reader.readLine().split(" ");
int a = Integer.valueOf(str[0]);
int b = Integer.valueOf(str[1]);
//邻接表加边
add(a, b);
add(b, a);
}
//搜索点u-连通
//记录每个点成为重心后的最大连通点个数,在这些最大的连通点个数中,找出最小的。
dfs(1);
System.out.println(ans);
}
}
注意:
1.N初始化
2.最大值的最小值初始化。
3.头节点的初始化。
数据结构:
每个点只遍历一次
在邻接表的基础上
h[N]
e[M]
next[M]
index[]
st[N]//状态
boolean[]
题目1:
例如说这个节点4:
h[4]-3,6,1
实际上这三个点都属于4的邻接表里next点,所以都回去遍历。
但是最后一次循环返回的9.
广度优先遍历
- 数据结构
- h[N]
e[M]
next[M]
index[]
- d[N]
- Q[N]
- /*终点
* 1。结构定义-queue
* 2。距离初始化
* 3。头节点初始化
* 4。图存储-领邻接表
* 要素:h[],next[],index,e[]*/
输出层次
代码:
package 大学菜;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.LinkedList;
import java.util.Queue;
/*终点
* 1。结构定义-queue
* 2。距离初始化
* 3。头节点初始化
* 4。图存储-领邻接表
* 要素:h[],next[],index,e[]*/
public class 层次最短距离2 {
static int N = 100010;
static int[] h = new int[N];
static int[] next = new int[N];
static int[] e = new int[N];
static int index;
static Queue<Integer> queue = new LinkedList<>();
static int[] d = new int[N];
static int n;
public static void add(int a,int b){
e[index] =b;
next[index] = h[a];
h[a] = index;
index++;
}
public static int bfs(){
//初始化
for(int i= 0;i<d.length;i++){
d[i] = -1;
}
d[1]=0;
queue.add(1);
while (!queue.isEmpty()){
int p = queue.poll();
for(int i=h[p];i!=-1;i=next[i]){
int j = e[i];
if(d[j] == -1){
d[j] = d[p]+1;
queue.add(j);
}
}
}
return d[n];
}
public static void main(String[] args) throws IOException {
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
String[] s = reader.readLine().split(" ");
n = Integer.valueOf(s[0]);
int m = Integer.valueOf(s[1]);
for(int i=0;i<h.length;i++){
h[i] = -1;
}
for(int i=0;i<m;i++){
String[] s2 = reader.readLine().split(" ");
int a = Integer.valueOf(s2[0]);
int b = Integer.valueOf(s2[1]);
add(a,b);
}
System.out.println(bfs());
}
}
- 宽搜应用:
拓扑序列:
所有的边都是从前指向后的。
有向无环图。至少存在一个入度为0的点。
用度数进行衡量
开始:队列放入入度为0的点p
每次都进行判断,
删除p,它的邻接点如果也有入度为0的点的话,也继续入队列
使用数组进行记录被删去的点。
如果当前点有点是也是入度为0,就进队列。
q[N]
d[N]
代码:
package 大学菜;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.LinkedList;
import java.util.Queue;
/*终点
* 1。结构定义-queue
* 2。距离初始化
* 3。头节点初始化
* 4。图存储-领邻接表
* 要素:h[],next[],index,e[]*/
public class 拓扑序列 {
static int N = 100010;
static int[] h = new int[N];
static int[] next = new int[N];
static int[] e = new int[N];
static int index;
static Queue<Integer> queue = new LinkedList<>();
static int[] d = new int[N];//入度
static int n,cnt=1;
//记录数组:
static int[] top = new int[N];
public static void add(int a,int b){
e[index] =b;
next[index] = h[a];
h[a] = index;
index++;
}
public static boolean topbfs(){
//初始化-- cuoqu
for(int i=1;i<=n;i++){
if(d[i] == 0){
queue.add(i);
}
}
//
while (!queue.isEmpty()){
int p = queue.poll();
top[cnt] = p;
cnt++;
for(int i=h[p];i!=-1;i=next[i]){
int j = e[i];
d[j]--;
if(d[j] == 0){
queue.add(j);
}
}
}
return cnt>=n;
}
public static void main(String[] args) throws IOException {
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
String[] s = reader.readLine().split(" ");
n = Integer.valueOf(s[0]);
int m = Integer.valueOf(s[1]);
for(int i=0;i<h.length;i++){
h[i] = -1;
}
for(int i=0;i<m;i++){
String[] s2 = reader.readLine().split(" ");
int a = Integer.valueOf(s2[0]);
int b = Integer.valueOf(s2[1]);
add(a,b);
d[b]++;
}
if(topbfs()){
for(int i=1;i<=n;i++){
System.out.println(top[i]+" ");
}
}else System.out.print("-1");
}
}
//3 3
//1 2
//2 3
//1 3