一。Breadth-First Search宽度优先:
从根节点开始,沿着树(图)的宽度遍历树(图)的节点。 如果所有节点均被访问,则算法中止。
宽度优先算法的遍历顺序为:1->2->3->4->5->6->7->8 from jianshu.com
什么时候用BFS?
level order traversal in binary tree
topological sorting
connected component(islands)
二。BFS的套路:
无特殊情况,BFS就用queue(队列)实现!!!
由于需要横向一层一层遍历,有时还要利用层数。
算法中引入一个FIFO的queue
第一次q.size()拿到第一层,然后用for循环吐完,expand完,generate完子节点;
第二次进入for循环就是第二层的数量了。依次执行知道q为空。
通常题目中提及“最短路径”用BFS,可以得到满足条件且路径最短(层数最少)的解。
void bfs(){
q.offerFirst(Arrays.asList(0,0); //将首节点加入队列
visited[0][0]=true; //标记首节点已经被访问
while(!q.empty()){
int size=q.size();
for(int i=0;i<size;i++){
List<Integer> cur=q.pollLast();
Integer nextX=cur.get(0)+dir[0];
Integer nextY=cur.get(0)+dir[0];
if(next is legal){
q.offerFirst(next); //入队,同时计数或维护等;
}
}
}
}
引用其它博客总结,能用DFS做的算法题,用BFS也能做。而且有限用BFS做,不容易出错。
3.算法题:
算法分类
3.1.binary tree中的BFS
(129,101,662,863,987)
129. Sum Root to Leaf Numbers
BFS
class Solution {
public int sumNumbers(TreeNode root) {
Queue<TreeNode>q = new LinkedList<>();
int sum = 0;
if(root == null)
return sum;
q.add(root);
while(!q.isEmpty()) {
TreeNode temp = q.remove();
if(temp.left == null && temp.right == null){
sum += temp.val;
}else {
if(temp.left!= null) {
int cur = temp.val * 10 + temp.left.val;
temp.left.val = cur;
q.add(temp.left);
}
if(temp.right!= null) {
int cur = temp.val * 10 + temp.right.val;
temp.right.val = cur;
q.add(temp.right);
}
}
}
return sum;
}
}
DFS
class Solution {
int sum;
public int sumNumbers(TreeNode root) {
sum=0;
dfs(root,0);
return sum;
}
public void dfs(TreeNode node,int num){
//base case//reach the bottom, return
if(node==null){
return;
}
//else: cur level : do sth
num=num*10+node.val;
if(node.left==null && node.right==null){
//BUG: if reach leaf, then accumulate total sum
sum+=num;
}
//recursion rule: 小一号问题shi什么?
dfs(node.left,num);//往下走,
dfs(node.right,num);
}
}
101. Symmetric Tree
BFS
//每次两个两个poll出来处理
public boolean isSymmetric(TreeNode root) {
Queue<TreeNode> q = new LinkedList<>();
q.add(root);
q.add(root);
while (!q.isEmpty()) {
TreeNode t1 = q.poll();
TreeNode t2 = q.poll();
if (t1 == null && t2 == null) continue;
if (t1 == null || t2 == null) return false;
if (t1.val != t2.val) return false;
q.add(t1.left);
q.add(t2.right);
q.add(t1.right);
q.add(t2.left);
}
return true;
}
recursion
class Solution {
public boolean isSymmetric(TreeNode root) {
if(root==null) return true; //null
return recurseSymm(root,root);
}
public boolean recurseSymm(TreeNode a,TreeNode b){
//base case
if(a==null && b==null) return true;
if(a==null || b==null){
return false;
}
//recursion rule ---->bug 没有写 a.val==b.val
//cur level: the value should be the same
return a.val==b.val && recurseSymm(a.left,b.right) && recurseSymm(a.right,b.left);
}
}
662. Maximum Width of Binary Tree
class Solution {
public int widthOfBinaryTree(TreeNode root) {
if(root==null) return 0;
//bfs
//interface
Pair<TreeNode, Integer> start=new Pair<>(root,0);
LinkedList<Pair<TreeNode, Integer>> q = new LinkedList<>();
q.offerLast(start);
int maxWidth = 0;
while(!q.isEmpty()){
Pair<TreeNode,Integer> head = q.getFirst(); //每次取一下第一个
int size=q.size();
int index=0;//放在外头声明
Pair<TreeNode,Integer> curPair=null;
for(int i=0;i<size;i++){
curPair=q.pollFirst();
TreeNode cur=curPair.getKey();
index=curPair.getValue();
if(cur.left != null){
q.offerLast(new Pair(cur.left,2*index));//问题:每一层index怎么加?如果遇到 2 3 null 9?
}
if(cur.right != null){
q.offerLast(new Pair(cur.right,2*index+1)); //不用+1的方式,是固定的2*idx+1
}
}
// calculate the length of the current level,
// by comparing the first and last col_index.
maxWidth = Math.max(maxWidth, index - head.getValue() + 1);
}
return maxWidth;
}
}
987. Vertical Order Traversal of a Binary Tree
/*
1(0,0)
/ \
(1,-1) 2 3 (1,1)
/ \ / \
4 5 6 7
(2,-2) (2,0) (2,2)
*/
这里不光用了new的class,还应用了自定义泛型。学起来!
//root.first:val
//root.second:row 0
//root.third:col 0
难点:
假如当前我不知道col的号码都是多少,
如何将每一个node的值加入到res中?
排序之后:<4(2,-2) >,<2 (1,-1) >,< 1(0,0)>,<5(2,0)>,<6(2,0)>,< 3 (1,1)>,<7(2,2)>;
先拿第一个作为curColumnIndex作为参照系。
List<Integer> curColumn=new ArrayList<Integer>();
Integer curColumnIndex=list.get(0).third;
for( Coord<Integer,Integer,Integer> c:list){
Integer column=c.third,value=c.first;
if(column==curColumnIndex){
curColumn.add(value);//column同样都是0时,加1,加5,加6
}else{
res.add(curColumn);
curColumnIndex=column;//column从-2转到-1,转到0
curColumn=new ArrayList();//都需要新的ArrayList
curColumn.add(value);
}
}
完整解法:
class Solution {
public List<List<Integer>> verticalTraversal(TreeNode root) {
List<List<Integer>> res=new ArrayList<>();
if(root==null) return res;
bfs(root);
//bfs遍历 +pack <TreeNode,row,col> in Queue+add to list layer by layer
//才能按照val,row,col排序
Collections.sort(list,(c1,c2)->{
if(c1.third.equals(c2.third)){
if(c1.second.equals(c2.second)){
return c1.first-c2.first;
}else{
return c1.second-c2.second;
}
}else{
return c1.third-c2.third;
}
});
List<Integer> curColumn=new ArrayList<Integer>();
Integer curColumnIndex=list.get(0).third;
for( Coord<Integer,Integer,Integer> c:list){
Integer column=c.third,value=c.first;
if(column==curColumnIndex){
//0
curColumn.add(value);
}else{
res.add(curColumn);//?????????????
curColumnIndex=column;
curColumn=new ArrayList();
curColumn.add(value);
}
}
res.add(curColumn);
return res;
}
//bfs +pack <TreeNode,row,col> in Queue+add to list layer by layer
List<Coord<Integer,Integer,Integer>> list=new ArrayList<>();
private void bfs(TreeNode root){
Queue<Coord<TreeNode, Integer, Integer>> q=new ArrayDeque<>();
int row=0;
int col=0;
q.offer(new Coord(root,row,col));
while(!q.isEmpty()){
Coord<TreeNode, Integer, Integer> cur=q.poll();
root=cur.first;
row=cur.second;
col=cur.third;
if(root!=null){
list.add(new Coord(root.val,row,col));
q.offer(new Coord(root.left,row+1,col-1));
q.offer(new Coord(root.right,row+1,col+1));
}
}
}
class Coord<F,S,T>{
//bug
public final F first;
public final S second;
public final T third;
public Coord(F first,S second,T third){
this.first=first; //r
this.second=second; //c
this.third=third; //val
}
}
}
3.2.graph中的BFS(topological sorting)
(127,133,210,310,743,1129,1311,847,1345)
Topological Sorting
Topological sorting for Directed Acyclic Graph (DAG) is a linear ordering of vertices such that for every directed edge u v, vertex u comes before v in the ordering. Topological Sorting for a graph is not possible if the graph is not a DAG.
It does not allows cycle in the graph. Like prerequisite class, if the graph has a cycle, which means one class would be its own prerequisite. That would be illogical.
Topological Sorting helps to check whether there is a cycle in the graph.
if all vertex will be add to topo-ordered-list, then it’s acyclic. Otherwise, there are some vertex left and cannot be add to the list, it’s cyclic.
AOV:Activity On Vertex
AOE: Activity On Edge
133. Clone Graph
BFS
class Solution {
Map<Node,Node> copied;
public Node cloneGraph(Node node) {
if (node == null) {
return node;
}
copied=new HashMap<>();
Node copyNode=new Node(node.val );//bug: new Node 的时候,new ArrayList<>()
copied.put(node,copyNode);
//BFS
Queue<Node> q=new LinkedList<>();
q.offer(node);
while(!q.isEmpty()){
Node cur=q.poll(); //1
if(cur.neighbors!=null){
for(Node n:cur.neighbors){
//2,4
if(!copied.containsKey(n)){
Node newNode=new Node(n.val);//2'
copied.put(n,newNode);
q.offer(n);//bug2 forget to offer n
}
copied.get(cur).neighbors.add(copied.get(n));
}
}
}
return copied.get(node);// return from the copied map
}
}
DFS
class Solution {
private HashMap <Node, Node> visited = new HashMap <> ();
public Node cloneGraph(Node node) {
//1.base case
if (node == null) {
return node;
}
// If the node was already visited before.
// Return the clone from the visited dictionary.
if (visited.containsKey(node)) {
return visited.get(node);
}
//2.recursion rule: else : node != null && !visited.containsKey(node)
// Create a clone for the given node.
// Note that we don't have cloned neighbors as of now, hence [].
Node cloneNode = new Node(node.val, new ArrayList());
// The key is original node and value being the clone node.
visited.put(node, cloneNode);
// Iterate through the neighbors to generate their clones
// and prepare a list of cloned neighbors to be added to the cloned node.
for (Node neighbor: node.neighbors) {
cloneNode.neighbors.add(cloneGraph(neighbor));
//key point: cloneGraph(neighbor) return the copied node...
}
return cloneNode;
}
}
(Topological Sorting)
207. Course Schedule
难点: return removedEdges==totalEdges;
如果移除的边和总的边数一样多,那么说明没有环,topological sorting正常进行。课程设置没有问题。
class Solution {
public boolean canFinish(int numCourses, int[][] prerequisites) {
int totalEdges=prerequisites.length;
int[] indeg=new int[numCourses];
//build the graph
for(int[] edge: prerequisites){
indeg[edge[0]]++;
}
Queue<Integer> q=new LinkedList<>();
for (int i = 0; i < numCourses; i++) {
if (indeg[i] == 0) q.offer(i);//特别 :+0
} //[[1,0],[2,1],[3,1]]
int removedEdges = 0;
while(!q.isEmpty()){
//
int cur=q.poll();//0
for(int[] edge: prerequisites){
if(cur==edge[1]){
//0
indeg[edge[0]]--; //indeg[1] --
removedEdges++;
if(indeg[edge[0]] == 0){
//indeg[1]==0
q.offer(edge[0]);
}//我的bug出在,这个if的判断写在了上一个if的外头,与之并列了。
//logic here: when the cur==0; indeg[1]--,
//1 becomes the one with indegree==0,offer 1 into the q.
}
}
}
return removedEdges==totalEdges;//check if remove n edges in the topo sorting, all has been offer and poll.//which means, prereq and later courses are all satisfied.there is no cycle inside.
}
}
(Topological Sorting)
210. Course Schedule II
难点:嵌套的int[]中
for (int[] edge : prerequisites) {
inDeg[edge[0]]++;
}
什么时候用edge[0],什么时候用edge[1]。
什么时候inDeg[edge[0]]++;什么时候inDeg[edge[0]]–;
class Solution {
public int[] findOrder(int numCourses, int[][] prerequisites) {
List<Integer> res = new ArrayList<>();
int[] inDeg = new int[numCourses];
for (int[] edge : prerequisites) {
inDeg[edge[0]]++;
}
Queue<Integer> queue = new ArrayDeque<>();
for (int i = 0; i < numCourses; i++) {
if (inDeg[i] == 0) queue.offer(i);
}
while (!queue.isEmpty()) {
Integer cur = queue.poll();
res.add(cur);
for (int[] edge : prerequisites) {
if (cur == edge[1]) {
inDeg[edge[0]]--;
if (inDeg[edge[0]] == 0) {
queue.offer(edge[0]);
}
}
}
}
int[] resArray = new int[numCourses];
if (res.size() == numCourses) {
for (int i = 0; i < numCourses; i++) {
resArray[i] = res.get(i);
}
}
return res.size() == numCourses ? resArray : new int[0];
}
}
(topological sorting)
310. Minimum Height Trees
youtube中文solution
21天高频复习计划
想象把一个点拿出来当根,然后从叶子结点(indegree==1)这层开始offer,
每次删除叶子结点,最后剩下的一层,就是根。
如果剩下两个,就有两个都可以作为根的节点
class Solution {
public List<Integer> findMinHeightTrees(int n, int[][] edges) {
List<Integer> res=new ArrayList<>();
if(n==1) {
res.add(0);
return res;
}
int[] indeg=new int[n]; //[3,0]
Map<Integer,List<Integer>> map=new HashMap<>();
for(int i=0;i<n;i++) map.put(i,new ArrayList<>());
for(int[] edge:edges){
map.get(edge[0]).add(edge[1]);
map.get(edge[1]).add(edge[0]);
indeg[edge[0]]++; //indeg[3]
indeg[edge[1]]++;//indeg[0]
}
Queue