数据结构与算法(十六)冒泡排序和鸡尾酒排序
冒泡排序(Bubble Sort)是一种交换排序,它的基本思想是:两两比较相邻记录的关键字,如果反序则交换,以将当前序列的最小值交换到当前序列最前端为一轮结束,需要(length-1)轮,感觉数据是一个一个往上冒出来,这个现象类似“冒泡泡”,所以称为冒泡排序。
1、顺序存储结构实现冒泡排序
首先我们需要一个待排序的数组 arr[10] = {0, 9, 1, 5, 8, 3, 7, 4, 6, 2},其中arr[0]=0用做哨兵或临时变量所以不参与排序,所以数组下标从1开始,有效长度len=9,实际长度为10。
然后我们还需要交换数据的操作:
void swap_arr(int *arr, int i, int j)
{int temp = arr[i];arr[i] = arr[j];arr[j] = temp;
}
1.1、最简单的冒泡排序
void Simple_Bubble_Sort(int *arr, int len)
{for (int i=1; i<len; i++) for (int j=1; j<len; j++) if (arr[j] > arr[j+1]) /*如果反序就交换*/swap_arr(arr, j, j+1);
}
最简单的冒泡排序从严格意义来说,并不是冒泡排序,因为不符合“冒泡”的思想,它应该是最简单的交换排序。
它的思路:持续(length-1)轮,每一轮整个序列两两相邻记录的关键字都进行比较交换,这样整个序列一定会变为有序。
当然这样的思路让它的时间效率非常糟糕,它不区分最好和最坏情况,所以它的最好时间复杂度和最坏时间复杂度都为O(n²)。
这样简单的思路和实现,也是让初学者最容易理解和记住的排序算法。
1.2、标准的冒泡排序
void Bubble_Sort(int *arr, int len)
{for (int i=1; i<len; i++) /*冒泡到i位置,下轮循环度减一*/for (int j=len-1; j>=i; j--) /*从底向上,两两比较交换*/if (arr[j] > arr[j+1]) /*如果反序就交换*/swap_arr(arr, j, j+1);
}
每一轮下标 j 都会从8开始,将这一轮的最小值往上送到序列最顶端。
下图中,较小的数据如同气泡般慢慢浮到上面,因此就将该算法命名为冒泡算法。
标准的冒泡排序也需要进行(length-1)轮循环,但是总的比较和交换次数比最简单的冒泡排序要减少一半。
标准的冒泡排序的平均时间复杂度O(n²/2)。
1.2、优化冒泡排序
请问,标准的冒泡排序是否还有优化的空间?
假如序列变为arr[10] = {0, 2, 1, 3, 4, 5, 6, 7, 8, 9},我们一眼就能看出只要将1和2交换,整个序列就有序了,但标准的冒泡排序不知道,它仍会兢兢业业地一轮一轮去比较,而且比较次数还是非常多的,所以标准的冒泡排序在这个方面仍然可以优化。
优化方法也非常简单,当一轮比较之后都没有任何数据进行交换,那么说明序列已经有序了,不需要开始下一轮了。
在标准的冒泡排序的基础上,设置一个标志位,每开始一轮之前,先假设标志位为有序,如果途中交换过数据,则将标志位设置为无序,然后在每一轮结束后,判断标志位是否有序即可。
void Optimal_Bubble_Sort(int *arr, int len)
{ for (int i=1; i<len; i++) { /*flag用来标记序列是否已经完全排序好了*/bool flag = true; /*开始了新一轮的交换之前,先假设这一轮flag=true*/for (int j=len-1; j>=i; j--) { if (arr[j] > arr[j+1]) { /*如果反序就交换*/swap_arr(arr, j, j+1);flag = false; /*有交换说明还没排好*/}}if (flag == true) /*若flag=true说明这一轮都没有交换过数据*/break; /*就可以退出循环不用继续了*/}
}
这个优化避免了序列已经有序的情况下,仍无意义的、费时的循环判断,让冒泡排序在最好以及相对好的情况下的性能有了很高的提升,但在较坏和最坏情况下没有提升,优化后的冒泡排序的时间复杂度仍为O(n²/2)。
2、链式存储结构实现冒泡排序
首先链式存储结构采用单链表的形式:
typedef int DataType;
typedef struct LinkNode {DataType data; /*数据域*/LinkNode *next; /*后继指针*/
}*LinkList;
然后是交换结点数据的操作,该操作不交换结点:
/*交换结点里面的数据*/
void swap_link(LinkList node, LinkList next)
{int temp = node->data;node->data = next->data;next->data = temp;
}
如果实现冒泡排序的链式算法需要使用len,那么和数组的冒泡排序并无太大差别,差别在于单链表只能单向从头到尾的访问,而之前数组的冒泡排序是从尾到头的排序,所以需要修改一下思路:每次将最大值冒泡到链表的尾部,j 每次都从下标1开始,随着 i 的增大而 j 的判定范围往前面收缩。
void Optimal_Bubble_Sort_LinkList(LinkList Head, int len)
{for (int i = 1; i<len; i++) {bool flag = true;LinkList q = Head->next;for (int j = 1; j<len-i+1; j++, q = q->next) /*j<len-i+1:让j的范围随着i的增大往前收缩*/{if (q->data > q->next->data) {swap_link(q, q->next); flag = false; }}if (flag == true) /*若flag=true说明这一轮都没有交换过数据*/break;}
}
如果不想使用len来实现冒泡排序的链式算法,我们可以使用一个End指针来表示已经排好序的位置,将每一轮的遍历到End前一个结点为止,此时End前一个结点也排好序了,End就前移一位。
void Optimal_Bubble_Sort_LinkList(LinkList Head)
{LinkList p, q;LinkList End = NULL; /*一开始End为NULL,在数组外*/for (p=Head->next; p->next!=NULL; p=p->next) {bool flag = true;for (q=Head->next; q->next!=End; q=q->next) /*q遍历到End前一个结束*/{if (q->data > q->next->data) {swap_link(q, q->next);flag = false; }}End = q; /*End往前移一位*/if (flag == true) /*若flag=true说明这一轮都没有交换过数据*/break;}
}
3、鸡尾酒排序:冒泡排序的升级
鸡尾酒排序其实是冒泡排序的一种升级,并不在十大经典排序算法中。
鸡尾酒排序可以理解为双向冒泡排序,排序过程就像钟摆一样,第一轮先从左到右比较交换,把最大值冒泡到最右端,然后再从右到左把最小值冒泡到最左端,一轮可以把排序两个数据,也因此轮数变为原来的一半。
void Cocktail_Sort(int *arr, int len)
{for (int i=1; i<len/2; i++){/*先从左往右冒泡最大值*/bool flag = true;for (int j=i; j<len-i+1; j++) /*从第一个冒泡到最后一个*/{ if (arr[j] > arr[j+1]) {swap_arr(arr, j, j+1);flag = false; }}if (flag == true) break; /*若flag=true说明这一轮都没有交换过数据*//*再从右往左冒泡最小值*/flag = true;for (int j=len-i; j>=i; j--) /*经过从左往右冒泡最大值,右边有一个排好序了*/{ /*从倒数第二个开始,从右往左冒泡最小值到i位置*/if (arr[j] > arr[j+1]) {swap_arr(arr, j, j+1);flag = false; }}if (flag == true) break; /*若flag=true说明这一轮都没有交换过数据*/}
}
鸡尾酒排序相当于冒泡排序来说,优势是减少了排序的轮数,虽然鸡尾酒排序的最坏时间复杂度还是O(n²/2),但在大多数情况下(除最坏以外)都比冒泡排序效率高很多,而缺点就是代码量增加了。
鸡尾酒排序因为双向排序的缘故,不适合使用单链表来实现,但可以使用双向链表实现,这里就不再实现了。
数据结构与算法(十六)冒泡排序和鸡尾酒排序相关推荐
- 数据结构与算法(十二):八大经典排序算法再回顾
文章出自汪磊的博客,未经允许不得转载 一.排序的理解 提到排序大部分同学肯定第一时间想到int数组的排序,简单啊,所谓排序不就是将int数组按照从大到小或者从小到大排序吗,如果我有个数组存放的不是in ...
- 我的软考之路(六)——数据结构与算法(4)之八大排序
排序是编程的基础,在程序中会经常使用,好的排序方法可以帮助你提高程序运行的效率,所以学好排序,打好基础,对于程序的优化会手到擒来.无论你的技术多么强,如果没有基础也强不到哪去. 不多说了,我们直接进入 ...
- 数据结构与算法(六)
数据结构与算法 第一章 绪论 第二章 线性表 第三章 树与二叉树 第四章 图 第五章 查找 第六章 排序 文章目录 数据结构与算法 第六章 内部排序 一.基本概念 二.冒泡排序 三.快速排序 四.直接 ...
- 数据结构与算法(六)- 单向链表的反转
数据结构与算法(六)- 单向链表的反转 一.头节点插入法 /*** 反转单向链表(头插法)** 1.先定义一个节点reverseHead = new HeroNode()* 2.从头到尾遍历原来的链表 ...
- 数据结构和算法(十)递归-迷宫游戏
1. 数据结构和算法(十)递归-迷宫游戏 1.1 迷宫游戏 今天做一个简单的迷宫游戏,用二维数实现地图,让程序自动寻路的小游戏. 1.2 简单的迷宫 简单的迷宫 用二维数实现地图,找路策略:[右- ...
- 06_JavaScript数据结构与算法(六)单向链表
JavaScript 数据结构与算法(六)单向链表 认识链表 链表和数组 链表和数组一样,可以用于存储一系列的元素,但是链表和数组的实现机制完全不同. 数组 存储多个元素,数组(或列表)可能是最常用的 ...
- 排序算法之冒泡排序及鸡尾酒排序
目录 一.排序算法的分类 二.冒泡排序 1.原始的冒泡排序 2.改进一步的冒泡排序 3.更进一步的冒泡排序 三.鸡尾酒排序 四.完整测试代码 一.排序算法的分类 在介绍排序算法之前,我们先根据时间复杂 ...
- 冒泡排序和鸡尾酒排序(改进的冒泡排序)
冒泡排序 冒泡排序是最基本的排序算法,也是排序算法中的经典的算法,也是比较简单.容易理解的算法,而且还可以对其排序过程进行优化. 冒泡排序排序过程总是大数往前放,小数往后放,相当于气泡往上升,所以称作 ...
- 冒泡排序,鸡尾酒排序,选择排序
冒泡排序 改进 鸡尾酒排序 选择排序 冒泡排序 最好情况:Ο(n) 最坏情况:Ο(n2) 平均情况:Ο(n2) 辅助空间:Ο(C) 稳定性:稳定 冒泡排序是一种简单的算法,它重复地走访过要排序的元素, ...
最新文章
- HZOJ string
- hashmap的各种问题及答案
- mysql实现读写分离
- jQuery基础--样式篇(3)
- 论文浅尝 | 改善多语言KGQA的 Zero-shot 跨语言转换
- 容量耦合系数模型_期刊在线 | 基于ALE流固耦合方法的刷式密封泄漏特性理论与实验研究...
- 实战:自定义简易版SpringBoot
- java this关键字的使用_Java this 关键字的使用方法详解
- 使用dbstart 和dbshut 脚本来自动化启动和关闭数据库
- java api 版本控制_API 版本控制的几种方式
- rgss3a解包器_Rgss3a解包器下载
- 基于ZigBee cc2530单片机多传感器的智能阳台仿真设计与实现
- public static void main解释
- 大一c语言课设之图书管理系统
- PostgreSQL 聚合函数讲解 - 3 总体|样本 方差, 标准方差
- 用IDEA创建基于Spring Cloud的Feign的微服务:服务接口、服务提供者、服务使用者分离
- 为陶崇园争取正义懒人包1.0
- 熊分享-负重前行有我陪伴!
- java基础(多态的理解与应用)
- IDC时评:从巴黎圣母院大火看数据中心运维