【算法】最直接的算法——穷举法详解

这篇具有很好参考价值的文章主要介绍了【算法】最直接的算法——穷举法详解。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

第三章 穷举法

一、基本概念

穷举法又称为枚举法或者蛮力法,是一种简单直接解决问题的方法,常常是基于问题的直接描述去编写程序,比如说求n的阶乘,那么就直接一个循环n次的for循环。

穷举法依赖的基本技术是遍历,也就是采用一定策略依次处理待求解问题的所有元素。对于穷举法自身的优化,一般只能减少其执行的系数,但是数量级不会改变。由于穷举法需要遍历所有元素,因此他的时间性能往往是最低的,指数级的时间开销往往都是采用穷举带来的,但是它依旧是很重要的算法设计思想,因为:

  • 理论上,穷举法可以解决许多计算领域的问题(只要机器性能足够或者时间开销可承受)。并且在一些较为基本的问题的求解中运用十分广泛,比如求n个数的和。
  • 穷举法可以用于解决一些规模较小的问题,因为其时间规模在可承受范围内
  • 对于某方面的问题(比如排序、查找、串匹配),可以基于穷举法设计出一些优化算法,这些优化算法是可用并且具有实用价值的,比如说KMP算法就是基于穷举法优化的串匹配算法
  • 穷举可以作为某类问题的时间性能下界,来衡量同样问题其他算法是否具有更高效率。

下列是一个经典穷举问题:
【算法】最直接的算法——穷举法详解
我们知道,假设有i张10元,j张5元,k张1元,那么满足兑换方案的方程应该是 i + j + k = 50 i+j+k=50 i+j+k=50并且 i ∗ 10 + j ∗ 5 + k = 100 i*10+j*5+k=100 i10+j5+k=100。而10元最多5张,5元最多10张,1元最多50张。按照上述编程可得:

class Main {
    public static void main(String[] args) {
        int sum = 0;
        for (int i = 0; i < 10; i++) {
            for (int j = 0; j < 20; j++) {
                for (int k = 0; k < 100; k++) {
                    if (i * 10 + j * 5 + k == 100 && i + j + k == 50){
                        System.out.printf("%d %d %d\n", i, j, k);
                        sum++;
                    }
                }
            }
        }
        System.out.println(sum);
    }
}

二、查找中的穷举法

查找是穷举法应用十分重要的一个领域,虽然穷举十分笨拙,但是只要规模不大,还是可行的

2.1 顺序匹配

顺序查找是基于穷举的查找法。在一个有n个元素的一维的顺序表中,从第0个元素开始逐个向下查找,如果找到目标值则直接返回目标值的下标;否则继续查找下一个元素,直到n个元素均被遍历完。分析时间开销:最好的情况,也就是需要查找的元素刚好是0号元素,时间开销为O(1);最坏情况,也就是顺序表中没有目标值,需要遍历n个元素,时间开销O(n);平均需要遍历n/2个元素,时间开销还是O(n)

2.2 串匹配问题

简单的模式匹配算法

子串的定位操作称为串的模式匹配,其中简单的模式匹配算法是一种不依赖其他串操作的暴力匹配算法。其算法思想是,将主串中和模式串等长的子串全部提取出来,并且依次对比。

暴力模式匹配算法的最坏时间复杂度为O(nm),最好的时间复杂度为O(m),其中n,m分别为主串和模式串的长度。

改进的模式匹配算法——KMP算法

在暴力匹配算法中,每次匹配失败都是后移一位再从头开始比较,但是比如:

在 a b a b c a b c a c 中查找abcac

这会导致一定的重复比较,从而导致效率下降(展开说)

1.字符串的前缀、后缀和部分匹配值

前缀指的是出最后一个字符外所有的头部子串,后缀指的是除第一个字符外字符串的所有尾部子串;部分匹配值为字符串的前缀和后缀的最长相等前后缀长度。

在对比到第k个字符时,如果发生了串不匹配,可以寻找已匹配的串的最大公共前后缀,从而使得不需要重复对比。

在一个有n个字符的串中,可能存在n种匹配失败的情景,对应的是n种部分匹配串。每一种部分匹配串的最大前后缀是固定的,因此可以提前计算出对比到k个字符错配时主串需要前进的步数,并且将其存储在next数组中。这样在KMP算法执行时,可以直接使用

