球球速刷LC--BFS DFS 二轮


二叉树里已经大量使用了DFS,BFS,二叉树的前序遍历,中序遍历 后序遍历就是DFS,层序遍历
就是BFS。
对于二叉树这种具有单向分层结构,进行DFS BFS时无需担心会重复访问,但是对于无向图或者
有向有环图等结构,需要使用辅助数据结构来记录当前节点是否已经访问过。

岛屿数目!!!
class Solution {
    int m=0;
    int n=0;
    //DFS解法
    void mark_island_dfs(int i,int j,vector<vector<char>>& grid){
            grid[i][j] = 'a';//设定访问标志
            if(i-1>=0 && grid[i-1][j]=='1' ) mark_island1_dfs(i-1,j,grid);
            if(i+1<m && grid[i+1][j]=='1' ) mark_island1_dfs(i+1,j,grid);
            if(j-1>=0 && grid[i][j-1]=='1' ) mark_island1_dfs(i,j-1,grid);
            if(j+1<n && grid[i][j+1]=='1' ) mark_island1_dfs(i,j+1,grid);
   }    
public:
    int numIslands(vector<vector<char>>& grid) {
        if(grid.empty()) return 0;
        
         m=grid.size();
         n=grid[0].size();
        //mark all point's of a same island as visited;
       int island_num=0;  
        for(int i=0;i<m;++i){
            for(int j=0;j<n;++j){
                //find a seed of a island
              if(grid[i][j]=='1' ){
                  ++island_num;
                  mark_island_dfs(i,j,grid);
              }  
            }
        }
        return island_num;
    }
};
包围区域!!!

先将所有与边缘连通的O标记为其他字符Y,在将剩余O翻转为X。最后恢复Y

class Solution {
    void mark_dfs(int i,int j,vector<vector<char>>& board){
         vector<int>x_directions={i,i,i-1,i+1};
         vector<int>y_directions={j-1,j+1,j,j};
        
         for(int k=0;k<4;++k){
             int x=x_directions[k];
             int y=y_directions[k];
             if(x>=0 && y>=0 && x<board.size() && y<board[0].size()){
                 if(board[x][y] == 'O'){
                     board[x][y]='Y';
                     mark_dfs(x,y,board);
                 }
             }
         }
    }
 public:
    void solve(vector<vector<char>>& board) {
        //将所有与边缘联通的O 标记为Y
        if(board.size()<=1) return;
        
        int m=board.size();
        int n=board[0].size();
        
        for(int i=0;i<m;++i){
            if(board[i][0] == 'O'){
               board[i][0]='Y';
               mark_dfs(i,0,board);
            }
            if(board[i][n-1]=='O'){
                board[i][n-1]='Y';
                mark_dfs(i,n-1,board);
            }
        }
        
        for(int i=0;i<n;++i){
            if(board[0][i] == 'O'){
               board[0][i]='Y';
               mark_dfs(0,i,board);
            }
            if(board[m-1][i]=='O'){
                board[m-1][i]='Y';
                mark_dfs(m-1,i,board);
            }
        }
        
        for(int i=0;i<m;++i){
            for(int j=0;j<n;++j){
                if(board[i][j]=='O') board[i][j]='X';
                if(board[i][j]=='Y') board[i][j]='O';
            }
        }      
    }
};
矩阵里搜索单词

经典的四向搜索问题。

#define UP 0
#define DOWN 1
#define LEFT 2
#define RIGHT 3

bool backTrak(const vector<vector<char> > &board , const string& word, int row , int col,int pos,vector<vector<bool> >& record)
{
    if(row >= board.size() || row < 0 )return false;
    if(col >= board[row].size() || col < 0) return false;
    
    if( board[row][col] == word[pos] && pos <= word.size()-1)
    {
        record[row][col] = true;
        if(pos == word.size()-1 ) return true;
        if(row+1 < board.size() && false == record[row+1][col] && backTrak(board ,  word,  row+1 ,  col, pos+1,record)) return true;
         else if(row-1 >= 0 && false == record[row-1][col] && backTrak(board ,  word,  row-1 ,  col, pos+1,record)) return true;
         else if(col+1 < board[row].size() && false == record[row][col+1]&& backTrak(board , word,  row , col+1, pos+1,record))return true;
         else if(col-1 >=0 && false == record[row][col-1] && backTrak(board ,  word,  row ,  col-1, pos+1,record)) return true;
    }
    record[row][col] = false;
    return false;
}


