目录
查找问题(顺序查找,二分查找,分块查找,二叉排序树查找,哈希查找)
算法概述
什么是算法?
算法(Algorithm)是指解题方案的准确而完整的描述,是一系列解决问题的清晰指令,算法代表着用系统的方法描述解决问题的策略机制。. 也就是说,能够对一定规范的输入,在有限时间内获得所要求的输出。. 如果一个算法有缺陷,或不适合于某个问题,执行这个算法将不会解决这个问题。. 不同的算法可能用不同的时间、空间或效率来完成同样的任务。. 一个算法的优劣可以用空间复杂度与时间复杂度来衡量。. 算法中的指令描述的是一个计算,当其运行时能从一个初始状态和(可能为空的)初始输入开始,经过一系列有限而清晰定义的状态,最终产生输出并停止于一个终态。. 一个状态到另一个状态的转移不一定是确定的。. 随机化算法在内的一些算法,包含了一些随机输入。
解题的方法与步骤
算法概念的要点
-
算法的每一步都必须清晰,明确
-
算法所处理的输入的值域必须仔细定义
-
同样一种算法可以用几种不同的形式来描述
-
可能存在几种解决相同问题的算法
-
针对同一个问题的算法可能会基于完全不同的解题思路,而且解题速度也会完全不同。
欧几里得算法
求解任意两个数m(m>0)和n(n>0)的最大公约数,简称为欧几里得算法。
欧几里得递归算法
package 算法设计与分析;
public class DiGui {
public int getGCD(int m, int n) {
if (n == 0) {
return m;
} else {
return getGCD(n, m % n);
}
}
public static void main(String[] args) {
DiGui gcd = new DiGui();
int div = gcd.getGCD(90, 60);
System.out.println("最大公约数为:" + div);
}
}
欧几里得迭代算法
package 算法设计与分析;
import java.util.Scanner;
public class DieDai {
public static void main(String[] args) {
Scanner input=new Scanner(System.in);
System.out.println("请输入第一个数字");
int a = input.nextInt();
System.out.println("请输入第二个数字");
int b = input.nextInt();
int r = 1;
// a对b取余,b赋值给a 余数赋值给b,,直到 b == 0
do {
r = a % b;
a = b;
b = r;
}
while(b!=0);{
System.out.print("这两个数的最大公约数为"+a);
}
}
}
欧几里得连续整数检测算法
package chapter01;
import java.util.Scanner;
public class JianCe {
public static void main(String[] args) {
System.out.println("请输入两个正整数:");
Scanner scan = new Scanner(System.in);
Scanner scan2 = new Scanner(System.in);
int m = scan.nextInt();
int n = scan2.nextInt();
System.out.println("欧几里得算法求最大公约数是:" + gcd(m, n));
}
public static int gcd(int m,int n) {
int t;
if(m<n) {
t=m;
}else {
t=n;
}
while(m%t!=0||n%t!=0){
t--;
}
return t;
}
}
算法是用计算机解决某一类特定问题的一组规则的有穷集合,或则说是对特定问题求解步骤的一种描述,它是指令的有限序列。
算法的特征
-
输入:一个算法可以有零个或则多个输入,这些输入是在算法开始之前给出的量,它们取决于特定对象的集合,通常体现为算法中的一组变量。
-
输出:一个算法必须具有一个或多个输出,以反映算法对输入数据加工后的结果,这些输出是同输入有某种特定关系的量,实际上是输入的某种函数,不同取值的输入,产生不同的输出结果,没有输出的算法是没有意义的。
-
确定性:确定性算法指算法的每一个步骤都必须是有明确的定义的,必须是足够清楚的,无二义性的,确定性保证了以同样的输入多次执行一个算法时,必定产生相同的结果,否则一定出现了错误。
-
可行性:算法中所有的操作都必须足够基本,使算法的执行者或阅读者能明确其含义以及如何执行。它们可以通过已经实现的基本运算执行有限次数来实现。
-
有穷性:算法的有穷性是指算法必须总能在执行有限步骤之后终止,且每一步的时间也是有限的。
算法的基本要素
一个算法通常由两种基本要素组成,一种是对数据对象的运算和操作,二是算法的控制结构。
①对数据对象的运算和操作
-
算术运算:包括加,减,乘,除等运算。
-
逻辑运算:包括与,或,非等运算。
-
关系运算:包括大于,小于,等于,不等于等运算。
-
数据传输:包括赋值,输入,输出等操作。
②算法的控制结构
-
一个算法的功能不仅取决于选用的操作,而且还与各操作之间的执行顺序有关,算法中各操作之间的执行顺序称为算法的控制结构。
算法描述的工具
①自然语言:就是直接将设计者完成任务的思维过程用母语记录下来。
②流程图:是用规定的图形,流程线,文字说明表示算法的方法,是一种图形方式的描述手段,流程图可以清晰的描绘出完成解题任务的方法及步骤,是最早的算法描述工具。
③N-S流程图:这种流程图适用于结构化程序设计,能清楚的显示出程序的结构,是一种结构化的流程图。
④伪代码:伪代码是用介于自然语言和计算机语言之间的文字和符号描述算法,是一种描述语言。
算法与程序和数据结构之间的关系
①算法与程序
-
算法代表了对特定问题的求解,是行为的说明,是一组逻辑步骤,而计算机程序则是算法用某种程序设计语言的表述,是算法在计算机上的具体实现,执行一个程序就是执行一个用计算机语言表述的算法。
-
算法在描述上一般使用半形式化的语言,而程序是用形式化的计算机语言描述的,是使用一些特殊编程语言表达的算法。
-
一个算法可以用不同的编程语言编出不同的程序,但他们遵循的逻辑步骤是相同的,它们都表达同样的算法,它们不是同样的程序 ②算法与数据结构
-
算法是数据结构的灵魂,算法的结构和选择在很大程度上依赖于数据结构,数据结构是算法的基础,“算法+数据结构=程序”
问题求解的过程
问题及问题的求解过程
一个计算机开发的过程就是使用计算机求解问题的过程,软件工程将软件开发和维护过程分为若干阶段,称为生命周期或软件生命周期。
分为分析,设计,编码,测试和维护五个阶段。
算法设计和分析的步骤
①理解问题:在设计算法前首先要做的就是完全理解所给出的问题,明确定义所要求的问题,并用恰当的方式表示问题
②设计方案:求解问题时,考虑从何处下手,考虑选择何种问题求解策略和技术进行求解,以得到问题求解的算法。
③实现方案:实现求解问题的算法,使用问题进行测试,验证。
④回顾复查:检查该求解方法是否确实求解了问题或达到了目的。
⑤评估算法:考虑该解法是否可以简化,改进和推广。、
算法设计与算法表示
算法的求解过程
算法分为精确算法和启发式算法。精确算法就是总能保证求得问题的解;启发式算法通过使用某种规则,简化或智能猜测来减少问题求解的时间。对于最优问题的解,一个算法如果致力于寻找近似解而不是最优解,被称为近似算法,如果在算法中需做出某些随机选择,则称为随机算法。
算法设计策略
算法设计策略是使用算法解题的一般性方法,可用于解决不同领域的多种问题,算法设计方法主要有:分治策略,贪心算法,动态规划,回溯法,分支限界法等。
算法的表示
算法需要用一种语言来描述,算法的表示是算法思想的表示形式,算法应该用没有歧义的算法语言来描述,不能具有二义性。
算法的确认和算法分析
确认一个算法是否正确称为算法确认其目的在于确认一个算法是否能正确无误的工作,即证明算法对所有可能的合法输入都能得出正确的答案。
算法证明
算法证明与算法描述语言无关。使用数学工具证明算法的正确性,称为算法证明。算法证明常用的方法是数学归纳法。
算法测试
程序测试是指对程序模块或程序总体,输入事先准备好的样本数据(测试用例)检查程序的输出,来发现程序存在的错误及判定程序是否满足其设计要求。
调试只能指出有错误,而不能指出它们不存在错误。测试的目的是发现错误,调试是诊断和纠正错误。
算法分析
算法分析是对算法利用时间资源和空间资源的效率进行研究。算法分析活动对算法的执行时间和所需的存储空间进行估算。算法分析不仅可以预防算法能是否有效的完成任务,而且可以知道在最好,最坏和平均情况下的运行时间。使用样本数据可以测量一个程序所消耗的时间和空间,这称为程序的性能测量。
算法的复杂性分析
采用一个合适的算法的,解决问题的速度会大大提高,评价一个算法的好坏主要看执行算法时需要花费的计算机CPU时间的多少和需要占用的计算机村存储空间的大小。
算法评价的基本原则
正确性
在给定的有效的输入后,算法经过有限时间的计算,执行结果满足预先规定的功能和性能要求,答案正确,,算法应当满足具体问题的需求。其正确性表现在下面的四个方面。
-
程序不含语法错误。
-
程序对几组输入数据能够得出满足规格说明要求的结果。
-
程序对边界值等具有刁难性的测试用例能够得出满足规格说明要求的结果。
-
程序对于一切合法的输入数据都能产生满足规格说明要求的结果。
可读性
算法就是为了方便与用户的阅读和交流。算法应该易于理解,调试和修改,可读性有助于用户对算法的理解。
健壮性和可靠性
-
健壮性是当输入的数据非法时,算法能适应的做出反应或者处理,不会产生莫名奇妙的输出结果。当程序发生意外时,能按某种预定方式做出适当处理。
-
程序的可靠性指一个程序在正常情况下能正确的工作,而在异常情况下也能做出适当处理。
效率
效率包括运行程序所花费的时间以及运行这个程序所占用的存储空间,算法应该有效的使用存储空间,并具有较高的时间效率。
简明性
是指算法应该思路清晰,层次分明,容易理解,利用编码和调试,即要求算法简单,程序结构简单。
最优性
最优性指求解某类问题时效率最高的算法,即算法的执行时间已达到求解该类问题所需时间的下界。最优性与所求问题自身的复杂程度有关。
影响程序运行时间的因素
一个程序的运行时间是程序运行从开始到结束所需的时间,影响程序的运行时间主要有以下几个因素
程序所依赖的算法
求解同一个问题的不同算法,其程序运行时间一般不同。一个好的算法运行时间较少,算法自身的好坏对运行时间的影响是根本的和起作用的。
问题的规模和输入数据
程序的一次运行是针对所求问题的某一特定实例而言的,分析算法需要考虑一个基本问题是所求解问题实例的规模,对于不同的计算机,就算问题的实例规模相同,但由于输入输入数据的状态不同,所需的时间和开销也会不同。
计算机系统的性能
算法运行所需的时间依赖于计算机的硬件系统和软件系统。
算法复杂度
算法复杂度是算法效率的度量,是评价算法优劣的重要依据。一个算法的复杂程度主要体现在运行该算法所需要的计算机资源的量上,这主要依赖于算法要解决问题的规模,算法的输入和算法本身的函数。算法的复杂度主要包括时间复杂度和空间复杂度。
算法的时间复杂度
算法的时间复杂度指算法运行时所需时间,也指执行算法所需要的计算工作量。
-
度量算法的工作量
-
算法的执行时间大部分发在递归和循环上
-
时间复杂度
-
最好,最坏和平均时间复杂度
算法的空间复杂度
算法的空间复杂度是指算法运行所需的存储空间。一个算法所占用的存储空间包括算法程序所占的空间,输入的初始数据所占的空间以及算法执行过程中所需的额外空间。
-
固定空间需求:与所处理数据的大小和个数无关,与问题的实例特征无关,主要包括程序代码,常量,简单变量,定长成分的结构变量所占的空间。
-
可变空间需求:与算法在某次运行中所处理的特定的数据的规模有关,这部分存储空间包括数据元素所占的空间,以及算法执行所需的空间。
两种存储空间的方式
算法存储空间的分析分为两种情形:静态分析和动态分析
①静态分析
-
一个算法静态使用的存储空间,是指算法在执行前,可以通过程序静态的分析确定的使用空间,称为静态空间。
-
在静态空间分析中,静态数组占用了大部分的静态空间。
②动态分析
-
一个算法在执行过程中以动态方式使用的存储空间是指在算法执行过程中动态分配的存储空间,它们从程序的表面形式上不能完全确定,我们把在算法执行中才能确定的空间称为动态空间。动态空间的确定主要由两种情况构成,一是函数的递归,二是调用动态分配和回收函数。
注意:空间复杂度一般按最坏的情况考虑,为了减少算法所占的存储空间,通常采用压缩存储技术,以便减少不必要的额外空间。
使用程序步分析算法
①度量一个程序的执行时间通常有两种方法
-
事后统计的方法:①通过一个算法在给定输入下所执行的总的语句条数来计算算法的时间复杂度。缺陷是:必须先运行依据算法编制的程序;所得的时间的统计量依赖于计算机的硬件,软件等环境因素。
-
②事前分析估算的方法
运行耗时主要体现在以下的方面
-
选用了怎样的方法
-
问题的规模
-
书写程序的语言
-
编译程序所产生的机器代码的质量
-
机器执行指令的速度
②使用程序步分析算法
-
程序步是指在语法上或语义上有意义的程序段,该程序段的执行时间必须与问题的实例规模无关。
-
程序步并不是直接计算总的语句执行条数,而是将若干条语句合并成一个程序员步来计算。
渐进表示法
-
运行时间的上界(O)
-
运行时间的下界(Ω)
-
运行时间的准确界(Θ)
算法按时间复杂度分类
-
渐进时间复杂度有多项式时间限界的算法称作多项式时间算法。
-
θ(1):常数级< θ(log(n)):对数级< θ(n):线性级< θ(nlog(n)):对数线性级 <θ(n^2):平方级 <θ(n^3):立方级
-
-
渐进时间复杂度为指数函数限界的算法称作指数时间算法。
-
O(2^n)<O(n!)<O(n^n)
-
算法中常见的重要问题类型
排序问题(插入排序,选择排序,归并排序,快速排序)
查找问题(顺序查找,二分查找,分块查找,二叉排序树查找,哈希查找)
图问题
组合问题
几何问题
数值问题
常用的算法设计方法
算法是解决问题方法的精确描述,但是并不是所有的问题都有算法。解题算法是一个有穷的动作序列,动作序列中仅有一个初始的动作,序列中每个动作的后继动作是确定的,序列的终止表示问题得到解答或问题没有解答。
数值计算方法
迭代法
迭代法适用于方程或方程组的求解,是用间接方法求解方程近似根的一种常用方法
插值法
插值法又叫内插法,往往只知道它在某区间中若干点的函数值,这时候做出适当的特定的函数,使得在这些点上取已知值,并且在这个区间内其他各点上就用这个特定函数所取的值作为函数的近似值,这种方法就叫做插值法。
差分法
差分方程用于求解微分方程的近似解
归纳法
归纳法就是通过观察一些简单而特殊的情况,最后总结出有用的结论或找出解决问题的有效途径。归纳是一种抽象,是从特殊现象中找出一般的关系的过程。
递推法
递推是指从已知的初始条件出发,逐次推出所要求的各中间结果和最后结果。
递推法实际上是需要抽象为一种递推关系,然后按照递推关系求解。
减半递推技术
减半是指问题的规模减半,而问题的性质不变,递推就是重复减半的过程。
递归法
为了降低问题的复杂程度,将问题逐层分解,最后归纳为一些简单的问题,这种将问题逐层分解的过程,实际上并没有对问题进行求解,而是当解决了最后的简单问题后,再将这些解进行合并,这就是递归的思想。
非数值的计算方法
列举法
基本思想
根据提出的问题列出所有可能的情况,并用问题中给定的条件检验哪些是需要的,哪些是不需要的,即搜索所有可能的情形,从中找出符合要求的解,在使用列举算法时,要尽量让方案优化,减少运算工作量。
特点
算法简单,但是当列举的可能情况比较多时,执行列举算法的工作量将会增大。
分治算法
分治法是自顶向下,分而治之的方法,其思想是把一个大规模的问题分解成若干个规模较小的,与原问题类型相同的子问题,使得从这些规模较小问题的解易于构造整个问题的解,通过对子问题的求解,并把子问题的解合并起来从而构造出整个问题的解。分治算法处理问题的算法可以写成一个递归的过程。
贪心算法
贪心算法把构造可行解的工作分成许多阶段来完成,在各个阶段,选择哪些在某些意义下是局部最优的方案,期望各阶段的局部最优的选择来带动整体最优。但是,贪心算法并不是每次都能成功的产生出一个整体最优解,贪心算法在每一阶段都保持着局部最优,从各阶段结合起来,总的结果并不一定是最优的。
动态规划算法
动态规划算法与分治算法的共同点是把一个大问题分解为若干个小问题,通过求解子问题的解进而得到原问题的解。不同点是分治法每次分解的子问题数目比较少,子问题之间界限清楚,处理的过程通常是自顶向下的进行,动态规划算法分解的子问题可能比较多,而且子问题相互包含,把计算的结果都存储起来,通常是自底向上进行。
回溯算法
回溯算法就是通过对问题的分析,找出解决问题的一个线索,然后沿着这个线索逐步试探,对于每一步的试探,若试探成功就得到问题的解,若试探失败,就逐步退回,换到别的线路继续试探。
回溯算法是一种选优的搜索算法,按照最优的条件向前搜索,以达到目标;但是当搜索到某一步的时候,发现原先的选择并不优或则达不到目标时,就退回一步从新选择。回溯算法是通过系统的搜索来确定问题的解。
分支限界算法
分支限界算法是一种在表示问题解空间的树上进行系统搜索的方法。所不同的是,回溯算法使用了深度优先策略,而分支限界算法一般采用广度优先策略或采用最大效益策略。
⛵总结
-
算法是指有若干条指令组成的有穷序列,具有五大特征:有零个或者多个输入,至少有一个输出,确定性,可行性,有穷性
-
设计算法的步骤一般分为五步:理解问题,确定算法的运行环境,设计算法,分析算法和编程实现。
-
对算法研究主要包括算法的设计,表示,确认和分析。
-
算法的复杂性主要包括时间和空间两个方面,时间是衡量算法完成任务时所需要的时间消耗。空间是衡量算法在执行过程中所需存储空间的大小。
-
空间代价分析分为两种:静态分析和动态分析。在静态空间分析中主要是数组。在动态空间分析中主要考虑函数的递归调用和空间的动态分配。
-
算法的时间复杂性由算法中的基本操作的次数来衡量。算法的执行时间大部分发在递归和循环上,循环的时间代价可以用加法规则和乘法规则估算;对于递归算法,可以通过解递归方程计算。算法的运行时间可以用程序步来衡量。
-
算法的时间和空间效率是衡量一个算法性能的重要标准,对算法的性能分析可以采用事前分析和事后策略形式进行,算法分析通常使用渐进表示法对一个算法时间和时间需求做事前分析。算法复杂度的渐进表示法用于在数量级上估算一个算法的时空消耗。
-
分治算法通过把问题化为较小的问题来解决原问题,从而简化或减少了原问题的复杂度。
-
贪心算法通过分阶段挑选最优解,较快得到整体的最优解,贪心算法既可能得到次优解,也可能得到最优解,依赖于具体问题的特点和贪心策略的选取。
-
动态规划算法用填表的方法保存了计算的中间结果,从而避免了大量重复的运算。
-
回溯算法跳过大量无需测试的元组,可快速得到需要的解。
-
分支限界法在系统搜索问题解的空间时,加入上下界的条件检查以达到最大剪纸的目的。
如果这篇【文章】有帮助到你,希望可以点个赞👍,创作不易,如果有对【Java基础】【后端技术】、【数据结构】【Linux操作系统】感兴趣的小可爱,也欢迎关注 【LNORA】,对【算法设计与分析】感兴趣的可以免费订阅【算法设计与分析】的专栏,如果我的文章有帮助到你,麻烦来个一键三连奥,这将是对我莫大的鼓励,我将为大家带来更加优质的文章!我们可以一起进步,每天进步一点点,我将会给你带来巨大的【收获与惊喜】💝💝!