排序算法系列:冒泡排序与双向冒泡排序

您所在的位置:网站首页 冒泡排序有什么用 排序算法系列:冒泡排序与双向冒泡排序

排序算法系列:冒泡排序与双向冒泡排序

2024-07-14 11:53| 来源: 网络整理| 查看: 265

概述

排序算法应该算是一个比较热门的话题,在各个技术博客平台上也都有一些博文进行了一定程度的讲解。但还是希望能从自我完善的角度出发,可以更详细、全面、形象地表达这些算法的精髓。本文就先从最简单的冒泡排序开始说起,别说你已经彻底了解了冒泡排序算法(虽然一开始我也是这样以为的)。

版权说明

著作权归作者所有。 商业转载请联系作者获得授权,非商业转载请注明出处。 本文作者:Q-WHai 发表日期: 2016年01月29日 本文链接:https://qwhai.blog.csdn.net/article/details/50591859 来源:CSDN 更多内容:分类 >> 算法与数学

目录 文章目录 概述版权说明目录@[toc] 冒泡排序教科书版算法原理算法实现算法过程图复杂度分析 标准版算法原理算法实现算法过程图复杂度分析 改进版优化方案算法实现复杂度分析 算法评价 双向冒泡排序算法原理算法步骤算法实现复杂度分析算法评价 RefGitHub源码链接: 冒泡排序

冒泡排序(Bubble Sort)是一种交换排序1,它的基本思想是:两两比较相邻记录的关键字,如果反序则交换,直到没有反序的记录为止。                                        — 《大话数据结构》

教科书版 算法原理

一般来说,我们学习排序算法的入门都是从教科书中获得。而教科书上讲解的内容都是一些相对比较简单的,其目的是为了让人能够很好地去理解它,也可以让人能够很容易地编写出可行的代码。   教科书版冒泡排序的原理是对数组进行两层循环遍历。让数组中较小的关键字能够较快地移动到数组的顶部,从而当两层循环结束,排序即可完成。

算法实现 public void sort(int[] array) { int arrayLength = array.length; for (int i = 0; i array[j + 1]) { ArrayUtils.swap(array, j, j + 1); } } } } 算法过程图

如果你说这两个算法代码差不多,那就先把代码比对清楚,你会发现不同的地方还是很多的。通过上面的代码实现,我们可以很容易地画出如下过程图。(这次就感觉像是一个小气泡,在一点一点向上冒了。_)

