LEETCODE拾萃(算法篇)——暴力破解(DFS、BFS、PERMUTATION)

引言

现在互联网的招工流程,算法题是必不可少的,对于像我这种没搞过ACM的吃瓜群众,好在有leetcode,拯救我于水火。于是乎,断断续续,刷了一些题,其中一些题还是值得细细品味的,现把一些问题整理一下,有些解法是我自己写的,也有些解法是参考了discuss中的答案,当做是秋招的一个小小总结。由于水平有限,代码写得并不好,选的题目也只能用于入门,希望大家见谅。

暴力破解

暴力破解,据我个人理解,就是遍历整棵搜索树,没有删繁就简,紧是单单的搜索和匹配。

1、基本暴力破解:对于最基本的暴力破解,就是取出所有的可能,和题目中的条件相比较,直到找到符合题意的答案。

举例:鸡兔同笼问题,已知x个头,y只脚,问兔和鸡的个数。

解法:从0~x个枚举鸡(或兔)的只数,计算脚的个数是否为y。

2、DFS:深度优先搜索。从原始状态出发,选择任一状态,沿这个状态一直走到没有下一个状态为止,这时候进行回溯,到当前状态的上一个状态,选择另一个状态进行遍历。DFS在代码实现上常使用递归方法来实现。

举例1:Leetcode 129. Sum Root to Leaf Numbers

Given a binary tree containing digits from 0-9 only, each root-to-leaf path could represent a number.

An example is the root-to-leaf path 1->2->3 which represents the number 123.

Find the total sum of all root-to-leaf numbers.

For example,

1
/ \
2 3

The root-to-leaf path 1->2 represents the number 12.
The root-to-leaf path 1->3 represents the number 13.

Return the sum = 12 + 13 = 25.

解法:最直观的思路,就是找到所有路径,记录路径上所有的值,并且求和,代码如下:

class Solution {
public:
    int ret = 0;
    int sumNumbers(TreeNode* root) {
        if(root == nullptr) return ret;
        getSum(root,root->val);
        return ret;
    }
    //递归调用
    void getSum(TreeNode* root,int preval){
        if(root->left==nullptr&&root->right==nullptr){
            ret+=preval;
            return;
        }
        if(root->left)getSum(root->left,preval*10+root->left->val);
        if(root->right)getSum(root->right,preval*10+root->right->val);
        return;
    }
};

除此之外,DFS还可常用于寻找所有可达状态:

举例二:Leetcode200. Number of Islands

Given a 2d grid map of '1's (land) and '0's (water), count the number of islands. An island is surrounded by water and is formed by connecting adjacent lands horizontally or vertically. You may assume all four edges of the grid are all surrounded by water.

Example 1:

11110
11010
11000
00000

Answer: 1

Example 2:

11000
11000
00100
00011

Answer: 3

思路:在这个题目中,认为所有为1(上下左右)组成的是一个岛,求给出数组中岛的个数。主要的思想就是从任一个为值1的点开始,将所有能达到的点都标记为已访问,如果所有可达的点都为已访问或0,说明这个岛中所有的数据已经被搜索完毕,则可以去找下一个岛。最后可以得到岛的个数。在下面的代码中,已访问的点标记为0,如果相邻的点的值为1,说明可扩展,则进行扩展。

解法:

class Solution {
public:
    int dir[4][2]={{-1,0},{1,0},{0,-1},{0,1}};
    int m,n,ret = 0;
    int numIslands(vector<vector<char>>& grid) {
        if(grid.empty())return 0;
        m = grid.size(),n = grid[0].size();
        for(int i=0;i<m;i++){
            for(int j=0;j<n;j++){
                if(grid[i][j]=='1'){
                    dfs(i,j,grid);
                    ++ret;
                }
            }
        }
        return ret;
    }
    
    void dfs(int s,int t,vector<vector<char>>& grid) {
     //如果已经访问过或超界,则不需要再扩展
        if(s<0||s>=m||t<0||t>=n||grid[s][t]=='0'){
            return;
        }
     //标记为已经访问过
        grid[s][t]='0';
     //向下一个状态扩展,进行递归访问
        for(int i=0;i<4;i++){
            dfs(s+dir[i][0],t+dir[i][1],grid);
        }
        return;
    }
};

