二叉树里已经大量使用了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);
}
};