您的位置:首页 > 其它

学以致用之冒泡排序

2016-03-23 23:17 253 查看
版权声明:本文为博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。 本文链接:https://blog.csdn.net/jk_wangzw/article/details/50967720

冒泡排序是一种看起来很简单的交换排序,尤其是其易记的特点,促使很多初学者只是简单地记住冒泡排序的某种代码实现,而忽略了对其更深层次的理解,本文旨在通过对冒泡排序的解析来帮助初学者真正理解冒泡排序的代码实现原理,如果你能在阅读完本文后不参考任何代码随手实现一种冒泡排序,那么这篇文章的目的便达到了。

下面是两段不同的冒泡排序实现代码,如下:

public void bubbleSort(int[] arrs){
for(int i=0; i<arrs.length-1; i++){
for(int j=0; j<arrs.length-1-i; j++){
if(arrs[j]>arrs[j+1]){
int temp = arrs[j];
arrs[j] = arrs[j+1];
arrs[j+1] = temp;
}
}
}
}
public void bubbleSort(int[] arrs){
for(int i=0; i<arrs.length-1; i++){
for(int j=arrs.length-1; j>i; j--){
if(arrs[j]<arrs[j-1]){
int temp = arrs[j];
arrs[j] = arrs[j-1];
arrs[j-1] = temp;
}
}
}
}

在继续往下阅读之前,请先暂缓几分钟,有必要先确保自己是否真的在思考两段代码的不同之处,而不是把注意力停留在代码的表象上。
显然上面两段代码都比较容易读懂,但是我们的追求不再是停留在“读”这个层次,而是应该具备去“写”任何一种可能的冒泡排序实现乃至一种与冒泡排序类似但是从未见过的算法的实现代码,那时死记硬背这种方式显然不会帮助我们太多。

好吧,现在你应该已经意识到理解算法代码实现原理的重要性,接下来正式进入我们的主题。

通过阅读上面两段冒泡排序的实现代码,不难发现两段代码中都有类似的for循环嵌套以及都要进行元素的交换这一过程,那么是不是意味着冒泡排序的实现必须要依赖for循环和适当的时候交换数组中的相邻元素呢,这个问题不难回答,作为一种交换排序,交换是永远无法回避的问题,但是for循环却并不是循环中唯一的选择,重点不在于选择了何种循环,而在于选择了循环,我们试图去看清冒泡排序的实现中真正必要的那些东西,而不是代码中展现在我们面前的那些表象。

现在回想一下冒泡排序的思想,事实上冒泡排序一直在做着相同的事情,总是试图把剩下的元素中最大或最小的那个元素移动到数组元素遍历方向所指向的那一端,但又不得不做些什么去保障之前的劳动成果不会被自己破坏。

上面的冒泡排序的思想中有两处需要重点关注的地方:

  • 目标元素的移动方向应该与元素被遍历的方向保持一致

  • 有且仅保证所有已排完序的目标元素不再参与排序

那么,冒泡排序的代码实现中又是如何去完成自身职责的呢,这才是本文的重点,那么应该先考虑上面两个关注点所带来的问题:

  • 是什么在控制目标元素的移动方向?

  • 如何在本轮排序中排除那些已经排完序的目标元素?

请仔细思考这两个问题,可能读到这里你已经有种豁然开朗的感觉,那么你可以选择不再继续往下阅读,去实践一下自己刚刚注意到的一些东西也是一个很好的选择。

对于第一个问题,答案就在下面两段代码的不同之处:

if(arrs[j]>arrs[j+1]){
...
}
if(arrs[j]<arrs[j-1]){
...
}

没错,目标元素的移动方向取决于你正在操作当前元素与上一个还是下一个元素进行比较,至于什么时候交换元素并不会影响元素的移动方向,只会决定元素的排序方式,是从小到大还是从大到小进行排序。

对于第二个问题,答案也很简单,其实正是外层for循环和内层for循环的循环条件协作的功劳,这也正是外层for循环唯一的职责。

未完待续。

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: