搜索相关专题_leetcode刷题总结

leetcode上面很多关于状态求解的题目,本质上都是属于搜索问题,对于基本的搜索,主要可以分成BFS和DFS两种。

可能部分小伙伴对于搜索不是很熟悉,但是一说深度(DFS)和广度遍历(BFS)就会联想到图的广度和深度遍历,其实除了图有BFS、DFS,树也有嘛,层次遍历就是广度遍历嘛,前序、中序、后序遍历就是深度遍历嘛。当然除了很明显的遍历搜索,有些属于隐性的深度、广度搜索,一般来说深度搜索会比较常见点,例如,回溯法大多都是采用深度递归遍历的嘛。废话不多说,我们来上题目吧。

  • 1、N皇后 https://leetcode-cn.com/problems/n-queens/

    n 皇后问题研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击。
    给定一个整数 n,返回所有不同的 n 皇后问题的解决方案。
    每一种解法包含一个明确的 n 皇后问题的棋子放置方案,该方案中 ‘Q’ 和 ‘.’ 分别代表了皇后和空位。
    示例:
    输入: 4
    输出: [
    [".Q…", // 解法 1
    “…Q”,
    “Q…”,
    “…Q.”],
    ["…Q.", // 解法 2
    “Q…”,
    “…Q”,
    “.Q…”]
    ]
    解释: 4 皇后问题存在两个不同的解法。

如果你说你会回溯法,N皇后都不会,那怕是说不过去噢。代码如下:

	public List<List<String>> solveNQueens(int n) {
        List<List<String>> res = new ArrayList<>();
        int[] flag = new int[n+1];
        traceBack(flag,1,n+1,res);
        return res;
    }

    private void traceBack(int[] flag, int index, int length, List<List<String>> res) {
        if (index==length){//这个时候是全部填完了
            List<String> item = new ArrayList<>();
            StringBuilder builder = new StringBuilder();
            for (int i = 1; i < length; i++) {
                builder.delete(0,length);
                for (int j = 1; j < length; j++) {
                    if (flag[i]==j){
                        builder.append("Q");
                    }else {
                        builder.append(".");
                    }
                }
                item.add(builder.toString());
            }
            res.add(item);
            return;
        }

        for (int i = 1; i <length; i++) {
            if(isValid(flag,index,i)){
                flag[index] = i;
                traceBack(flag,index+1,length,res);
            }
        }

    }

    private boolean isValid(int[] flag, int index, int col) {
        for (int j = 1; j < index; j++) {
            if (flag[j]==col||Math.abs(j-index)==Math.abs(flag[j]-col)){
                return false;
            }
        }
        return true;
    }

对于N皇后这个问题,可以说这种解法是一种十分经典,也是十分基础的解法。但是我们仍然可以从上述的解答中学习到一些东西,比如我们是如何进行搜索的呢,是通过不断的index+1来进行搜索的,默认来看,这里的搜索顺序是从1-length-1。所以说回溯这个词也是十分有趣的嘛,像这种题目,都是有明显的顺序,然后index值等于某个边界时,便完成一次深度搜索。

  • 2、解数独 https://leetcode-cn.com/problems/sudoku-solver/

    编写一个程序,通过已填充的空格来解决数独问题。
    一个数独的解法需遵循如下规则:
    数字 1-9 在每一行只能出现一次。
    数字 1-9 在每一列只能出现一次。
    数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。
    空白格用 ‘.’ 表示。

这个题目,同样是一个经典的搜索题目,还是采用深度搜索可以求解的,代码如下:

	public static void solveSudoku(char[][] board) {
        tryNextValue(board);
    }

    private static boolean tryNextValue(char[][] board) {
        //首先判断有没有填充完全
        boolean find = false;
        int i=0,j=0;
        
        for (i=0; i < board.length; i++) {
            for (j=0; j < board[0].length; j++) {
                if (board[i][j]=='.'){
                    find = true;
                    break;
                }
            }
            if (find) break;
        }
        
        if (i==9){
            return true;
        }
            
        //此时我们得到i,j
        for (char num = '1'; num <= '9'; num++) {
            if (isValidSudoku(board,i,j,num)){
                board[i][j] = num;
                if (tryNextValue(board)){
                    return true;
                }
                board[i][j]='.';
            }
        }
        return false;
    }

    public static boolean isValidSudoku(char[][] board,int i,int j,char c) {
        for (int k = 0; k < 9; k++) {
            if (board[i][k]==c) return false;
            if (board[k][j]==c) return false;
            if (board[(i/3)*3+k/3][(j/3)*3+(k%3)]==c) return false;
        }
        return true;
    }