重点:next数组的计算

最长相等前后缀长度可以使得主串不需要回退,故KMP算法可以在O(n+m)的时间数量级上完成串的模式匹配操作,提高了模式匹配效率。其中,O(m)的时间复杂度是在求next数组时产生的,O(n)的时间复杂度是在执行KMP算法时产生的。

总的来说,相对于朴素模式匹配算法,KMP算法能够避免主串指针频繁回溯,从而提高了效率

2.KMP算法的原理是什么

当子串与扫描到的主串不匹配的时候,首先计算出已匹配的子串的前缀和后缀的最大公共子集。然后可以将子串向后移动,将共有前缀移动到原子集的共有后缀处,从而避免重复查找,使得子串不需要回退。

右移位数 = 已匹配的字符数 - 对应的部分字符值

3.KMP算法的进一步优化
KMP算法在对比诸如"aaaab"这类串的时候,还是会出现重复匹配的问题,为了解决而需要在next数组的基础上再进一步处理得到nextval数组。

三、排序问题中的穷举法

排序问题指的是如何将乱序的序列排列成元素有序的序列

3.1 选择排序

选择排序的基本思想是:每一趟在后面n-i+1个待排序元素中选取关键字最小的元素,作为有序子序列的第i个元素,直到第n-1趟做完,待排元素只剩下一个,就不用再选了。

假设排序表为L[1…n],第i趟从L[i…n]中选择关键字最小的元素与L(i)交换,每趟排序可以确定一个元素的最终位置,这样经过n-1趟排序就可以使得整个排序表。

具体步骤如下:

  1. 将整个顺序表划分为有序区和无序区,初始时有序区为空,无序区含有所有元素
  2. 在无序区查找值最小的元素,将它和无序区的第一个元素交换,使得有序区扩展一个元素,无序区减少一个元素
  3. 不断重复上述步骤,直到计生一个记录为止

空间效率:只使用常数个辅助单元,空间效率为O(1)

时间效率:简单选择排序中,元素移动操作次数很少,不会超过3(n-1)次,最好情况是移动0次。但是元素间比较次数和序列初始状态无关,都是n(n-1)次,因此时间复杂度为O(n2)

该算法不稳定,可用顺序表和链表表示

3.2 冒泡排序

从后往前两两比较元素的值,如果逆序则交换两个元素的值,每一趟排序都可以将一个元素移动到最终位置,已经确定好位置的元素无需对比。如果在某一趟中没有发生交换,那么证明剩余序列已经有序,可以提前结束了。

// 冒泡排序
void swap(int &a, int &b){
    int temp = a;
    a = b;
    b = temp;
}

void bubbleSort(int a[], int n){
    for (int i = 0; i < n - 1; ++i) {
        bool flag = false; // 是否发生交换的标志
        for (int j=n-1; j>i; j--){ // 一趟冒泡的
            if (a[j-1]>a[j]){   // 如果是逆序
                swap(a[j-1], a[j]);
                flag = true;
            }
        }
        if (!flag)
            return;
    }
}

性能:
空间复杂度:O(1)
时间复杂度:
最好情况是有序的,为O(n);最坏情况为逆序,需要交换 n ( n − 1 ) 2 \frac{n(n-1)}{2} 2n(n1)为O(n2);平均的复杂度也是O(n2)
冒泡排序是稳定的,适用于链表。

四、组合问题中的穷举法

01背包问题

问题:给定n个重量为{w1,w2,…wn},价值为{v1,v2,…vn}的物品和一个容量为C的背包,如何装入物品使得背包中的物品价值最大。

思想:穷举法解决01背包其实就是遍历n个物品集合的所有组合,找出总重量不超过背包的组合集中价值最大的组合。比如有3个物品的所有组合有{1},{2},{3},{1,2},{1,3},{2,3},{1,2,3}

开销:对于有n个物品的01背包问题,采用穷举法需要消耗O(n2)的时间,虽然可以采用一定的剪枝措施,比如如果发现放入{1,2}就已经超重了,那么凡事含有{1,2}的集合都会超重(比如说{1,2,3}),这些集合就不需要再进行遍历。但是这只能减少它的执行系数,但是数量级不会改变,仍然是O(n2)。

任务分配问题