![这里写图片描述](https://img-blog.csdn.net/20160128110715936) 图-2 冒泡排序-标准版 复杂度分析 排序方法时间复杂度空间复杂度稳定性复杂性平均情况最坏情况最好情况冒泡排序(标准版)O(n2)O(n2)O(n)O(1)稳定简单 改进版 优化方案

通过对上面两种冒泡排序算法的学习,我们可以看到不管是哪一种算法,都不能很好地避免对一个已经有序的数组减少比较操作(只是不用做交换处理)。   现在,我们想到一种方法,使用一个标志位,来标识当前数组是否已经有序。如果无序,则继续冒泡排序;如果已经有序,则退出排序算法。这样就可以很好地规避掉一些不必要的比较操作。

算法实现 private void core(int[] array) { boolean status = true; // 记录是否发生交换信息 int arrayLength = array.length; for (int i = 0; i = i; j--) { if (array[j] > array[j + 1]) { ArrayUtils.swap(array, j, j + 1); status = true; } } } } 复杂度分析 排序方法时间复杂度空间复杂度稳定性复杂性平均情况最坏情况最好情况冒泡排序(改进版)O(n2)O(n2)O(1)O(1)稳定简单 算法评价

从冒泡的原理上,我们可以知道,从前向后进行循环遍历交换和从后向前进行循环遍历交换的代价和逻辑是一致的,那么我们也就可以从后向前进行冒泡排序,代码就里就不再给出了。   在单向冒泡排序算法中,存在着一个著名的“乌龟问题”2。   而在排序过程中,又主要是这个过程耗费了大量时间。关于具体的实例在下面的“双向冒泡排序”算法中体现。

双向冒泡排序 算法原理

在基于冒泡排序的基础上,我们知道,无论是从前向后遍历交换,还是从后向前遍历交换,对程序的逻辑和性能的代价都是不影响的,那么我们就可以让一部分情况下从前向后遍历交换,另一部分情况从后向前遍历交换。

![双向冒泡排序算法](https://img-blog.csdn.net/20160120101221621) 图-3 双向冒泡排序 算法步骤 比较相邻两个元素的大小。如果前一个元素比后一个元素大,则两元素位置交换对数组中所有元素的组合进行第1步的比较奇数趟时从左向右进行比较和交换偶数趟时从右向左进行比较和交换当从左端开始遍历的指针与从右端开始遍历的指针相遇时,排序结束 算法实现

代码如下:

private void core(int[] array) { int arrayLength = array.length; int preIndex = 0; int backIndex = arrayLength - 1; while(preIndex = backIndex) { break; } backSort(array, backIndex); backIndex--; } } // 从前向后排序 private void preSort(int[] array, int length, int preIndex) { for (int i = preIndex + 1; i array[i]) { ArrayUtils.swap(array, preIndex, i); } } } // 从后向前排序 private void backSort(int[] array, int backIndex) { for (int i = backIndex - 1; i >= 0; i--) { if (array[i] > array[backIndex]) { ArrayUtils.swap(array, i, backIndex); } } } 复杂度分析 排序方法时间复杂度空间复杂度稳定性复杂性平均情况最坏情况最好情况双向冒泡排序O(n2)O(n2)O(n)O(1)稳定简单 算法评价

如果单纯从时间复杂度上来讨论,双向冒泡排序与冒泡排序算法复杂度是一致的。不过在双向冒泡排序算法中,我们引入了一些变量,以控制程序流程,在空间复杂度上虽然都O(1),不过双向冒泡排序还是会大一些(至少有多了两个位置指针)。从代码的复杂度上,双向冒泡排序算法会大一些。   不过在上面的冒泡排序算法中,我们了解到冒泡排序算法有一个“乌龟问题”。正是因为这个原因,我们引入了双向冒泡排序算法。这里我们可通过一个实例更加象形地了解它。   假设我们现在有一个待排序序列**{6, 5, 4, 3, 2, 1}**。分别使用单向和双向冒泡排序对其进行排序,两种排序算法的过程如下(左图为单向冒泡,右图为双向冒泡):

步骤单向冒泡排序双向冒泡排序原始状态[6, 5, 4, 3, 2, 1][6, 5, 4, 3, 2, 1]第 1 趟[1, 6, 5, 4, 3, 2][1, 6, 5, 4, 3, 2]第 2 趟[1, 2, 6, 5, 4, 3][1, 5, 4, 3, 2, 6]第 3 趟[1, 2, 3, 6, 5, 4][1, 2, 5, 4, 3, 6]第 4 趟[1, 2, 3, 4, 6, 5][1, 2, 4, 3, 5, 6]第 5 趟[1, 2, 3, 4, 5, 6][1, 2, 3, 4, 5, 6] Ref 《大话数据结构》《算法导论》[ 白话经典算法系列之一 冒泡排序的三种实现 ] GitHub源码链接: https://github.com/qwhai/algorithms-sort

所谓交换,就是根据序列中两个记录键值的比较结果来对换这两个记录在序列中的位置,交换排序的特点是:将键值较大的记录向序列的尾部移动,键值较小的记录向序列的前部移动。 ↩︎

乌龟问题说的是:假设我们需要将序列A按照升序序列排序。序列中的较小的数字又大量存在于序列的尾部,这样会让小数字在向前移动得很缓慢。 ↩︎



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3