算法学习之深度优先搜索(DFS)

1、无死角搜索

1、数独游戏

1、问题
在这里插入图片描述2、思想
(1)在输入上,将输入的每一行字符串转换为一个字符数组存入二位数组的一行
(2)深度搜索一般模板:

int dfs(t)
{
    if(t>n)  
    {
         print(); 
         return ;
    }
    for(int i=1;<=n;i++i)
    {
         if(judge(t))    dfs(t+1);
     }
}

(3)在dfs内部用检查是否符合条件的函数check进行设置,然后进入下一层dfs
(4)注意!这一层不符合条件返回之后要还原数组,将这个位置设置为‘0’
(5)不符合条件进入下一层dfs
(6)判断到最后符合条件了,直接输出并且使用System.exit(0)而不是return 因为此题只有唯一解,不用返回上一层,直接结束方法即可

3、代码

package com.lanqiao.eight;

import java.util.Scanner;

public class shudu {
	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);
		char[][] table = new char[9][];
		for(int i=0;i<9;i++)
		{
			table[i] = sc.nextLine().toCharArray();
			//注意点:将输入的一行字符串转为字符并存入当前一行
		}
		dfs(table,0,0);
	}

	private static void dfs(char[][] table, int x, int y) {
		if(x==9)
		{
			print(table);
			System.exit(0);//退出程序,不能用return
		}
		if(table[x][y]=='0')//此位无数字
		{
			for(int k=1;k<10;k++)
			{
				if(check(table,x,y,k))
				{
					table[x][y]=(char)('0'+k);
					dfs(table,x+(y+1)/9,(y+1)%9);
					//处理下一个状态,小技巧:当y==8的时候才会进入下一行
					//当y==8的时候从下一行的第一个开始 (y+1)%9==0
				}
			}
			table[x][y]='0';
		}else {
			dfs(table,x+(y+1)/9,(y+1)%9);//处理下一个状态
		}
	}

	private static boolean check(char[][] table, int x, int y, int k) {
		boolean flag = true;
		char kl = (char)(k+'0');
		//检查同行、同列
		for(int i=0;i<table.length;i++)
		{
			if(table[x][i]==kl) return false;
		}
		for(int i=0;i<table[0].length;i++)
		{
			if(table[i][y]==kl) return false;
		}
		//检查小九宫格
		int startX = x/3*3;
		int startY = y/3*3;
		for(int i=startX;i<(x/3+1)*3;i++)
		{
			for(int j=startY;j<(y/3+1)*3;j++)
			{
				if(table[i][j]==kl) return false;
			}
		}
		return flag;
	}

	private static void print(char[][] table) {
		for(char[] a:table)
		{
			for(char c:a)
			{
				System.out.print(c+" ");
			}
			System.out.println();
		}
		
	}
	
}
2、部分和

1、题目
在这里插入图片描述2、思想
(1)相当于构造非空子集,在DFS过程中对每个元素有两种情况,一种要一种不要,逐渐深入。
(2)当满足条件的时候,退出
(3)因为要输出使用的元素,所以使用一个静态数组进行记录,但是要注意,dfs退回时进行回溯恢复数组

3、代码

package com.lanqiao.eight;

import java.util.LinkedList;
import java.util.Scanner;

public class findK {
	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);
		int n = sc.nextInt();
		int[] arr = new int[n];
		for(int i=0;i<n;i++)
		{
			arr[i] = sc.nextInt();
		}
		int k = sc.nextInt();
		dfs(arr,0, k);
	}
static LinkedList<Integer> ans = new LinkedList<>();
	private static void dfs(int[] arr, int cur, int k) {
		if(k==0)
		{
			System.out.println("Yes!");
			for(Integer i:ans)
			{
				System.out.print(i+" ");
			}
			System.exit(0);
		}
		if(k<0||cur==arr.length) return;
		dfs(arr,cur+1,k);//不要当前元素
		ans.add(arr[cur]);
		dfs(arr,cur+1,k-arr[cur]);//要当前元素
		//回溯
		ans.remove(ans.size()-1);//LinkedList下标从0开始
	}
}

3、水洼数目

1、题目
在这里插入图片描述在这里插入图片描述2、思想
(1)对每个W进入dfs
(2)将进入dfs的位置变为‘.’ 走围绕它一圈的八个位置,如果是W进入dfs
(3)知道全图无W结束

3、代码(经典,要背)

package com.lanqiao.eight;

import java.util.Scanner;

