痛定思痛,开启算法之路(十四)

6.4 排序

**这个排版问题也是无力吐槽啊,弄得句不成句、段不像段,奈何寄人篱下,还是言归正传吧!**

    基数排序、插入排序(时间复杂度和数据移动次数均为O(n^2))、选择排序(时间O(n^2),移动O(n log n))、归并排序(时间和移动均为(n log n),且须额外空间)、
快速排序(平均O(n log n),无需额外空间(但根据初始选择支点的不同以及初始序列顺序的不同其性能不稳定),对于长序列快排相对更好,
而对于较短的序列像插入、选择排序相比则方法更好;
所以在实际情况中可以多种算法结合,对于长序列先使用快排,当规模小到一定数量之后转为简单排序);
这些基本排序算法比较简单不再赘述;

归并排序算法:
痛定思痛,开启算法之路(十四)_第1张图片
痛定思痛,开启算法之路(十四)_第2张图片
快速排序算法如下:
痛定思痛,开启算法之路(十四)_第3张图片
痛定思痛,开启算法之路(十四)_第4张图片

6.4.6 排序问题的下界
痛定思痛,开启算法之路(十四)_第5张图片
6.5.1 最大和最小数
痛定思痛,开启算法之路(十四)_第6张图片
6.5.2 查找第k小的数
这里写图片描述
1)求第k小的数,之前接触的一个数据结构上的算法是:序列首先应该是有序的(倒序),然后设置两个变量,其中一个变量先行k个元素,然后与第二个元素一同前行,当第一个元素到达序列最后一个元素时第二个元素即指向了所要求的第k小元素;
2)此处是有优化的:此处的解法借用快排的思想,每次分割为左右两个序列:对比所求的k与此处分割元素的位置,然后在分个元素相应左右子序列再次递归查找第k位置,算法如下:
痛定思痛,开启算法之路(十四)_第7张图片
6.6 数据压缩
痛定思痛,开启算法之路(十四)_第8张图片
前缀限制之前也接触过只是称呼不同而已,即各编码前缀不同相互包含对于数据结构中所学过的哈夫曼编码(用二叉树从根向下左右分别取1/0直到叶节点);同时注意以上标注红色箭头的地方,字频的统计是根据典型文本进行统计而得到的标准字频表。二叉编码树如下,现在的问题就是求解如何构造这可编码树使其长度最小。(着急了,之前都是看完理解好了再回过头来总结的,这次边看边写下理解想法的);这里的二叉编码树跟数据结构学习的哈弗曼树是一样的,也即构造哈弗曼树而已,图及算法如下(思想还是比较简单的不再赘述):

痛定思痛,开启算法之路(十四)_第9张图片
痛定思痛,开启算法之路(十四)_第10张图片
6.7 串匹配
这里写图片描述
串匹配算法接触的应该是比较多了,最直观的就是从前往后依次回退匹配,在最坏情况下比较次数可能达到O(mn)(两个串分别长m和n),这个算法在大多数情况下是非常低效的,因为他包含了许多不必要的回溯扫描重复元素的步骤;在数据结构中介绍过一种改进算法KMP算法,其利用next数组记录子串已匹配元素的对称性,从而可以使A串不动而同时使B串一次移动向后移动多步而避免重复匹配过程(下附其算法);当然这还不是更高效的,在我分享学习的上一本书《算法设计和分析基础》中还介绍了更加高效的算法Horspool算法和Boyer-Moore算法,链接见http://blog.csdn.net/Enjoy_endless/article/details/79067780;

之前也没有详细介绍KMP算法,在这里主要介绍一下其算法执行思想及原理:
痛定思痛,开启算法之路(十四)_第11张图片
第一步:求出其各元素对应的next数组值:

如上图:先按照步骤1比较next(i-1)+1,如果相等直接next[i] = j;如果不等则按步骤2比较next(j)+1,取值及后续处理同前;

来探讨一下**为什么**这样比较:

**第一步**比较原理其实比较明显,首先明确next数组记录的是此字符以前串的匹配度(是不包括自身字符的),
如next[i-1]表示的是前i-2个字符所组成串的匹配度i,所以求next[i]时比较的是前i-1个字符,
如上图黑色部分所示假设前i-2个字符以匹配元素为黑色部分,
当加入第i-1个元素时自然需要再次比较匹配的元素是第i-1个元素和前面黑色部分后面的第一个元素是否相等;

**第二步**比较:注意第二步比较的选取是有讲究的(即第一步中第i-1个元素与第next(i-1)+1元素不相等时,进入第二步):
如下图四个红色圈所画(编一下号,从左到右分别为1、2、3、4号),首先由第一步知前后两个黑色部分的元素肯定是对应相等的,
也即1等于3和2等于4,而由next[j](j=next[i-1]+1)可以得出1等于2,从而得出1等于4,而此时所要求的next[i]正是在1和4相等的基础上,
再次比较第i-1个元素是否与第一个红圈后的第一个元素是否相等,讲到这里应该就很明确了!往后的步骤与上等同。

痛定思痛,开启算法之路(十四)_第12张图片
痛定思痛,开启算法之路(十四)_第13张图片
痛定思痛,开启算法之路(十四)_第14张图片
6.8 序列比较

把一个序列变成另一个序列的最少修改步数(利用动态规划);

改变过程可以有三种方法:1)插入 2)删除 3)替换;

假设将子串A(n)转换为子串B(m),必须考虑在对A、B改动最小的条件下构造出修改次数最少的步骤的所有可能性;记C(i,j)为A(i)变成B(j)的最小代价,那么关注的就是这个最小代价而不是改动本身。
C(n,m)和某个C(i,j)之间的关系,有4种可能性:
痛定思痛,开启算法之路(十四)_第15张图片
(注意上面最后一个等式后面的 c( i, j )的c是小写的,它仅表示i=n,j=m时ai是否等于bj,如红色箭头所指)

如上所示利用归纳法减少规模计算,但是每次减少的仅仅是常数级的,如果直接按如上计算那其代价将是指数级别的;看到这个归纳式我们应该想到之前遇到的背包问题,同样缩小规模计算减少的只是常数级别;但是我们发现其总体可能性取值范围是有限的O(nm)(所以其时间及空间复杂度均为O(nm)),每次递归计算都包含了过多的重复计算,所以同样我们可以借助一个二维表格来存储记录之前已经计算出的值,每次往后递归时直接取值即可;
(如下图所示每一个C(i,j)的值来源于其左上角三个元素的比较取值并计算,即标黑的三个方块)
痛定思痛,开启算法之路(十四)_第16张图片
痛定思痛,开启算法之路(十四)_第17张图片

        类比背包问题加上这个序列比较问题,当一个问题的解取决于其较小问题时,动态规划将十分有用;
        在动态规划中,往往是用表来存储已得到的结构,并以某种顺序(通常是按行访问)扫描整张表,
        从而导致平方级的运行时间(而不是指数级别);当然动态规划一般不如分治法等其他方法。

你可能感兴趣的:(algorithm,Thinking)