问题描述:
任务分配问题中,会有n个任务和m个人,每个任务只分配给一个人,每个人只执行一个任务,第i个人执行第j个任务分配的开销为Cij。任务分配问题的目标是找出开销最小的分配序列。

分析:
根据描述,可以使用一个n*m的二维数组存储信息,第i行第j列表示第i个人执行第j个任务所需的花销。而任务分配问题就是选择n行中的一个元素,代表选出n个人并且给他们分配一个任务。这些被分配到任务的人可以组成一个n个元素的顺序表alloc,其中alloc[i]表示第i个人被分配到了第alloc[i]个任务。比如说{2,1,3}表示第一个人被分配到第2个任务,第二个人被分配到第1个任务,第三个人被分配到了第三个任务。穷举法其实就是遍历alloc表的所有组合,从中选取开销最小的组合。这类似于找到组合的全排列

开销:任务分配问题的全排列的时间开销为n!,这意味着除非问题规模很小,否则时间开销都是难以承受的。

五、图问题中的穷举法

哈密顿回路问题

哈密顿回路问题中,有n座城市,要求从某一个城市出发,只经过每个城市一次,然后回到出发的城市。如果存在这种路径则称之为哈密顿回路。

分析:
n个城市可以看作一个有n个结点的无向图G,而城市之间的路径就是图中的边。穷举法求哈密顿回路的基本思路是,对于无向图G,依次将图中所有顶点进行全排列,满足以下两个条件的全排列构成的回路就是哈密顿回路:

  • 相邻顶点之间存在边
  • 最后一个顶点和第一个顶点之间存在边

开销:
哈密顿算法只需要找到一条符合的边就可以结束算法, 可能并不需要遍历所有全排列,但是最坏情况是不存在哈密顿回路,这种情况必须遍历所有全排列,时间复杂度为O(n!)

TSP问题

TSP问题又称为旅行家问题,旅行家要去n个城市旅游,然后返回处罚的城市,要求各个城市只经过一次并且所走的路径最短。

分析:
n个城市可以看作一个有n个结点的无向图G,和哈密顿回路不同的是,哈密顿中的图并非为有权图。穷举法找最短路径,首先是找出所有顶点的全排列,然后找出所有路径汇总的哈密顿回路。对比各个哈密顿回路,选出其中最短的哈密顿回路。其求解方法其实和哈密顿回路较为相似

开销:
任何情况下都需要遍历全排列,因此时间开销固定为O(n!)

六、几何问题中的穷举法

最近点对问题

在一个二维平面上有n个点,需要找出这n个点中距离最近的一对点

分析:
穷举法都很暴力,遍历所有的点对,并且使用 x 2 + y 2 \sqrt{x^2+y^2} x2+y2 求出距离,然后最终得出最短距离。其时间复杂度为O(n^2)

凸包问题

定义1:
对于平面上一个点的有限集合,如果集合中任意的两个点P和Q连成的线段上的所有点都位于集合内,则称该集合为凸集合。比如:
【算法】最直接的算法——穷举法详解
很显然,圆形和正方形都是凸集合,而下列图形则显然不是凸集合
【算法】最直接的算法——穷举法详解
一个点集S的凸包是包含S的最小凸集合,其中最小是指S的凸包一定是所有包含S的凸集合的子集文章来源地址https://www.toymoban.com/news/detail-473521.html