class Solution {
public:
    bool exist(vector<vector<char> > &board, string word) {
         if(word.size() == 0 )return false;
         vector<vector<bool> > record(board.size(),vector<bool>(board[0].size(),false));
         
         for(int i = 0 ; i < board.size() ; i++)
         {
            for(int j = 0 ; j < board[i].size() ; j++)
            {
                if( backTrak(board ,  word,  i, j, 0,record) ) return true;
            }              
        }
        return false;        
    }
};
拷贝图

可以利用map同时起到判断是否第一次访问,以及记录当前节点的拷贝节点的地址的作用。

class Solution {
    map<int,Node*>copys;
public:
    Node* cloneGraph(Node* node) {
    if(node==NULL) return NULL;
     
    Node*pCurr=NULL;   
    //第一次访问当前节点,则拷贝一份该节点,并将其子节点放入。
    if(copys.count(node->val)==0){
        pCurr=new Node(node->val);
        copys[node->val]=pCurr;
        
        for(auto child:node->neighbors){
          pCurr->neighbors.push_back(cloneGraph(child));
        }
    }else{
        pCurr=copys.find(node->val)->second;
    }   
    return pCurr;
}};
太平洋大西洋出海口

分别标记能流入太平洋的位置以及能流入大西洋位置。二者均能流入则为所求

class Solution {
    //<i,j>为出海口,将所有可流入该出海口点标记为1
    void dfs_ocean(int i,int j,vector<vector<int>>&ocean,vector<vector<int>>& matrix){
        // 四个方向
        int direction_x[4]={i,i,i-1,i+1};
        int direction_y[4]={j-1,j+1,j,j};
        
        for(int k=0;k<4;++k){
            int x=direction_x[k];
            int y=direction_y[k];
            if(x>=0&&x<ocean.size()&&y>=0&&y<ocean[0].size()&&matrix[i][j]<=matrix[x][y] && ocean[x][y]==0){
              ocean[x][y]=1;
              dfs_ocean(x,y,ocean,matrix);
            }
        }
    }
    
public:
    vector<vector<int>> pacificAtlantic(vector<vector<int>>& matrix) {
        vector<vector<int>>ret;
        if(matrix.empty()) return ret;
        
        int m=matrix.size();
        int n=matrix[0].size();
        //find if flow to pacific
        //0 unsure , 1 can flow to pacific,-1 can not flow to pacific
        vector<vector<int>> pacific(m,vector<int>(n,0));
        auto atlantic=pacific;
        
        //初始化所有出海口
        for(int i=0;i<m;++i){
            pacific[i][0]=1;
            atlantic[i][n-1]=1;
        }
        
        for(int i=0;i<n;++i){
            pacific[0][i]=1;
            atlantic[m-1][i]=1;
        }
        
        for(int i=0;i<m;++i){
            dfs_ocean(i,0,pacific,matrix);
            dfs_ocean(i,n-1,atlantic,matrix);
        }
        
        for(int i=0;i<n;++i){
            dfs_ocean(0,i,pacific,matrix);
            dfs_ocean(m-1,i,atlantic,matrix);
        }       
        for(int i=0;i<m;++i){
            for(int j=0;j<n;++j){
                if(pacific[i][j]==1 && atlantic[i][j]==1){
                    vector<int>r={i,j};
                    ret.push_back(r);
                }
            }
        }
        
        return ret;      
        
    }
};
最长增长路径!!!

对每个位置,访问比自己大的邻接点,并且更新从当前位置出发能达到的最大增长长度

class Solution {
    vector<vector<int>>mark;
    
