高效面试之DFS

一 ,深度优先搜索代码模板

 void dfs()
 {
        if()
        {
           return;//收敛或终止条件
        }
      if(可以剪枝) return 
        for()
        {
            if()
            {
                dfs();
            }
        }
 }

题目索引
    1. 给定一个矩阵,求两个目标之间独立路径数
    2.  n*n 八皇后棋盘,求 n 不同时,共有多少解法
    3.  给定一串数字,求能够构成合法 IP 的数目
    4.  给定一个候选数和一个数组,从数组选出几个数相加为候选数,找出所有可能的组合解
    5.  一个放好了部分数字的数独棋牌,求有多少种方法
    6.  迷宫问题


真题:
题目1.独立路径数
分析:

设 f[i][j],表示从起点 (1; 1) 到达 (i; j) 的路线条数,则状态转移方程为:
f[i][j]=f[i-1][j]+f[i][j-1]

1.dfs
int uniquePaths(int m, int n) 
{
    if (m < 1 || n < 1) return 0; // 终止条件
    if (m == 1 && n == 1) return 1; // 收敛条件
    return uniquePaths(m - 1, n) + uniquePaths(m, n - 1);// 修改 path
}
但是这样会超时。
2.备忘录
int getPaths(int x, int y)
{
    if (f[x][y]>0) return f[x][y];
    else return f[x][y]=dfs(x,y);
}

参考代码:

class Solution {
public:
    int uniquePaths(int m, int n) {
       /* 方法一: 带备忘录的递归*/
        this->f = vector<vector<int> >(m + 1, vector<int>(n + 1, 0));
         return dfs(m, n);
     }
private:
    vector<vector<int> > f; // 缓存
    int  dfs(int x, int y) {
        if (x < 1 || y < 1) return 0; // 数据非法,终止条件
        if (x == 1 && y == 1) return 1; // 回到起点,收敛条件
         return getPaths(x - 1, y) + getPaths(x, y - 1);
    }
    int  getPaths(int x, int y) {
        if (f[x][y] > 0) return f[x][y];
        else return f[x][y] = dfs(x, y);
    }
      /*方法二:使用动态规划.由于只能往下或者往右走,因此(i, j)只会由(i - 1, j)或者(i, j - 1)到达。
      int i,j;
      int paths[m][n];
      memset(paths,0,sizeof(int)*m*n);
      for(i=0;i<m;i++)
           paths[i][0]=1;
      for(j=0;j<n;j++)
           paths[0][j]=1;
      for(i=1;i<m;i++)
      {
          for(j=1;j<n;j++)
              paths[i][j]=paths[i-1][j]+paths[i][j-1];//(i, j)只会由(i - 1, j)或者(i, j - 1)到达
      }
      return paths[m-1][n-1];
    }
    */
};

Now consider if some obstacles are added to the grids. How many unique paths would there be?


class Solution {
public:
    int uniquePathsWithObstacles(vector<vector<int> > &obstacleGrid) {
        int m=obstacleGrid.size();//行
        int n=obstacleGrid[0].size();//列
        // 0 行和0 列未使用
        this->paths = vector<vector<int> >(m + 1, vector<int>(n + 1, 0));
        return dfs(obstacleGrid,m,n);
    }
private:
    vector<vector<int> > paths;
   
    int dfs(const vector<vector<int> > &obstacleGrid,int x,int y)
    {
        if(x<1 || y<1) //数据非法
            return 0;
        if(obstacleGrid[x-1][y-1]) //(x,y)是障碍物,这一句一定要写在收敛条件前面
            return 0;
        if(x==1 && y==1) //收敛
            return 1;
        return getPaths(obstacleGrid,x-1,y)+getPaths(obstacleGrid,x,y-1);
    }
    int getPaths(const vector<vector<int> > &obstacleGrid,int x, int y)
    {
        if(paths[x][y]>0)
            return paths[x][y];
        else
            return paths[x][y]=dfs(obstacleGrid,x,y);
    }
};

2.八皇后

Follow up for N-Queens problem.

Now, instead outputting board configurations, return the total number of distinct solutions.


给定一个n*n的棋盘,问有多少种解,N-Queens需要给出具体的路径,本题只需要给出总个数


分析:

result 记录皇后摆放情况 result[i]表示第i行皇后的位置

cur标记当前位置(行)

res 解的总数

思路:

按行放置皇后,如果放到了第N行,则解数+1.

判断皇后放在当前cur行的val列是否可以  isok(vector<int> &result,int cur,int val);

深度搜索每一种可能解

dfs()

