算法学习-2
2017-01-17 16:49
232 查看
当我们在没有学习算法之前,在做排序问题的时候,首先想到的应该是选择排序或者是冒泡排序。其中选择排序可能是大脑最自然的思考方式。
很小的时候上体育课,在操场上,同学们零零散散站着,老师扫了一眼,叫了最高个同学站在到前面,然后再在剩余同学中选择最高的依次站成一排,重复此过程直到最后一个同学也站入队伍了后,队伍也就按高矮顺序排好了,自此我们潜意识的学习了选择排序法。一碰到排序问题,潜意识的联想到这种排序方式。
在我们进入高中或者大学,长大懂事了,上体育课时知道要站成一排一排,但一开始由于都喜欢跟自己玩的好的伙伴站在一起,站成的队列并没有按高低顺序站好。这时老师也变懒了,不会再一个个指定位置站了,只会说一句,你们按高矮顺序排列。我们就自个看看左右,如果右边的比自己矮就跟他换一个位置,这样过了一会队伍就按右到左从高到矮顺序排好了。这种排序方法差不多就是冒泡排序。
来源于实际生活中的体验能很好的被大脑所参考,所以我们在写排序算法,估计大多数人会写出这两种方法,而对于像插入排序,归并排序等等算法要是没有经过一定的训练和深入的了解,就算当时学会了,过了一段时间,想要写出来应该会很难。
以上都是题外话,进入主题,写出归并排序的伪代码:
归并排序是一种采用分治策略的排序方法,分治策略是学习算法的一个入门级的方法,讲起来很简单,当一个问题的求解过程规模比较大,步骤比较多的时候,把它拆分成2个或多个规模较小但是跟原问题一样的问题,然后分别求解,并把结果合并得到未拆分前问题的答案。把拆分后的子问题,重复这个过程,就是书面上说的递归操作,就可以把真正需要求解的问题分解成规模极小的问题,这些问题的答案往往是显然的。
如果只是会说出分治策略的思想,就觉得自己能很好的写出归并排序程序,就有点图样图森破了。
下面就按照上面表述的思路来试着写伪代码
通过实践以后可知分治法最难的部分应该在合并结果上,这也是需要我们自己去思考编程最多的一部分。
下面贴出书本上的伪代码,从而可以比较一下自己写的代码有哪些不足及不好的地方
合并部分单独抽出来处理,可见编写归并排序算法主要实现的逻辑代码还是在归并操作,分治策略只是一个递归结构,了解就很简单。
该例程更加一般化,不但可以排序整个数组A(p=1,r=A.length),也可排序A的部分元素,在伪代码的编写上也更加清晰易懂。在合并操作中使用了一个小技巧,在两个子数组后面插入了一个哨兵元素防止对数组取值的时候越界。
再看出之前写的代码会发现出现数组越界问题
修改:
虽然正确,代码不够优雅简洁。
不要以为你会实现几个功能就说你会开发,会编程。
很小的时候上体育课,在操场上,同学们零零散散站着,老师扫了一眼,叫了最高个同学站在到前面,然后再在剩余同学中选择最高的依次站成一排,重复此过程直到最后一个同学也站入队伍了后,队伍也就按高矮顺序排好了,自此我们潜意识的学习了选择排序法。一碰到排序问题,潜意识的联想到这种排序方式。
在我们进入高中或者大学,长大懂事了,上体育课时知道要站成一排一排,但一开始由于都喜欢跟自己玩的好的伙伴站在一起,站成的队列并没有按高低顺序站好。这时老师也变懒了,不会再一个个指定位置站了,只会说一句,你们按高矮顺序排列。我们就自个看看左右,如果右边的比自己矮就跟他换一个位置,这样过了一会队伍就按右到左从高到矮顺序排好了。这种排序方法差不多就是冒泡排序。
来源于实际生活中的体验能很好的被大脑所参考,所以我们在写排序算法,估计大多数人会写出这两种方法,而对于像插入排序,归并排序等等算法要是没有经过一定的训练和深入的了解,就算当时学会了,过了一段时间,想要写出来应该会很难。
以上都是题外话,进入主题,写出归并排序的伪代码:
归并排序是一种采用分治策略的排序方法,分治策略是学习算法的一个入门级的方法,讲起来很简单,当一个问题的求解过程规模比较大,步骤比较多的时候,把它拆分成2个或多个规模较小但是跟原问题一样的问题,然后分别求解,并把结果合并得到未拆分前问题的答案。把拆分后的子问题,重复这个过程,就是书面上说的递归操作,就可以把真正需要求解的问题分解成规模极小的问题,这些问题的答案往往是显然的。
如果只是会说出分治策略的思想,就觉得自己能很好的写出归并排序程序,就有点图样图森破了。
下面就按照上面表述的思路来试着写伪代码
//排序A[1,..,n] //MERGE_SORT是一个procedure,是一个函数,是我们需要编写的函数,同时我们也要告诉自己他是一个已经实现的函数,他可以正确的排序传给它的数组参数。 MERGE_SORT(A) n = A.length if n==1 return //只有一个元素,必然是排好序的,也不需要分解成子问题求解,因此返回 else //从中间等量分解为2个规模差不多的需要排序的数组, A1 = A[1,..,n/2] A2 = A[n/2+1,..,n] //排序A1,A2 MERGE_SORT(A1) MERGE_SORT(A2) //A1,A2已排序,合并结果 K=1,i=1;j=1 for k = 1 to n if i<=A1.length and A1[i] <= A2[j] A[k] = A1[i] i = i+1 else A[k] = A2[j] j = j+1 //合并结束,A已经排好序
通过实践以后可知分治法最难的部分应该在合并结果上,这也是需要我们自己去思考编程最多的一部分。
下面贴出书本上的伪代码,从而可以比较一下自己写的代码有哪些不足及不好的地方
MERGE_SORT(A,p,r) if p < r q = (p + r)/2 MERGE_SORT(A,p,q) MERGE_SORT(A,q+1,r) MERGE(A,p,q,r) MERGE(A,p,q,r) n1 = q - p + 1 n2 = r - q let L[1..n1+1] and R[1..n2+1] be new array for i=1 to n1 L[i] = A[p+i-1] for j=1 to n2 R[j] = A[q+j] L[n1+1] = ∞ R[n2+1] = ∞ i=1 j=1 for k=p to r if L[i] <= R[j] A[k] = L[i] i=i+1 else A[k] = R[j] j=j+1
合并部分单独抽出来处理,可见编写归并排序算法主要实现的逻辑代码还是在归并操作,分治策略只是一个递归结构,了解就很简单。
该例程更加一般化,不但可以排序整个数组A(p=1,r=A.length),也可排序A的部分元素,在伪代码的编写上也更加清晰易懂。在合并操作中使用了一个小技巧,在两个子数组后面插入了一个哨兵元素防止对数组取值的时候越界。
再看出之前写的代码会发现出现数组越界问题
for k = 1 to n if i<=A1.length and A1[i] <= A2[j] A[k] = A1[i] i = i+1 else A[k] = A2[j] j = j+1//当A1中最后一个元素最大时,A2把最后一个元素插入到A中后,会执行到这,然后在最后一次循环k=n时,A2[j]越界
修改:
for k = 1 to n if j > A2.length //防止越界 A[k] = A1[i] i = i+1 else if i > A1.length //防止越界 A[k] = A2[j] j = j+1 else if A1[i] <= A2[j] A[k] = A1[i] i = i+1 else A[k] = A2[j] j = j+1
虽然正确,代码不够优雅简洁。
不要以为你会实现几个功能就说你会开发,会编程。
相关文章推荐
- 浅析时钟向量算法
- 书评:《算法之美( Algorithms to Live By )》
- 动易2006序列号破解算法公布
- C#递归算法之分而治之策略
- Ruby实现的矩阵连乘算法
- C#插入法排序算法实例分析
- C#算法之大牛生小牛的问题高效解决方法
- C#算法函数:获取一个字符串中的最大长度的数字
- 超大数据量存储常用数据库分表分库算法总结
- C#数据结构与算法揭秘二
- C#冒泡法排序算法实例分析
- 算法练习之从String.indexOf的模拟实现开始
- C#算法之关于大牛生小牛的问题
- C#实现的算24点游戏算法实例分析
- 经典排序算法之冒泡排序(Bubble sort)代码
- Android数据加密之异或加密算法的实现方法
- c语言实现的带通配符匹配算法
- 浅析STL中的常用算法
- 算法之排列算法与组合算法详解
- C++实现一维向量旋转算法