BFS

BFS

(1)BFS的问题一般都会选用队列方式实现;
(2)代码模板如下:

void BFS()
{
    定义队列;
    定义备忘录,用于记录已经访问的位置;

    判断边界条件,是否能直接返回结果的。

    将起始位置加入到队列中,同时更新备忘录。

    while (队列不为空) {
        获取当前队列中的元素个数。
        for (元素个数) {
            取出一个位置节点。
            判断是否到达终点位置。
            获取它对应的下一个所有的节点。
            条件判断,过滤掉不符合条件的位置。
            新位置重新加入队列。
        }
    }
}

一、二进制矩阵中的最短路径

在一个 N × N 的方形网格中,每个单元格有两种状态:空(0)或者阻塞(1)。
一条从左上角到右下角、长度为 k 的畅通路径,由满足下述条件的单元格 C_1, C_2, …, C_k 组成:
相邻单元格 C_i 和 C_{i+1} 在八个方向之一上连通(此时,C_i 和 C_{i+1} 不同且共享边或角)
C_1 位于 (0, 0)(即,值为 grid[0][0])
C_k 位于 (N-1, N-1)(即,值为 grid[N-1][N-1])
如果 C_i 位于 (r, c),则 grid[r][c] 为空(即,grid[r][c] == 0)
返回这条从左上角到右下角的最短畅通路径的长度。如果不存在这样的路径,返回 -1 。
在这里插入图片描述
解:

struct Node {
    int x;
    int y;
};
class Solution {
public:
    int shortestPathBinaryMatrix(vector<vector<int>>& grid) {
        int ans = 0;
        queue<Node> myQ; // BFS一般通过队列方式解决
        int M = grid.size();
        int N = grid[0].size();

        // 先判断边界条件,很明显,这两种情况下都是不能到达终点的。
        if (grid[0][0] == 1 || grid[M - 1][N - 1] == 1) {
            return -1;
        }
        
        // 备忘录,记录已经走过的节点
        vector<vector<int>> mem(M, vector<int>(N, 0));
        
        myQ.push({0, 0});
        mem[0][0] = 1;

        // 以下是标准BFS的写法
        while (!myQ.empty()) {
            int size = myQ.size();

            for (int i = 0; i < size; i++) {
                Node currentNode = myQ.front();
                int x = currentNode.x;
                int y = currentNode.y;

                // 判断是否满足退出的条件
                if (x == (N - 1) && y == (M - 1)) {
                    return (ans + 1);
                }

                // 下一个节点所有可能情况
                vector<Node> nextNodes = {{x + 1, y}, {x - 1, y}, {x + 1, y - 1}, {x + 1, y + 1},
                                          {x, y + 1}, {x, y - 1}, {x - 1, y - 1}, {x - 1, y + 1}};

                for (auto& n : nextNodes) {
                    // 过滤条件1: 边界检查
                    if (n.x < 0 || n.x >= N || n.y < 0 || n.y >= M) {
                        continue;
                    }
                    // 过滤条件2:备忘录检查
                    if (mem[n.y][n.x] == 1) {
                        continue;
                    }
                    // 过滤条件3:题目中的要求
                    if (grid[n.y][n.x] == 1) {
                        continue;
                    }

                    // 通过过滤筛选,加入队列!
                    mem[n.y][n.x] = 1;
                    myQ.push(n);
                }               

                myQ.pop();
            }
            ans++;
        }

        return -1;        
    }
};

二、完全平方数

给定正整数 n,找到若干个完全平方数(比如 1, 4, 9, 16, …)使得它们的和等于 n。你需要让组成和的完全平方数的个数最少。
在这里插入图片描述
解:
在这里插入图片描述

public class NumSquares {
	private class Node {
		int val;
		int step;

		public Node(int val, int step) {
			this.val = val;
			this.step = step;
		}
	}