{

for()//就如第0行,每个位置都可以放isok,那么每一种情况都需要判断 所以for里面有dfs

{

    if(isok)

    {

        dfs(,cur+1);//cur标记当前位置,改变路径

     }

}

}



代码:


class Solution {

public:

    int totalNQueens(int n) {

        int ret = 0;//解的个数

  vector<int> result(n); //result[n]=m,表示第n行的皇后放在第m列

        dfs(ret, result, 0);

        return ret;

    }

 void dfs(int& ret, vector<int>& result, int cur)

    {

        if(result.size() == cur)

        {

            ++ret;//最后一行可以放,找到一个解

        }

        else

        {

            //需找当前行,可以放皇后的列,如果找到,则放置下一行的皇后dfs

            for(int i = 0; i < result.size(); ++i)

            {

               

                if(isOK(result, cur, i))

                {

                    result[cur] = i;

                    dfs(ret, result, cur+1);

                }

            }

        }

    }

    //cur表示在第几行,val表示皇后的列取值,isok来判断cur行的皇后放在val列是否可以

    bool isOK(vector<int>& result, int cur, int val)

    {

        for(int i = 0; i < cur; ++i)

        {

            if(val == result[i]) ///两个皇后在同一列上

                return false;

            else if(abs(val - result[i]) == abs(i - cur)) //两个皇后在同一对角线上,两个皇后的列值相减等于行值相减,则在同一对角线

                return false;

        }

        return true;

    }

};


3.有效IP地址数目


Given a string containing only digits, restore it by returning all possible valid IP address combinations.

For example:
Given "25525511135",

return ["255.255.11.135", "255.255.111.35"]. (Order does not matter)


分析:
1.给定的字符串,只能分成4段,每段值必须小于255,check(beg,end)
2.我们进行下一步DFS的条件是什么?收敛条件是什么?
我们可以按端搜索,那端的长度取多少?最多取3,每取一个值就判断一下check(start,i),是否可以作为单独的一段,可以的话,就搜索下一段DFS,
那收敛条件就是,已经遍历到了字符串的尾部[start == s.size()],到了段尾[dep == maxDep],则找到一个合法的ip 

class Solution {

private:
    vector<string> ret;
    int pos[4];//pos[n]=i,第n+1端从i个字符串开始,方便输出ip地址
public:
    bool  check(string &s, int beg, int end)
    {
        string ip(s, beg, end - beg + 1);
        if (ip.size() == 1)
            return "0" <= ip && ip <= "9";
        else if (ip.size() == 2)
            return "10" <= ip && ip <= "99";
        else if (ip.size() == 3)
            return "100" <= ip && ip <= "255";
        else
            return false;
    }
   
    void dfs(int dep, int maxDep, string &s, int start)
    {
        if (dep == maxDep)
        {
            if (start == s.size())
            {
                int beg = 0;
                string addr;
                //根据记录的每一段ip地址的起始位置,加上.号,输出ip地址
                for(int i = 0; i < maxDep; i++)
                {
                    string ip(s, beg, pos[i] - beg + 1);
                    beg = pos[i] + 1;
                    addr += i == 0 ? ip : "." + ip;//第一段不要.
                }
                ret.push_back(addr);
            }
            return;
        }
       
        for(int i = start; i < s.size(); i++)
            if (check(s, start, i))
            {
                pos[dep] = i;//记录第i段的起始位置
                dfs(dep + 1, maxDep, s, i + 1);
            }
    }
   
    vector<string> restoreIpAddresses(string s) {
        ret.clear();
        dfs(0, 4, s, 0);
        return ret;
    }
};

4.组合数

For example, given candidate set 2,3,6,7 and target 7

A solution set is: 

[7]  

[2, 2, 3]  

给定一个候选数和一个数组,找出所有可能的组合解



class Solution {
/*
gap 差距
*/
public:
    vector<vector<int> > combinationSum(vector<int> &candidates, int target) {
        sort(candidates.begin(), candidates.end());
        vector<vector<int> > result; // 最终结果
        vector<int> intermediate; // 中间结果
        dfs(candidates, target, 0, intermediate, result);
        return result;
    }
private:
    /*dfs(),gap表示离到达target的值,start表示从 candidate中的几个数开始本次搜索*/
    void dfs(vector<int>& candidates, int gap, int start, vector<int>& intermediate,vector<vector<int> > &result)
    {
        if (gap == 0) { // 找到一个合法解
            result.push_back(intermediate);
            return;
        }
        for (size_t i = start; i < candidates.size(); i++) { // 扩展状态
            if (gap < candidates[i]) return; // 剪枝
            intermediate.push_back(candidates[i]); // 执行扩展动作
            dfs(candidates, gap - candidates[i], i, intermediate, result);
            intermediate.pop_back(); // 撤销动作
        }
    }
};

