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:

11110110101100000000

Answer: 1

Example 2:

11000110000010000011

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!),一般只有数据量较小的时候可以使用。

时间: 2024-08-27 15:41:41

Leetcode拾萃(算法篇)——暴力破解(DFS、BFS、permutation)的相关文章

DFS BFS 示例 以及BFS部分算法代码

上一篇写了DFS的邻接矩阵和邻接表的,这篇再写一下BFS的 先大概讲一下BFS , 全名广度优先搜索,没有回溯和探查,逐层遍历,采用队列实现 先访问当前顶点v, 然后访问v的各个未被访问过的邻接顶点,然后在一次访问v的各个邻接顶点的未被访问的邻接顶点 代码实现如下 : void BFS (Graph &G , int v ) { int i , w , n = G.Number(); //取出顶点个数 bool * visit = new bool [n] ; for( i =0 ;i< n

网络攻防研究第001篇:尝试暴力破解某高校研究生管理系统学生密码

前言 如果你是在校大学生,而且还对网络攻防比较感兴趣的话,相信你最开始尝试渗透的莫过于所在院校的学生管理系统.因为一般来说这样的系统往往比较薄弱,拿来练手那是再合适不过的了.作为本系列的第一篇文章,我将会利用暴力破解的方式,尝试对某高校的研究生管理系统的学生密码进行破解.由于这个管理系统的网站属于该高校的内网资源,外网是无法访问的,因此大家就不要尝试按照文中的内容来对文中出现的网址进行访问了.利用本文所论述的暴力破解思想,可以帮助大家更好地认识我们的网络,也有助于了解目标网站是否安全.那么在这里

DVWA学习篇一:暴力破解

1      测试环境介绍 1.使用Burpsuit工具进行暴力破解 2.测试环境为OWASP环境中的DVWA模块 2      测试步骤 2.1  设置浏览器代理 首先运行Burpsuit工具,设置监听地址和端口,然后在浏览器里面设置好代理IP和地址.如下图: 2.2  抓取登陆页面数据 开启Burpsuit拦截功能,抓取登陆页面登陆账号和密码,这个密码实际是错误的,后面我们需要爆破的就是这个密码. 2.3  发送到入侵模块(Intruder模块) 在刚刚拦截到的请求页面右击选择发送到入侵模块

WEB安全第七篇--终结篇考验逻辑思维:逻辑漏洞大汇总(越权、会话逻辑、业务逻辑、暴力破解)

零.前言 最近做专心web安全有一段时间了,但是目测后面的活会有些复杂,涉及到更多的中间件.底层安全.漏洞研究与安全建设等越来越复杂的东东,所以在这里想写一个系列关于web安全基础以及一些讨巧的payload技巧以便于备忘.不是大神.博客内容非常基础,如果真的有人看而且是大牛们,请不要喷我,欢迎指正我的错误(水平有限). 一.越权: 1.本质: 某账户拥有了超出它应有权限范围外的操作许可权. 2.分类: (1)水平越权:横向拥有了同等安全等级.密级等衡量标准账户的权限. (2)垂直越权:纵向拥有

Android中锁屏密码算法解析以及破解方案

一.前言 最近玩王者荣耀,下载了一个辅助样本,结果被锁机了,当然破解它很简单,这个后面会详细分析这个样本,但是因为这个样本引发出的欲望就是解析Android中锁屏密码算法,然后用一种高效的方式制作锁机恶意样本.现在的锁机样本原理强制性太过于复杂,没意义.所以本文就先来介绍一下android中的锁屏密码算法原理. 二.锁屏密码方式 我们知道Android中现结单支持的锁屏密码主要有两种: 一种是手势密码,也就是我们常见的九宫格密码图 一种是输入密码,这个也分为PIN密码和复杂字符密码,而PIN密码

防止ssh暴力破解linux密码

网上看到一篇安全方面的文章,常用的网站收藏无法收藏,于是放这里先.具体功能还未测试,请慎用. 下面是博客原文转载,感谢作者辛苦劳动: 服务器在互联网中,每天都有些没事儿干的人在你的机器端口扫来扫去,让管理员非常烦恼.本文描述一个方法,利用本文介绍的脚本结合iptables服务一并使用,用来防止公网计算机通过ssh进行用户口令暴力破解.目前稳重脚本已经在其他项目的实际运营环境中得到验证. 脚本内容: #!/bin/bash # 防SSH密码暴力破解脚本 # 通过分析secure日志文件使用ipta

Wireshark黑客发现之旅(4)——暴力破解

Wireshark黑客发现之旅(4)——暴力破解 聚锋实验室 · 2015/08/10 10:33 作者:Mr.Right.K0r4dji 申明:文中提到的攻击方式仅为曝光.打击恶意网络攻击行为,切勿模仿,否则后果自负. 一.个人观点 暴力破解,即用暴力穷举的方式大量尝试性地猜破密码.猜破密码一般有3种方式: 1.排列组合式:首先列出密码组合的可能性,如数字.大写字母.小写字母.特殊字符等:按密码长度从1位.2位……逐渐猜试.当然这种方法需要高性能的破解算法和CPU/GPU做支持. 2.字典破解

Python黑客编程2 入门demo--zip暴力破解

Python黑客编程2 入门demo--zip暴力破解 上一篇文章,我们在Kali Linux中搭建了基本的Python开发环境,本篇文章为了拉近Python和大家的距离,我们写一个暴力破解zip包密码的小程序.这个例子来自于<Voilent Python>一书,这也是一本python黑客编程的入门书,推荐大家看一看. 照顾没有接触过Python编程的同学,行文可能会有些啰嗦. 废话少说,我们进入正题. 2.1 准备基本材料 在/home/ziptest/目录下,我创建了两个文件,一个test

软件破解入门(暴力破解CrackMe)

所谓暴力破解,就是通过修改汇编代码进而控制程序的运行流程,达到不需注册码也能正常使用软件的目的.相对于解出算法进而编写注册机,暴破的技术含量是比较低的.但也正是因为一本05年的杂志上介绍“暴力破解”的文章,让我入了这个大坑.近来想重拾调试器,就先从最简单的CrackMe入手,熟练一下各工具方法. 下载CrackMe3文件(我用的是看雪<加密与解密>中的CFF CrackMe #3 程序  http://pan.baidu.com/s/1dD9v9x3 ). 1.查看此程序是否加壳.加壳的话还得