    int dfs(int i,int j,vector<vector<int>>&mark,const vector<vector<int>>&matrix){
        int m=matrix.size();
        int n=matrix[0].size();
        if(mark[i][j]==0){
            vector<int> delta_x={-1,1,0,0};
            vector<int> delta_y={0,0,1,-1};
            mark[i][j]=1;
            for(int k=0;k<4;++k){
                int x=i+delta_x[k];
                int y=j+delta_y[k];
                if(x>=0&&x<m&&y>=0&&y<n&&matrix[x][y]>matrix[i][j]){
                    if(mark[x][y]==0){
                       mark[i][j]=max(mark[i][j], dfs(x,y,mark,matrix)+1);
                    }else{
                        mark[i][j]=max(mark[i][j],mark[x][y]+1);
                    }
                }
            }
        }
        return mark[i][j];
    }
    
public:
    int longestIncreasingPath(vector<vector<int>>& matrix) {
        if(matrix.empty()) return 0;
        mark.resize(matrix.size(),vector<int>(matrix[0].size(),0));
        int ret=0;
        for(int i=0;i<matrix.size();++i){
            for(int j=0;j<matrix[0].size();++j){
                ret=max(ret,dfs(i,j,mark,matrix));
            }
        }
        return ret;
    }
};
单词接龙

通过BFS按层遍历,思路比较直接

void getNextWords(const string &currWord ,  queue<string>& nextLayer ,unordered_set<string>& wordDict)
{

   for(int i = 0 ; i < currWord.size() ;++i)  //生产新单词策略是遍历整个单词,并且依次将该单词各个字符变换为26个字母中其他字符,再
   {                                          //在字典中查找该单词是否存在
       string temp = currWord;
       for(char c = 'a' ; c <='z' ;++c)
       {
           if(c != currWord[i])
           {
               temp[i] = c;
               if(1 == wordDict.count(temp))
               {
                   nextLayer.push(temp);
                   wordDict.erase(temp);
               }
           }
       }
   }
}

class Solution {
public:
    int ladderLength(string beginWord, string endWord, unordered_set<string>& wordDict) {
        auto myDict = wordDict;
        queue<string>currLayer , nextLayer;
        int nLayer = 0;
        currLayer.push(beginWord);
        ++nLayer ;
        string currWord;
        
        while(true) //广度优先搜索
        {
            while(!currLayer.empty())
            {
                currWord = currLayer.front();
                getNextWords(currWord , nextLayer,myDict);//将当前单词可推导出的且尚未使用的词汇加入下一层
                currLayer.pop();
            }
            
            if(nextLayer.empty()) return 0; //无法推导出最终词汇
            
            ++nLayer;
            while(!nextLayer.empty())
            {
                currWord = nextLayer.front();
                if(currWord == endWord) return nLayer; //检查最终词汇是否已经被推导出来
                
                currLayer.push(currWord);
                nextLayer.pop();
            }
        }
    }
};
课表问题1

本质是寻找一个图里是否有环
构建图可以使用邻接矩阵等多种方式,以下代码为简单,直接使用数组记录一个节点的父节点和子节点。
BFS判断是否有环方法:
访问一个节点时,只有其所有父节点已经被访问过,才访问当前节点。从而保证不会访问成环。
此处每次访问某节点时,更新其子节点里记录父节点被访问的个数。
最终如果所有节点均被访问到,证明图里不存在环。

DFS判断是否有环方法:
记录当前路径上被访问的序列,如果发现当前节点的子节点已经被访问,则证明存在环。

struct Node{
    int c;
    vector<int>pres;
    vector<int>nexts;
};