到了这里,关于【算法】最直接的算法——穷举法详解的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • C++算法之旅、06 基础篇 | 第三章 图论

    常用代码模板3——搜索与图论 - AcWing 尽可能往深处搜,遇到叶子节点(无路可走)回溯, 恢复现场继续走 数据结构:stack 空间:需要记住路径上的点, (O(h)) 。 ⭐ BFS使用空间少; 无最短路 性质 每个DFS一定对应一个 搜索树 ;要考虑用什么 顺序 遍历所有方案;DFS就是递

    2024年02月10日
    浏览(35)
  • (数字图像处理MATLAB+Python)第三章图像基本运算-第二节:图像代数运算

    A:概述 加法运算 :指将两幅同大小的图像进行像素级别的加法操作,得到一幅新的图像。设两幅图像对应的像素值分别为 f 1 ( x , y ) f_{1}(x,y) f 1 ​ ( x , y ) 和 f 2 ( x , y ) f_{2}(x,y) f 2 ​ ( x , y ) ,则它们的加法运算可表示为 g ( x , y ) = f 1 ( x , y ) + f 2 ( x , y ) g(x,y)=f_{1}(x,y) + f_{

    2023年04月12日
    浏览(31)
  • 【软考数据库】第三章 数据结构与算法

    目录 3.1 数据结构 3.1.1 线性结构 3.1.2 数组 3.1.3 矩阵 3.1.4 树与二叉树 3.1.5 图 3.2 查找 3.2.1 顺序查找 3.2.2 折半查找 3.2.3 哈希表 3.3 排序 3.3.1 直接插入排序 3.3.2 希尔排序 3.3.3 简单选择排序 3.3.4 堆排序 3.3.5 冒泡排序 3.3.6 快速排序 3.3.7 归并排序 3.3.8 基数排序 3.3.9 内部排序算法

    2023年04月26日
    浏览(33)
  • 【AcWing算法基础课】第三章 搜索与图论

    本专栏文章为本人AcWing算法基础课的学习笔记,课程地址在这。如有侵权,立即删除。 特点 :尽可能先向 纵深方向 搜索。使用 stack 实现。所需空间 O(h) (h为深度)。不具有“最短性”。 题目链接 :842. 排列数字 1.1题目描述 给定一个整数 n,将数字 1∼n 排成一排,将会有

    2024年02月12日
    浏览(54)
  • 计算机组成原理---第三章存储系统 习题详解版

    知识扩展: 如果主存的容量无法满足 CPU 的需求,可以通过存储器扩展来解决,扩展的方式有两种: 主存的 位数 不够(相当于快递柜的尺寸太小,放不下大包裹),则可以通过位扩展的方式(快递柜扩容)实现; 主存的 字数 不够 (存储单元的数目不够, 相当于快递柜数

    2024年02月08日
    浏览(55)
  • SQL Server基础 第三章 数据表基本操作(增删改查,不允许保存更改异常!)

    往表里插数据我们现在有两种方式 第一种是编辑直接修改,第二种是通过查询来修改数据 两种方法的区别 第一种更直接,如果数据量小那么直接改就好了,那如果数据量稍微庞大我们就需要用新建查询来进行表内容的修改了!!!!!!! 只需要新建查询,然后新的查询文

    2023年04月26日
    浏览(41)
  • 数据库系统工程师——第三章 数据结构与算法

    数据结构是指 数据元素的集合 及 元素间的相互关系和构造方法 ,结构就是元素之间的关系。在数据结构中,元素之间的相互关系是数据的逻辑结构。按照逻辑关系的不同将数据结构分为线性结构和非线性结构,其中,线性结构包括线性表、栈、队列、串,非线性结构主要包

    2024年02月04日
    浏览(55)
  • 计算机网络原理(谢希仁第八版)第三章课后习题详解

    3-01 数据链路与链路有何区别?“电路接通了”与“数据链路接通了”的区别何在? 所谓链路就是从一个结点到另一个结点的一段物理线路。而中间没有其他的任何交换结点。在进行数据通信的时候,两个计算机之间的通信路径要经过许多这样的链路。 当需要在一条链路上传

    2023年04月08日
    浏览(95)
  • 第三章 图论 No.11二分图,匈牙利算法与点覆盖

    257. 关押罪犯 - AcWing题库 最大最小问题,一眼二分 答案的范围在 [ 1 , 1 e 9 ] [1, 1e9] [ 1 , 1 e 9 ] 之间,二分答案,check(mid) check:将所有权值大于mid的边进行二分,若能得到二分图,返回true,否则返回false 最终将得到最优解ans,所有大于ans的边组成的图为二分图,若是图中有边

    2024年02月12日
    浏览(31)
  • 【计算机视觉:算法和应用】第三章:图像处理——3.2线性滤波

    【计算机视觉:算法和应用】第二章:图像形成——2.1 几何图元与变换_Lu.马夋的博客-CSDN博客 【计算机视觉:算法和应用】第二章:图像形成——2.2相机辐射成像-CSDN博客 【计算机视觉:算法和应用】第二章:图像形成——2.3数码相机-CSDN博客 【计算机视觉:算法和应用】

    2024年02月03日
    浏览(34)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包