这里呢,我们是利用我们的一个二维数组board来作为我们搜索的一个依据,依次进行遍历,碰到空格,就跳出来,并且用全局变量i、j来进行记录,并且通过i==9是否满足来判断当前是不是已经填满了。其中

 		//此时我们得到i,j
        for (char num = '1'; num <= '9'; num++) {
            if (isValidSudoku(board,i,j,num)){
                board[i][j] = num;
                if (tryNextValue(board)){
                    return true;
                }
                board[i][j]='.';
            }
        }

这一块的代码中,我们可以看到,这里就是深度搜索的基本套路,判断当前是不是可以进行搜索的,如果可以,
1、先修改相关的变量
2、深度搜索
3、撤回修改

  • 3、 全排列 https://leetcode-cn.com/problems/permutations/

    给定一个没有重复数字的序列,返回其所有可能的全排列。
    示例:
    输入: [1,2,3]
    输出:
    [
    [1,2,3],
    [1,3,2],
    [2,1,3],
    [2,3,1],
    [3,1,2],
    [3,2,1]
    ]

这个题目,有两种办法
一种是利用一个boolean类型的数组来标记我们有没有使用第几个数,然后进行深度遍历,直到长为length的数组全部都标记也使用的时候,遍历结束,
另外一种,可以利用交换,从左边开始,依次与后边进行交换,然后index+1,直到index== length-1为止,当然为什么不是index==length呢,是由于如果index == length-1的话,后面就不存在交换,自然不需要搜索,代码如下:

	public List<List<Integer>> permute(int[] nums) {
        List<List<Integer>> res = new ArrayList<>();
        traceback(nums,0,nums.length-1,res);
        return res;
    }

    private void traceback(int[] nums, int start, int length, List<List<Integer>> res) {
        if (start==length){
            List<Integer> item = new ArrayList<>();
            for (int num:nums) {
                item.add(num);
            }
            res.add(item);
            return;
        }

        for (int i = start; i <=length; i++) {
            swap(nums,start,i);
            traceback(nums,start+1,length,res);
            swap(nums,start,i);
        }

    }
    
    private void swap(int[] nums, int i, int start) {
        int temp = nums[i];
        nums[i] = nums[start];
        nums[start] = temp;
    }

这个题目主要是为了筛去重复的,加一个isDuplicate函数进行判断即可。

public List<List<Integer>> permuteUnique(int[] nums) {
        Arrays.sort(nums);
        List<List<Integer>> result = new ArrayList<>();
        traceback(nums,0,nums.length-1,result);
        return result;

    }

    private void traceback(int[] nums, int start, int length, List<List<Integer>> res) {
        if (start==length){
            List<Integer> item = new ArrayList<>();
            for (int num:nums) {
                item.add(num);
            }
            res.add(item);
            return;
        }

        for (int i = start; i <=length; i++) {
            if(isDuplicate(nums,start,i)){
                swap(nums,start,i);
                traceback(nums,start+1,length,res);
                swap(nums,start,i);
            }

        }

    }

    private boolean isDuplicate(int[] nums, int start, int end) {
        for (int j = start; j <end; j++) {
            if (nums[j]==nums[end]){
                return false;
            }
        }
        return true;
    }

    private void swap(int[] nums, int i, int start) {
        int temp = nums[i];
        nums[i] = nums[start];
        nums[start] = temp;
    }

其他的和上面给出的代码很像,关键是其中判断重复的函数:

 	private boolean isDuplicate(int[] nums, int start, int end) {
        for (int j = start; j <end; j++) {
            if (nums[j]==nums[end]){
                return false;
            }
        }
        return true;
    }

这里从start-end-1来判断是否有等于nums[end]是为了保证,如果之前有与end相同的元素的话,就不会进行搜索,就可以避免重复!~

嗯,码字好累,歇会歇会~

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值