class Solution {
    bool bfs_no_loop(vector<Node>&graph)
    {
        vector<bool>visited(graph.size(),false);
        queue<Node*>q;
        vector<int>pres_visited_num(graph.size(),0);
        
        for(int i=0;i<graph.size();++i){
            if(graph[i].pres.size()==0){
               visited[i]=true;
               for(auto child:graph[i].nexts){
                   ++pres_visited_num[child];
                   q.push(&graph[child]);
               }
            }
        }
        while(!q.empty()){
             auto curr=q.front();
             q.pop();
             if(visited[curr->c]==false && pres_visited_num[curr->c]==curr->pres.size()){
                 visited[curr->c]=true;
                for(auto child:curr->nexts){
                  if(!visited[child]){
                      ++pres_visited_num[child];
                      q.push(&graph[child]);
                  }
               }
             }
          }
        for(auto v:visited){
            if(!v) return false;
        }
        return true;
    }
    
    
    vector<bool>visited;
    bool dfs_no_loop(Node&root,vector<Node>&tree,vector<bool>&visited,vector<bool>&curr_path_visit){
        //叶子节点
        if(root.nexts.size()==0) return true;
        else{
            for(auto child:root.nexts){
                if(visited[child]) continue;
                //父节点出现在后继中,即存在环
                if(curr_path_visit[child] == true) return false;
                else{
                    curr_path_visit[child]=true;
                    if(!dfs_no_loop(tree[child],tree,visited,curr_path_visit)){
                        return false;
                    }else{
                       visited[child]=true; 
                    }
                    curr_path_visit[child]=false;
                }
            }
        }
        return true;
    }
public:
    bool canFinish(int numCourses, vector<vector<int>>& prerequisites) {
        if(numCourses ==0) return true;
        
        visited.resize(numCourses,false);
        vector<Node>tree(numCourses);
        for(int i=0;i<tree.size();++i)tree[i].c=i;
        
        for(auto &pre:prerequisites){
            tree[pre[0]].pres.push_back(pre[1]);
            tree[pre[1]].nexts.push_back(pre[0]);
        }

        
        
        ///use bfs ------
        
        return bfs_no_loop(tree);
        
        for(auto &r:tree){
            //无前继则为某可子树的根节点
            if(r.pres.size()==0){
                vector<bool>curr_path_visit(tree.size(),false);
                curr_path_visit[r.c]=true;
                if(!dfs_no_loop(r,tree,visited,curr_path_visit)){
                    return false;
                }else{
                    visited[r.c]=true;
                }
            }
        }
        
        for(auto i:visited){
            if(!i) return false;
        }
        return true;
   }
};
课表问题2

采用课表1中的BFS方法,访问所有节点,其顺序即为可行的上课顺序

struct Node{
    int pres_num=0;
    vector<int>childs;
};

vector<int>BFS(vector<Node>&graph){
   
    queue<int>q;
    vector<bool>visit(graph.size(),false);
    vector<int>ret;
    //记录节点的先继节点已经访问的数目
    vector<int>pres_visit_num(graph.size(),0);
    
     //无先继节点可直接访问
    for(int i=0;i<graph.size();++i){
        if(graph[i].pres_num ==0){
            visit[i]=true;
            ret.push_back(i);
            //当前节点的子节点的父节点访问数+1
            for(auto child:graph[i].childs){
                ++pres_visit_num[child];
                q.push(child);
            }
        }
    }
    
    while(!q.empty()){
        auto curr=q.front();
        q.pop();
        //当前节点未访问 且其先继节点均访问完毕
        if(visit[curr]==false && pres_visit_num[curr]==graph[curr].pres_num){
            visit[curr]=true;
            ret.push_back(curr);
            for(auto child:graph[curr].childs){
                if(visit[child]==false){
                    ++pres_visit_num[child];
                    q.push(child);
                }
            }
        }       
    }
    bool all_visit=true;
    for(auto i:visit){
        if(!i) {
            all_visit=false;
            ret.clear();
            break;
        }
    }
    return ret;
}

class Solution {
public:
    vector<int> findOrder(int numCourses, vector<vector<int>>& prerequisites) {
        vector<Node>graph(numCourses);

        for(auto &pre:prerequisites){
            graph[pre[0]].pres_num++;
            graph[pre[1]].childs.push_back(pre[0]);
        }
        return BFS(graph);
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值