算法-蛮力法

蛮力法的主要思想就是用最简单的思路解决问题,一般性能不好,但仍然很重要。

  1. 理论上蛮力法可以解决可计算领域的各种问题
  2. 蛮力法解决较小规模问题是可接受的,如果设计一个更高效算法代价不值得
  3. 蛮力法可以作为时间性能的底线,来衡量更高效的算法

查找问题中的蛮力法

顺序查找

按照顺序进行查找,一般可以对算法进行适当优化,如设置哨兵等,能够改进时间性能,但只能减少系数,而数量级不会改变。

  1. 两数之和 - 简单 代码
  2. 剑指 Offer 53 - II. 0~n-1中缺失的数字 - 简单 代码

串匹配问题

串匹配定义:给定两个串S=”s1s2s……n“和T=”t1t2t……m“,在主串S中查找子串T的过程称为串匹配(也称模式匹配),T称为模式

BF算法

绝对的蛮力:从主串S的第一个字符开始和模式T的第一个字符进行比较,若相等,则继续比较二者的后续字符;若不相等,则从主串S的第二个字符开始和模式T的第一个字符进行比较,重复上述过程,若T中的字符串全部比较完毕,则说明本趟匹配成功;若最后一轮匹配的其实位置是n-m,则主串S中剩下的字符不足够匹配整个模式T,匹配失败。

KMP算法

基本思想是:主串不进行回溯
在这里插入图片描述

画了一个简图,对于主串S和子串T,在叉号位置(即j)不一致了,此时T的子串1(abcd)和子串2(abcd)内容是一致的,将子串1的下一位(即星号所在位置k)移到叉号所在位置,就可以继续进行比较。这个思路是不是就可以快速提高匹配效率。

所以这个算法需要先计算出不匹配字符前面的字符串,即是真前缀又是真后缀的最长子串。将这个子串长度加一便是当前字符不匹配时,需要右移的到T的字符的位置。我们将这个值记录到next数组中。

next[j]的值意味着当j不匹配的时候,从T的第k个字符进行比较。

计算next[j]的方法:假设next[j]的值是k

  1. 如果t[k]=t[j],那么next[j+1]=k+1
  2. 如果t[k]≠t[j],则需要查看t[next[k]]是否等于t[j],如果相等,则next[j+1]=next[k]+1,否则继续往前追溯。这么做的原因是next里是算的真前缀和真后缀,所以t[j]必然是要参加比较的,而真前缀已经在前面的字符串里计算好了,直接使用即可。

所以最终算法思路为,先计算T的next,然后将S和T进行比较,当在T的位置j不一致的时候,将T滑到next[j]的位置,继续比较。

  1. 686. 重复叠加字符串匹配 - 简单 代码

排序问题中的蛮力法

选择排序

扫描整个序列,找到整个序列的最小记录和序列中的第一个记录交换,从而将最小记录放到它在有序区的最终位置上,然后再从第二个记录开始扫描序列,重复这个过程。

  1. 912. 排序数组- 中等 代码

起泡排序

起泡排序开始的时候扫描整个序列,扎起扫描过程中两两比较相邻记录,如果反序则交换,最终,最大记录就被沉到了序列的最后一个位置,第二趟扫描将第二大记录沉到了导数第二个位置,重复操作,知道n-1趟扫描后,整个序列就排序好了。

起泡排序有优化方案

  • 如果没有交换,说明排序已经完成,无需继续排序
  • 如果在某个位置之后,再也没有交换,说明在该位置之后都是排序好的,该位置之后无需继续排序
  1. 剑指 Offer 21. 调整数组顺序使奇数位于偶数前面 - 中等 代码 代码正确,但是超时了,这个算法就是慢

组合问题中的蛮力法

生成排列对象

蛮力法生成{1,2,……,n}的n!排列。假设已经生成了(n-1)!个排列,可以把n插入到n-1个元素的每一种排列中的n个位置中去,来得到问题规模为n的所有排列。

  1. 46. 全排列 - 中等 代码

生成子集

n个元素的集合A={a1,a2,……,an}的所有2n的自己和长度为n的所有2n的比特串之间的一一对应关系

核心就在于这个一一对应关系,比特串里的0与1代表集合中的单个元素存在与不存在。

  1. 78. 子集 - 中等 代码

0/1背包问题

0/1背包问题是给定n个重量为{w1,w2,……,wn}、价值{v1,v2,……,vn}的物品和一个容量为C的背包,求这些物品中的一个最有价值的子集,并且要能够装到背包中。

这个问题使用蛮力法的话,就是计算出所有物品的子集,一个一个计算,看哪个价值最高。需要用到上面生成子集的方案。

这个就不做题了,一是因为主要功能在生成子集里已经做了,二是在乐扣里没有找到合适的题目,大家如果知道合适的题目可以推荐一下。

任务分配问题

假设有n个人物需要分配给n个人执行,每个任务只分配给一个人,每个人只分配一个任务,且第j个任务分配给第i个人的成本是C[i,j](1<=i,j<=n),任务分配问题要求找出总成本最小的分配方案。

