几个常见排序算法的实现

冒泡排序

突然心血来潮想要自己再重新实现一下当时学过的几个排序算法,然后马上就打开eclipse开始敲代码。说到排序,首先想到的就是冒泡,记得这个算法还是大一入学不久,编程入门老师讲过的。当时还是用c++实现的冒泡,现在虽然知道冒泡的原理,但是很多实现细节都感觉有些模糊了,今天就重新用Java实现一下,加深一下对其的理解。代码详见冒泡排序java实现
冒泡排序的原理十分简单也很容易理解,比较两个相邻的元素,如果前者比后者大,则交换他们两个。有一点要理解,就是排序的趟数。外层的for循环就是代表排序的趟数,n个元素的数组,排n-1趟就够了,比如数组[3,2]有两个元素,排一趟就有序了。内层的for循环代表的是每一趟排序需要比较的次数,因为第k趟排序结束后,那么最后K个元素就已经有序了,所以每一趟排序后下一趟排序所需要比较的次数是在不断减少的,每排一趟就减少一次。这个在代码中有详细的注释,我想应该不难理解。

插入排序

接下来谈一下插入排序,插入排序也算是比较经典的算法,理解起来很容易,举个例子,我们平时都玩过扑克吧,我们把扑克牌按照大小顺序排列起来的时候,就是插入排序的思想。原理是这样,但是具体实现起来还是要动一番脑子的,Java代码在这里插入排序java实现。注释很多,详细解释了每一个实现细节,现在再回头看看,觉得思路十分清晰。

归并排序

代码在这里归并排序Java实现,归并排序是分治法的应用,思想就是把一个复杂的大问题分解成一个个简单的小问题求解,再把解合并得到原问题的解,这种思想也是十分机智,应用也是十分广泛,小到一个排序算法,大到MapReduce框架。所以理解这种思想我觉得也是大有益处。其实在学数据结构的时候就学过这个算法,但是当时理解的也不是很透彻,甚至我连名字都叫反了(我一直都叫的并归排序(┬_┬)),现在不会再叫错了。那什么是归并呢,“”就是递归,指把待排序数组递归分解,“”就是合并,指把各个小数组合并。考虑把一个数组分解成两个部分,如果这两个数组内部数据是有序的,那么就可以把这两个数组合并排序。那么如何合并呢,就是新建一个临时数组temp,比较两个数组最前面的数,取出较小的那个放入temp,取了后相应的指针就往后移一位,然后再比较,直至一个数组为空,最后把另一个数组的剩余部分复制过来。
找了张图解释下归并排序的过程,很直观。
归并排序过程

快速排序

唉,说了这么多,终于说到快速排序了,还是先把代码贴出来吧,请点击快速排序java实现。快速排序印象当中也是大一的时候学的,也是c++版的,当时是真的模模糊糊,根本没弄明白这个算法的原理。现在理解了它的原理,就用Java重新实现一次吧。这个快排代码是我根据《算法导论》里的实现方式写的,看起来可能跟其他的实现不太一样,也可能更难理解一些,不过我觉得参考我的注释还是能看懂代码的。这个实现中的划分函数partition()只包含一层循环,在有些实现当中可能会看到两层循环,其实本质上是一样的。快排也是基于分治策略的(伟大吧),原理也是递归的分解大数组,但是快排不需要合并,因为快排是基于原址的,不像归并排序还需要一个临时数组。我们把快排的分解大数组称为划分(partition)这个划分要根据一定的原则,先要选定一个元素值作为标准,根据这个标准来划分,比如比这个标准小的元素都放左边,比这个标准大的元素都放右边。这样递归的进行下去,最终就会得到有序的数组。
同样的,还是图来的直观易懂。
快速排序过程

关于递归

归并排序和快速排序都用到了递归的思想,递归到底是什么意思呢,解释是这样的:如果一个函数在内部调用自己本身,这个函数就是递归函数。没错,就是这么简单。而且递归函数定义简单,逻辑清晰,容易让人理解。但是递归函数存在一个问题,这个问题跟计算机的工作原理有关,请看下面:

在计算机中,函数调用是通过栈(stack)这种数据结构实现的,每当进入一个函数调用,栈就会加一层栈帧,每当函数返回,栈就会减一层栈帧。由于栈的大小不是无限的,所以,递归调用的次数过多,会导致栈溢出。

在理论上讲,所有的递归函数都可以写成循环的形式,但是逻辑就不如递归的形式清晰。有一种解决办法,请看:

解决递归调用栈溢出的方法是通过尾递归优化,事实上尾递归和循环的效果是一样的,所以,把循环看成是一种特殊的尾递归函数也是可以的。尾递归是指,在函数返回的时候,调用自身本身,并且,return语句不能包含表达式。这样,编译器或者解释器就可以把尾递归做优化,使递归本身无论调用多少次,都只占用一个栈帧,不会出现栈溢出的情况。可是非常遗憾,大多数编程语言没有针对尾递归做优化,即使把函数改成尾递归方式,也会导致栈溢出。

关于这个优化递归的问题也是我前几天在学python的时候看到的,我自己也不是十分理解,但是对于今后的编程兴许能起到一些作用吧。

总结

终于要写完了,这几个算法虽然看上去简单,实现起来也还是挺费脑子的,就这几个算法的代码,我写了整整一天的时间,不过也算是把思路理清晰了,给自己今后的学习做了一点积累。在这里给大家分享一下自己的心得,希望能和大家共同进步!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值