	public int numSquares(int n) {
		Queue<Node> queue = new LinkedList<>();
		queue.add(new Node(n, 1));
		boolean record[] = new boolean[n];
		while (!queue.isEmpty()) {
			int val = queue.peek().val;
			int step = queue.peek().step;
			queue.remove();
			// 每一层的广度遍历
			for (int i = 1;; i++) {
				int nextVal = val - i * i;
				// 说明已到最大平方数
				if (nextVal < 0)
					break;

				// 由于是广度遍历,所以当遍历到0时,肯定是最短路径
				if(nextVal == 0)
					return step;
				
				// 当再次出现时没有必要加入,因为在该节点的路径长度肯定不小于第一次出现的路径长
				if(!record[nextVal]){
				   queue.add(new Node(nextVal,step + 1));
				   record[nextVal] = true;
				}
			}
		}
		return -1;
	}

	public static void main(String[] args) {
		System.out.println(new NumSquares().numSquares(13));
	}

}

三、单词接龙

给定两个单词(beginWord 和 endWord)和一个字典,找到从 beginWord 到 endWord 的最短转换序列的长度。转换需遵循如下规则:
每次转换只能改变一个字母。
转换过程中的中间单词必须是字典中的单词。

说明:
如果不存在这样的转换序列,返回 0。
所有单词具有相同的长度。
所有单词只由小写字母组成。
字典中不存在重复的单词。
你可以假设 beginWord 和 endWord 是非空的,且二者不相同。

解:
对给定的 wordList 做预处理,找出所有的通用状态。将通用状态记录在字典中,键是通用状态,值是所有具有通用状态的单词。
将包含 beginWord 和 1 的元组放入队列中,1 代表节点的层次。我们需要返回 endWord 的层次也就是从 beginWord 出发的最短距离。
为了防止出现环,使用访问数组记录。
当队列中有元素的时候,取出第一个元素,记为 current_word。
找到 current_word 的所有通用状态,并检查这些通用状态是否存在其它单词的映射,这一步通过检查 all_combo_dict 来实现。
从 all_combo_dict 获得的所有单词,都和 current_word 共有一个通用状态,所以都和 current_word 相连,因此将他们加入到队列中。
对于新获得的所有单词,向队列中加入元素 (word, level + 1) 其中 level 是 current_word 的层次。
最终当你到达期望的单词,对应的层次就是最短变换序列的长度

import javafx.util.Pair;

class Solution {
  public int ladderLength(String beginWord, String endWord, List<String> wordList) {

    // Since all words are of same length.
    int L = beginWord.length();

    // Dictionary to hold combination of words that can be formed,
    // from any given word. By changing one letter at a time.
    HashMap<String, ArrayList<String>> allComboDict = new HashMap<String, ArrayList<String>>();

    wordList.forEach(
        word -> {
          for (int i = 0; i < L; i++) {
            // Key is the generic word
            // Value is a list of words which have the same intermediate generic word.
            String newWord = word.substring(0, i) + '*' + word.substring(i + 1, L);
            ArrayList<String> transformations =
                allComboDict.getOrDefault(newWord, new ArrayList<String>());
            transformations.add(word);
            allComboDict.put(newWord, transformations);
          }
        });

    // Queue for BFS
    Queue<Pair<String, Integer>> Q = new LinkedList<Pair<String, Integer>>();
    Q.add(new Pair(beginWord, 1));

    // Visited to make sure we don't repeat processing same word.
    HashMap<String, Boolean> visited = new HashMap<String, Boolean>();
    visited.put(beginWord, true);

    while (!Q.isEmpty()) {
      Pair<String, Integer> node = Q.remove();
      String word = node.getKey();
      int level = node.getValue();
      for (int i = 0; i < L; i++) {

        // Intermediate words for current word
        String newWord = word.substring(0, i) + '*' + word.substring(i + 1, L);

        // Next states are all the words which share the same intermediate state.
        for (String adjacentWord : allComboDict.getOrDefault(newWord, new ArrayList<String>())) {
          // If at any point if we find what we are looking for
          // i.e. the end word - we can return with the answer.
          if (adjacentWord.equals(endWord)) {
            return level + 1;
          }
          // Otherwise, add it to the BFS Queue. Also mark it visited
          if (!visited.containsKey(adjacentWord)) {
            visited.put(adjacentWord, true);
            Q.add(new Pair(adjacentWord, level + 1));
          }
        }
      }
    }

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值