这个问题使用蛮力法的话,就是生成所有任务的全排列,总数为n!,然后一个一个计算哪个价值最高,需要用到上面生成排列对象的方案。

这个同样也不做题了,原因和0/1背包问题一样,核心逻辑在生成排列对象里完成了。总体而言,蛮力法解决组合问题,除非问题规模非常小,否则蛮力法几乎不实用。

图问题中的蛮力法

哈密顿回路问题

注明爱尔兰数学家哈密顿提出注明的周游世界问题,他用正十二面体的20个订单代表20个城市,要求从一个城市出发,经过每个城市恰好一次,然后回到出发城市。

在这里插入图片描述

用蛮力法解哈密顿回路问题

  1. 计算所有顶点的全排列,共有n!个
  2. 每个排列需要满足两个条件
    • 相邻顶点有存在边
    • 第一个顶点和最后一个顶点存在边

这个不做题了,核心还是求全排列,然后判断是否有边。真用蛮力法做,想想肯定得超时。

  1. 面试题 04.01. 节点间通路 - 中等

TSP问题

TSP问题是指旅行家要旅行n个城市然后回到出发城市,要求各个城市经历且仅经历一次,要求所走的路程最短。

这个问题和哈密顿回路的区别是加了路程最短的要求。

用蛮力法解TSP问题

  1. 计算所有顶点的全排列,共有(n-1)!个,这些排列中有一半是路径完全相反,所以可能的解缩减到(n-1)!/2个
  2. 遍历这些排列,并计算路程,选择路程最短的即可

这个也不做了,核心还是求全排列,必然超时

  1. LCP 16. 游乐园的游览计划 - 困难

几何问题中的蛮力法

最近对问题

最近对问题要求找出一个包含n个点的几何中距离最近的两个点。

蛮力法求解最近对问题的过程是:分别计算每一对点之间的距离,然后找出距离最小的那一对,为了避免对同一对点计算两次距离,只考虑i<j的哪些点对(Pi,Pj)。

  1. 1512. 好数对的数目 - 简单 代码

凸包问题

凸集合:对于平面上的一个点的有限几何,如果以集合中任意两点P和Q为端点的线段上的点都属于该集合,则称该集合为凸集合。

凸包:一个点集S的凸包是包含S的最小凸集合,其中,最小是指S的凸包一定是所有包含S的凸集合的子集。

凸包人话版:凸包就是你画了一个封闭的范围,这个范围包含了集合中所有的点。最小凸包就是利用集合S上的点构建封闭范围,里面包含集合所有点

使用蛮力法计算凸包:对于一个由n个点构成的集合S中的两个点Pi和Pj,当且仅当该集合中的其他点都位于穿过这两点的直线的同一边时(假设不存在三点同线的情况),他们的连线是该集合凸包边界的一部分。对每一对点都检验一遍后,满足条件的线段构成了该凸包的边界。这套方案时间复杂度O(n^3)。

这个就不做了,做法和最近对问题差不多,主要区别是找到对后,需要和剩余的其他点再做一次运算。而且乐扣里没有发现相关的题目。

总结

看了这么多类型的题后,感觉蛮力法对有些问题还是有用的,对有些问题规模较小也还是有些用处的,对规模较大的问题,就完全没有办法了。

这里面的类型比较重要的有

查找问题:顺序查找,KMP

排序问题:选择排序、起泡排序。这两个方法效率都不好,今后可以使用更高效的排序算法。但这两种方法也提供了很不错的思想。

组合问题:生成排序对象生成子集。这两个方法一个时间复杂度O(n!),一个为O(2^n),虽然效率不行,但是这是很多其他问题的基础,如图问题、0/1背包问题,任务分配问题等

几何问题:最近对问题、凸包问题,整体思路比较简单,两重循环可以解决。

这些里面,尤其是KMP、生成排序对象、生成子集,有时间可以自己动手写一下,还是有一定难度的。

最后

大家如果喜欢我的文章,可以关注我的公众号(程序员麻辣烫)

往期文章回顾:

算法

  1. 算法学习计划
  2. 蛮力法

技术

  1. 浅谈微服务
  2. TCP性能优化
  3. 限流实现1
  4. Redis实现分布式锁
  5. Golang源码BUG追查
  6. 事务原子性、一致性、持久性的实现原理
  7. CDN请求过程详解
  8. 记博客服务被压垮的历程
  9. 常用缓存技巧
  10. 如何高效对接第三方支付
  11. Gin框架简洁版
  12. InnoDB锁与事务简析

读书笔记

  1. 敏捷革命
  2. 如何锻炼自己的记忆力
  3. 简单的逻辑学-读后感
  4. 热风-读后感
  5. 论语-读后感

思考

  1. 对项目管理的一些看法
  2. 对产品经理的一些思考
  3. 关于程序员职业发展的思考
  4. 关于代码review的思考
  5. Markdown编辑器推荐-typora

猜你喜欢

转载自blog.csdn.net/shida219/article/details/108306149
今日推荐