public class shuiwa {
	static int M,N;
	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);
		M = sc.nextInt();
		N = sc.nextInt();
		int ans = 0;
		char[][] table = new char[M][];
		//table[0] = sc.nextLine().toCharArray();
		for(int i=0;i<M;i++)
		{
			table[i] = sc.next().toCharArray();
		}
		for(int i = 0;i<M;i++)
		{
			for(int j= 0;j<N;j++) {
				if(table[i][j]=='W')
				{
					ans++;
					dfs(table,i,j);
				}
			}
		}
		System.out.println(ans);
	}

	private static void dfs(char[][] table, int x, int y) {
		table[x][y] = '.';
		for(int i=-1;i<=1;i++)
		{
			for(int j=-1;j<=1;j++)
			{
				if(i==0&&j==0) continue;
				if(x+i<table.length&&y+j<table[0].length&&x+i>=0&&y+j>=0)
				{
					if(table[x+i][y+j]=='W')
					{
						dfs(table,x+i,y+j);
					}
				}
			}
		}
	}
}

4、亮点:
(1)在外层利用判断条件进入dfs
(2)在内层直接变.(将水抽干)

5、注意点
(1)在进入dfs后首先要对当前点变为’ . ‘
(2)在输入时要采用next(),nextLine() 会吞上面留下来的回车,导致首行为空

回溯和剪枝:

在这里插入图片描述剪枝:
在递归之前使用if判断是否需要进入这个状态,成为剪枝

4、N皇后问题

1、题目
在这里插入图片描述2、思想
(1)判断对角线技巧
在正对角线的元素,x-y相同。反对角线上的元素x+y相同
(2)借助一个一位数组实现,下标代表行数,数据代表在这一行的第几列
(3)对每行的每列进行循环,判断是否满足条件,满足的时候向下一行进行传输
(4)
3、代码:

package com.lanqiao.eight;

import java.util.Scanner;

public class nQueen {
	static int count = 0;
	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);
		int N = sc.nextInt();
		int[] ans = new int[N];
		dfs(ans, 0);
		System.out.println(count);
	}
	//额。。col代表行,弄反了
	public static void dfs(int[] arr, int col) {
		if (col==arr.length)
		{
			count++;
			return;
		}
		//尝试在当前行的任何一列放一个皇后
//		for(int i=0;i<arr.length;i++)
//		{
			for (int j = 0; j < arr.length; j++) {
				if (check(arr, col,j)) {
					arr[col]=j;
					dfs(arr, col + 1);
				}
//			}
		}
	}

	private static boolean check(int[] arr,int col, int j) {
		for(int i=0;i<col;i++) 
		{
			//分别是某一行与当前选中列想通、正对角线和反对角线
			if(arr[i]==j||i+arr[i]==col+j||arr[i]-i==col-j)
				return false;
		}
		return true;
	}
}

5、素数环

在这里插入图片描述在这里插入图片描述2、思想
(1)暴力破解法:把所有情况列出来,使用排列组合形式,然后对其进行一个一个判断
(2)dfs
在这里插入图片描述
逐步深入,然后剪枝
判断条件:
1、i在之前没有出现过
2、i和前面一个数相加是一个素数

3、代码

package com.lanqiao.eight;

import java.util.Scanner;

public class sushuCircle {
	static int[] ans;
	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);
		int N = sc.nextInt();
		ans = new int[N];
		ans[0]=1;
		dfs(N,1);
		for(int i:ans)
		{
			System.out.print(i+" ");
		}
	}

	private static void dfs(int n,int cur) {
		if(cur==n&&isP(ans[0]+ans[n-1]))
		{
			return;
		}
		for(int i=2;i<=n;i++)
		{
			if(check(i,cur))
			{
				ans[cur]=i;
				dfs(n,cur+1);
				//可以不用回溯,因为后面会直接覆盖
			}
		}
		
	}

	private static boolean isP(int k) {
		for(int i=2;i<k;i++)
		{
			if(k%i==0) return false;
		}
		return true;
	}

	private static boolean check(int i,int cur) {
		for(int a:ans)
		{
			if(a==i||!isP(i+ans[cur-1])) return false;
		}
		return true;
	}
	
}

4、注意点
(1)在得出结果时,要注意验证第一个和最后一个是否成立,成立即可形成封闭环

6、困难的串

1、问题
在这里插入图片描述 2、思想
判断条件:
是否前后截取相同

3、代码

package com.lanqiao.eight;

import java.util.Scanner;

public class difString {
	static int count = 0;
	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);
		int n = sc.nextInt();
		int l = sc.nextInt();
		dfs(l,n,"");
		
	}

	private static void dfs(int l, int n, String str) {
		//尝试在str后面添加一个字符
		for(char i='A';i<'A'+l;i++)
		{
			if(isHard(str,i))//判断是否是困难的串
			{
				String x = str+i;
				System.out.println(x);//输出
				count++;//计数
				if(count==n)//判断是否判断到了这一个
				{
					System.exit(0);//已经到达这一难度,退出
				}
				dfs(l,n,x);//进行下一步判断
			}
		}
	}

	private static boolean isHard(String str, char c) {
		int count = 0;//截取的宽度
		for(int i=str.length()-1;i>=0;i-=2)
		{
			final String s1 = str.substring(i,i+count+1);
			final String s2 = str.substring(i+count+1)+c;
			if(s1.equals(s2))
				return false;
			count++;
		}
		return true;
	}
}

小结

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值