3.BFS:广度优先搜索,是和DFS相对的一种方法,从初始状态开始,用“辐射状”的形式遍历其余所有状态。对于状态的遍历,BFS和DFS能使用的场景有很多有很多相似之处,例如上面的Leetcode129,就也有BFS解法。

BFS一般使用队列实现:将初始状态放到队列中,当出队的时候,将出队状态的所有未访问过的后续状态放到队列中进行后续访问。BFS的一个典型应用是用于(最短)路径的寻找,从初始状态到目标状态,因为BFS可以保证每次遍历的时候从初始状态到当前队列中的状态的步数是相同的;另一个典型应用是对于某种“分层”的场合,对于某一状态,其后续的所有状态具有相同的性质。

举例1:LeetCode 490,但这个题是付费的,我只看过解法,并没有做过,迷宫类的题是BFS最经典的应用。题目和解法可以参考我一直比较崇拜的一个博主的博客:https://www.cnblogs.com/grandyang/p/6381458.html

** 举例2:LeetCode 542. 01 Matrix**

Given a matrix consists of 0 and 1, find the distance of the nearest 0 for each cell.

The distance between two adjacent cells is 1.

Example 1:
Input:
0 0 0
0 1 0
0 0 0

Output:
0 0 0
0 1 0
0 0 0

Example 2:
Input:
0 0 0
0 1 0
1 1 1

Output:
0 0 0
0 1 0
1 2 1

Note:

  1. The number of elements of the given matrix will not exceed 10,000.
  2. There are at least one 0 in the given matrix.
  3. The cells are adjacent in only four directions: up, down, left and right.

解法:此题即是“分层”场景的一个典型应用”、对于每一个0,其周围的非0点的状态相同:周围最近的0距离为1;再以这些1为中心,扩散出去的第二层具有相同的性质。

class Solution {
public:
    int dir[4][2]={{0,1},{0,-1},{-1,0},{1,0}};
    #define pii pair<int,int>
    #define mp make_pair
    vector<vector<int>> updateMatrix(vector<vector<int>>& matrix) {
        int m = matrix.size();
        if(m==0)return matrix;
        int n = matrix[0].size();
        queue<pii> q;
        for(int i=0;i<m;i++){
            for(int j=0;j<n;j++){
                if(matrix[i][j]==0){
                    q.push(mp(i,j));
                }else{
                    matrix[i][j]=INT_MAX;
                }
            }
        }
        while(!q.empty()){
            pii tmp = q.front();
            q.pop();
            for(int k=0;k<4;k++){
                int a = tmp.first+dir[k][0],b = tmp.second+dir[k][1];
                if(a<0||a>=m||b<0||b>=n)continue;
                if(matrix[a][b]<=matrix[tmp.first][tmp.second]) continue;
                matrix[a][b]=min(matrix[a][b],matrix[tmp.first][tmp.second]+1);
                q.push({a,b});
            }
        }
        return matrix;
    }
};

举例3:102. Binary Tree Level Order Traversal

BFS另一个典型应用就是用于树或图的层次遍历,这个比较基本,此处不再赘述。

4、Permutation:即全排列,可以获取1~n的全排列

在C++中,可以使用STL里面的接口来实现,例如求一个数组的全排列:

class Solution {
public:
    vector<vector<int>> permute(vector<int>& nums) {
        sort(nums.begin(),nums.end());
        vector<vector<int>> ret;
        ret.push_back(nums);
        while(next_permutation(nums.begin(),nums.end())){
            ret.push_back(nums);
        }
        return ret;
    }
};

这个函数在使用之前要注意,需要将数组排序,这样才能检测出来当前的排列是否生成过。

除此之外,permutation也可以用来求子集(m中选k个),即使用m的全排列的前k个。

permutation的复杂度为O(n!),一般只有数据量较小的时候可以使用。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 210,978评论 6 490
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 89,954评论 2 384
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 156,623评论 0 345
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,324评论 1 282
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,390评论 5 384
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,741评论 1 289
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,892评论 3 405
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,655评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,104评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,451评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,569评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,254评论 4 328
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,834评论 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,725评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,950评论 1 264
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,260评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,446评论 2 348

推荐阅读更多精彩内容