5.数独

Write a program to solve a Sudoku puzzle by filling the empty cells.

Empty cells are indicated by the character '.'.

You may assume that there will be only one unique solution.


A sudoku puzzle...



class Solution {

public:

    bool isValidSudoku(vector<vector<char> > &board, int x, int y) {

        int row, col;

       

        // Same value in the same column?

        for (row = 0; row < 9; ++row) {

            if ((x != row) && (board[row][y] == board[x][y])) {

                return false;

            }

        }

       

        // Same value in the same row?

        for (col = 0; col < 9; ++col) {

            if ((y != col) && (board[x][col] == board[x][y])) {

                return false;

            }

        }

       

        // Same value in the 3 * 3 block it belong to?

        for (row = (x / 3) * 3; row < (x / 3 + 1) * 3; ++row) {

            for (col = (y / 3) * 3; col < (y / 3 + 1) * 3; ++col) {

                if ((x != row) && (y != col) && (board[row][col] == board[x][y])) {

                    return false;

                }

            }

        }

       

        return true;

    }

   

    bool internalSolveSudoku(vector<vector<char> > &board) {

        for (int row = 0; row < 9; ++row) {

            for (int col = 0; col < 9; ++col) {

                if ('.' == board[row][col]) {

                    for (int i = 1; i <= 9; ++i) {

                        board[row][col] = '0' + i;

                       

                        if (isValidSudoku(board, row, col)) {

                            if (internalSolveSudoku(board)) {

                                return true;

                            }

                        }

                       

                        board[row][col] = '.';

                    }

                   

                    return false;

                }

            }

        }

       

        return true;

    }

   

    void solveSudoku(vector<vector<char> > &board) {

        internalSolveSudoku(board);

    }

};


6.笨笨熊找回家的路


#include <stdio.h>

#include <stdlib.h>
#include <string.h>
char map[10][10]={0};
char vis[10][10]={0};//访问记录
int d[4][2]={-1,0,1,0,0,1,0,-1};//4个方向
int queue[10*10];
int m,n;//行列
bool BFS(int x,int y)
{
 int front=0,rear=0;//队列头和尾
 int cur;//当前节点坐标
 cur=x*m+y;
 vis[x][y]=1;
 queue[rear++]=cur;//当前节点接入队尾
 while(front!=rear)
 {
  cur=queue[front++];//从队首取元素
  x=cur/n;y=cur%n;
  for(int k=0;k<4;k++)//当前节点的4个领近节点加入队列
  {
   int nx=x+d[k][0];//nx表示next_x
   int ny=x+d[k][1];
   if(nx>=0 && nx<m && ny>=0 && ny<n && !vis[nx][ny] && map[nx][ny] == 'H')
   {
    return true;
   }
   if(nx>=0 && nx<m && ny>=0 && ny<n && !vis[nx][ny] && map[nx][ny] == '-')
   {
    int v=nx*m+ny;
    queue[rear++]=v;//接入队列尾
    vis[nx][ny]=1;
   }
  }
 }
}
bool DFS(int x,int y)
{
    if(map[x][y]=='H')
        return true;
    for(int k=0;k<4;k++)
    {
        int nx=x+d[k][0];
        int ny=y+d[k][1];
        if(nx>=0 && nx<m && ny>=0 && ny<n && !vis[nx][ny] && map[nx][ny] == '-')
        {
            vis[nx][ny]=1;
            DFS(nx,ny);
   vis[nx][ny]=0;
        }
    }
}
void main()
{
 
 int i,j,start_x,start_y,res;
 scanf("%d\n",&m);//行
 scanf("%d\n",&n);//列
 for(i=0;i<m;i++)
 {
  for(j=0;j<n;j++)
   scanf("%c",&map[i][j]);//别忘了&
 }
 for(i=0;i<m;i++)//需找B的坐标
 {
  for(j=0;j<n;j++)
  {
   if(map[i][j]=='B')
   {
    start_x=i;
    start_y=j;
    break;
   }
  }
 }
 res=DFS(start_x,start_y);
 if(res)
  printf("Y\n");
 else
  printf("N\n");
}
 void dfs()
 {
        if()
        {
           return;//收敛或终止条件
        }
      if(可以剪枝) return 
        for()
        {
            if()
            {
                dfs();
            }
        }
 }


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值