这篇文章仅仅用于巩固我复习算法,写的不好的地方,欢迎大家指正。
什么是算法:
定义:
算法:算法是解决特定问题求解的步骤的描述,在计算机中表现为指令的有限序列,并且每一条指令表示一个或多个操作。
特点:
- 输入输出:算法具有零个或多个输入,而至少有一个或多个输出。
- 有穷性:算法在执行有限的步骤之后,自动结束而不会出现无限循环,并且每一个步骤在可接受的时间内完成。
- 确定性:算法的每一步都具有确定的含义,不会出现二义性。
- 可行性:算法的每一步都必须是可行的,也就是说,每一步都能够通过执行有限次数完成。
设计要求:
- 正确性:算法的正确性是指算法至少应该具有输入,输出和加工处理无歧义性,能正确的反映问题的需求,能够得到问题的正确答案。
- 可读性:算法设计的另一个目的是为了便于阅读,理解和交流。
- 健壮性:当输入数据不合法时,算法也可以做出相关的处理,而不是产生异常或者莫名其妙的结果。
- 时间效率高和存储量低。
经典算法:
查找算法:
基本概念:
- 查找表是由同一类型的数据元素或者记录构成的集合。
- 关键字是数据元素中某个数据项的值,用它可以标志一个数据元素,也可以标志一个记录的某个数据项。
- 若此关键字可以唯一的标志一个记录,则把这个关键字叫做主关键字。
- 对于那些可以识别多个记录的关键字,我们把它叫做次关键字。
- 静态查找表:只用于查找操作的查找表。
- 动态查找表:在查找过程中还可以插入或者删除数据元素的查找表。
线性查找:
线性查找是最基本的查找技术,它的查找过程是:从表中的第一个或者最后一个数据元素开始,逐个进行与给定的数据元素比较,如果某个数据元素与给定的数据元素相等,则查找成功;如果直到最后一个数据元素或者第一个数据元素,都与给定的数据元素不相等时,则表中没有所查的记录,查找不成功。
二分查找:
使用二分查找法时必须保证数据元素是有序的。它的查找过程为:取中间的数据元素作为比较对象,若给定的数据元素与中间的数据元素相等,则查找成功;若给定的数据元素小于中间的数据元素,则在中间数据元素的左边区域进行查找;若给定的数据元素大于中间的数据元素,则在中间数据元素的右边区域进行查找。然后不断重复上述步骤,直到查找成功。如果所有的查找区域都没有给定的数据元素,那么查找失败。
插值查找:
插值查找可以算的是二分查找的一种改进,插值查找是根据查找的数据元素与查找表中的最大最小的数据元素比较后的查找方法。同时它把二分查找的取中间数据元素((最低下标+最高下标)/2下标的值)改成了取一个插值进行比较(最低下标+(最高下标-最低下标)*(给定的数据元素-最低下标的数据元素)/(最高下标的数据元素-最低下标的数据元素)或者写成这样low+(high-low)*(key-a[low])/(a[high]-a[low]))。如果给定的数据元素比插值小,那么最高下标调整到插值小一位;如果给定的数据元素比插值大,那么最小下标调整到插值大一位。然后不断重复上述步骤,直到查找成功,如果所有的查找区域都没有给定的数据元素,那么查找失败。对于表长且数据元素比较均匀的查找表插值查找比二分查找性能要好。
排序算法:
基本概念:
假设含有n个数据元素的序列为{r1,r2,……rn},其对应的关键字为{k1,k2,……kn},需确定1,2,……n的一种排列p1,p2,……pn;使其对应的关键字满足kp1<=kp2<=……kpn非递增或者非递减的关系,即使得序列成为一个按关键字有序的序列,这种操作就叫做排序。
冒泡排序:
冒泡排序是一种交换排序,它的基本思想是:两两比较相邻数据元素的关键字,如果反序则进行交换,直到没有反序的数据元素为止。冒泡排序的时间复杂度最好情况为O(n);最坏的情况为O(n(n-1)/2);因此总的时间复杂度为O(n^2)。实现思路:
简单选择排序:
简单选择排序法是把n个数据元素的关键字通过n-i次关键字之间的比较,从n-i+1个数据元素中选出关键字最小的数据元素,并且和第i(1<=i<=n)个数据元素进行交换。总的时间复杂度为O(n^2),但是却略优于冒泡排序。实现思路:
直接插入排序:
直接插入排序的基本操作是将一个数据元素插入到已经排好序的有序表中,从而得到一个新的,数据元素加一的有序表。时间复杂度也是O(n^2),实现思路为:
希尔排序:
希尔排序也叫做缩小增量排序,它是直接插入排序算法的一种更高效的改进版本。它的过程为:把原本大量的数据元素进行分组,分成若干个子序列,然后对这些子序列内分别进行直接插入排序,当整个序列都基本有序时,再对整体进行一次直接插入排序。(分组策略:将相距某个增量的数据元素组成一个子序列,这样才能保证在子序列内分别进行直接插入排序后得到的结果是基本有序而不是局部有序),希尔排序的时间复杂度为:O(n^3/2)。
归并排序:
“归并”在数据结构中的定义是将两个或者两个以上的有序表组合成一个新的有序表。归并排序就是利用了归并的思想实现的排序方法,原理为:假设初始的序列含有n个数据元素,则可以看成是n个有序的子序列,每个子序列的长度为1,然后两两归并;得到不小于n/2个长度为2或者为1的有序子序列;然后再两两归并,直到得到一个长度为n的有序序列为止,这种两两排序方法也叫做2路归并排序。归并排序的时间复杂度为:O(nlogn)。
快速排序:
快速排序的基本思想是:选取一个数作为基准数,通过该基准数把数据元素分成两部分,其中一部分都比基准数要大,一部分都比基准数要小;然后重复该步骤,直到整个序列都有序。快速排序的时间复杂度为:O(nlogn)。实现思路为:
快速排序选取的基准数很重要,基准数的大小会影响快速排序的效率。那么怎么选取到一个比较合适的基准数呢?可以使用三数取中法:取三个数据元素,可以随机选择,也可以取左端,中间和右端三个数,然后将这三个数进行排序,将中间的数作为基准数。或者可以使用九数取中法:分三次取样,每次取三个数据元素;三个样品分别选择出中间数,再从这三个中间数选择一个中间数作为基准数。
贪婪算法:
贪婪算法也叫做贪心算法,是指对问题进行求解时,把问题分为多个阶段,每个阶段都采取最优或者最好的选择,而不考虑后果。通常,这意味着选择的是某个局部最优,并以此希望最后堆叠出的结果也是最好/最优的选择。如果是这样的话,那么算法就是正确的;否则算法得到的是一个次最优/最好的选择。如果不要求绝对最优答案,那么有时候会使用简单的贪婪算法生成近似的答案,而不是使用通常产生准确的答案所需要的复杂算法。选择的贪婪算法必须具备无后效性,即某个状态以后的过程不会影响以前的状态,只与当前状态有关。
基本思路:
- 从某个初始解出发;
- 采用迭代的过程,当可以向目标前进一步时,就根据局部最优策略,得到该局部最优的选择作为一部分解,缩小问题规模;
- 将所有解综合起来作为原来整个问题的一个近似最优解。
- 补充:可以建立一个集合用来组装局部解为整个问题的解;也可以建立一个集合用来存放整个问题的可能解,从而在集合中选择出里面的最优解。贪婪算法的关键在于当可以向目标前进一步时,怎么选择出局部最优。
动态规划:
动态规划算法通常用于求解具有某种最优性质的问题。在这类问题中,可能会有许多可行解,我们希望从中找到最优解。我们可以用一个表来记录所有已解的子问题的答案。不管该子问题以后是否被用到,只要它被计算过,就将其结果填入表中。
基本思路:
- 划分:按照问题进行划分子问题(阶段), 子问题的解一旦求出就会被保存,所以每个子问题只需要解一次。划分后的子问题一定是有序或者是可以排序的。
- 表示:即如何让计算机理解子问题。将问题发展到各个阶段时所处的各种不同的客观情况表现出来。
- 确定初始状态(边界状态)和最终状态等。
- 写出状态转移方程(即问题是如何由子问题推导出来的),状态转移方程是一个递推型,即从一个或者多个值求出另一个值。