图解冒泡排序及算法优化(Java实现)
冒牌排序
基本思想
定义:冒泡排序的英文是bubblesort,它是一种基础的交换排序
原理:每次比较两个相邻的元素,将较大的元素交换至右端 (升序排序)
思路:相邻的元素两两比较,当一个元素大于右侧相邻元素时,交换它们的位置;当一个元素小于或等于右侧相邻元素时,位置不变
案例分析:
1、初始的无序数列 {5,8,6,3,9,2,1,7}
,希望对其升序排序
2、按照思路分析:
在经过第一轮交换后,最大的数 9 冒泡到了最右边
到此为止,所有元素都是有序的了,这就是冒泡排序的整体思路。
3、冒泡排序是一种稳定排序,值相等的元素并不会打乱原本的顺序。由于该排序算法的每一轮都要遍历所有元素,总共遍历(元素数量-1)轮,所以平均时间复杂度是O(n2)。
代码实现
第 1 版代码
public static void bubbleSort(int[] arr){
if (arr == null || arr.length == 0) return;
for (int i = 0; i < arr.length - 1; i++) {
for (int j = 0; j < arr.length - 1 -i; j++) {
int tmp = 0;
//升序排序>,降序排序<
if (arr[j] > arr[j + 1]){
tmp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = tmp;
}
}
}
}
使用双循环进行排序。外部循环控制所有的回合,内部循环实现每一轮的冒泡处理,先进行元素比较,再进行元素交换。
第 2 版代码
仍以无序数列 {5,8,6,3,9,2,1,7}
为例,我们发现在第 6 轮的时候,数列已经是有序了,但冒泡排序仍然进行了第7轮,可以做一个小优化,在外层循环设置一个哨兵标记isSorted
,默认有序,内层循环如果发生交换,则仍为无序
public static void bubbleSort(int[] arr){
if (arr == null || arr.length == 0) return;
for (int i = 0; i < arr.length - 1; i++) {
//是否已经有序的标记,默认有序
boolean isSorted = true;
for (int j = 0; j < arr.length - 1 -i; j++) {
int tmp = 0;
//升序排序>,降序排序<
if (arr[j] > arr[j + 1]){
tmp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = tmp;
//发生元素交换,序列仍是无序状态
isSorted = false;
}
}
if (isSorted){
break;
}
}
}
以isSorted
作为标记。如果在本轮排序中,元素有交换,则说明数列无序;如果没有元素交换,则说明数列已然有序,然后直接跳出大循环。
第 3 版代码
以新的无序数列 {3,4,2,1,6,7,8,9}
为例,发现前半部分是无序的,而后半部分[6 ,7 ,8 ,9]是有序区间
如果以上面的第2版代码执行,会发现只有前半部分的比较是有意义的,而后半部分的有序区间的比较是无意义的
怎么避免这种情况?那么可以在每一轮的排序后,记录下来最后一次元素交换的位置,该位置即为无序数列的边界,再往后就是有序区
public static void bubbleSort(int[] arr){
if (arr == null || arr.length == 0) return;
//记录记录下来最后一次元素交换的位置
int lastExchangeIndex = 0;
//无序数列的边界,每次比较只需要比到这里为止
int sortBorder = arr.length-1;
for (int i = 0; i < arr.length - 1; i++) {
//是否已经有序的标记,默认有序
boolean isSorted = true;
for (int j = 0; j < sortBorder; j++) {
int tmp = 0;
//升序排序>,降序排序<
if (arr[j] > arr[j + 1]){
tmp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = tmp;
//发生元素交换,序列仍是无序状态
isSorted = false;
//更新为最后一次交换元素的位置
lastExchangeIndex = j;
}
}
//更新无序数列的边界
sortBorder = lastExchangeIndex;
if (isSorted){
break;
}
}
}
在第3版代码中,sortBorder就是无序数列的边界。在每一轮排序过程中,处于sortBorder之后的元素就不需要再进行比较了,肯定是有序的
算法升级
分析
冒泡算法的每一轮都是从左到右来比较元素,进行单向的位置交换的,是单向的
以新的无序数列 {2,3,4,5,6,7,8,1}
为例,按照冒泡排序的算法,排序过程如下:
事实上,前面的[2,3,4,5,6,7,8]已经是有序了,只有元素1的位置不正确,却要进行7轮交换。可以将算法从单向交换改为双向交换,排序过程就像钟摆一样,第1轮从左到右,第2轮从右到左,第3轮再从左到右……,这就是鸡尾酒排序
.
鸡尾酒排序
图解鸡尾酒排序:
经过2轮交换(虽然实际上已经有序,但是流程并没有结束),进入第3轮交换从左到右进行,1和2比较,位置不变;2和3比较,位置不变;3和4比较,位置不变……6和7比较,位置不变。
没有元素位置进行交换,证明已经有序,排序结束。
public static void cockTailSort(int[] arr){
if (arr == null || arr.length == 0) return;
// 记录右侧最后一次交换的位置
int lastRightExchangeIndex = 0;
// 记录左侧最后一次交换的位置
int lastLeftExchangeIndex = 0;
// 无序数列的右边界,每次比较只需要比到这里为止
int rightSortBorder = arr.length - 1;
// 无序数列的左边界,每次比较只需要比到这里为止
int leftSortBorder = 0;
//i设置为1,代表从第1轮开始
for (int i = 1; i < arr.length; i++) {
boolean isSorted = true;
//奇数,从左到右
if (i % 2 != 0) {
for (int j = leftSortBorder; j < rightSortBorder; j++) {
if (arr[j] > arr[j + 1]) {
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
//发生元素交换,序列仍是无序状态
isSorted = false;
//更新为右侧最后一次交换元素的位置
lastRightExchangeIndex = j;
}
}
} else {
//偶数,从右到左
for (int j = rightSortBorder; j > leftSortBorder; j--) {
if (arr[j] < arr[j - 1]) {
int temp = arr[j];
arr[j] = arr[j - 1];
arr[j - 1] = temp;
//发生元素交换,序列仍是无序状态
isSorted = false;
//更新为左侧最后一次交换元素的位置
lastLeftExchangeIndex = j;
}
}
}
//更新无序数列的左边界
leftSortBorder = lastLeftExchangeIndex;
//更新无序数列的右边界
rightSortBorder = lastRightExchangeIndex;
if (isSorted) {
break;
}
}
}
优缺点:鸡尾酒排序的优点是能够在特定条件下,减少排序的回合数;而缺点也很明显,就是代码量几乎增加了1倍。
应用场景:无序数列中大部分元素已经有序
参考图书:《漫画算法—小灰的算法之旅》
欢迎关注我的掘金
图解冒泡排序及算法优化(Java实现)的更多相关文章
- 十大经典排序算法(java实现、配图解,附源码)
前言: 本文章主要是讲解我个人在学习Java开发环境的排序算法时做的一些准备,以及个人的心得体会,汇集成本篇文章,作为自己对排序算法理解的总结与笔记. 内容主要是关于十大经典排序算法的简介.原理.动静 ...
- 冒泡排序,冒泡性能优化--java实现
冒泡排序说明: 一次比较两个元素,如果他们的顺序错误就把他们交换过来. 重复地进行直到没有再需要交换,也就是说已经排序完成. 越小的元素会经由交换慢慢“浮”到数列的顶端. 冒泡排序算法的运作如下: 比 ...
- 粒子群优化算法及其java实现
憋了两周终于把开题报告憋出来了,再一次证明自己不适合搞学术,哎--,花了点时间把报告中提到的粒子群算法看了看,看了些资料,用java跑起来. 算法简介 粒子群算法最先由Barnhart博士和Kenne ...
- 必须知道的八大种排序算法【java实现】(一) 冒泡排序、快速排序
冒泡排序 冒泡排序是一种简单的排序算法.它重复地走访过要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来.走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成.这个 ...
- 8皇后以及N皇后算法探究,回溯算法的JAVA实现,非递归,循环控制及其优化
上两篇博客 8皇后以及N皇后算法探究,回溯算法的JAVA实现,递归方案 8皇后以及N皇后算法探究,回溯算法的JAVA实现,非递归,数据结构“栈”实现 研究了递归方法实现回溯,解决N皇后问题,下面我们来 ...
- 数据结构与算法【Java】05---排序算法总结
前言 数据 data 结构(structure)是一门 研究组织数据方式的学科,有了编程语言也就有了数据结构.学好数据结构才可以编写出更加漂亮,更加有效率的代码. 要学习好数据结构就要多多考虑如何将生 ...
- 几大排序算法的Java实现
很多的面试题都问到了排序算法,中间的算法和思想比较重要,这边我选择了5种常用排序算法并用Java进行了实现.自己写一个模板已防以后面试用到.大家可以看过算法之后,自己去实现一下. 1.冒泡排序:大数向 ...
- 查找算法(Java实现)
1.二分查找算法 package other; public class BinarySearch { /* * 循环实现二分查找算法arr 已排好序的数组x 需要查找的数-1 无法查到数据 */ p ...
- 常见排序算法(附java代码)
常见排序算法与java实现 一.选择排序(SelectSort) 基本原理:对于给定的一组记录,经过第一轮比较后得到最小的记录,然后将该记录与第一个记录的位置进行交换:接着对不包括第一个记录以外的其他 ...
随机推荐
- C#LeetCode刷题之#345-反转字符串中的元音字母(Reverse Vowels of a String)
问题 该文章的最新版本已迁移至个人博客[比特飞],单击链接 https://www.byteflying.com/archives/3935 访问. 编写一个函数,以字符串作为输入,反转该字符串中的元 ...
- C#LeetCode刷题之#26-删除排序数组中的重复项(Remove Duplicates from Sorted Array)
问题 该文章的最新版本已迁移至个人博客[比特飞],单击链接 https://www.byteflying.com/archives/3622 访问. 给定一个排序数组,你需要在原地删除重复出现的元素, ...
- 【Spring注解驱动开发】如何实现方法、构造器位置的自动装配?我这样回答让面试官很满意!
在 冰河技术 微信公众号前面的文章中,我们介绍了如何使用注解来自动装配Spring组件.之前将的都是在来的字段上添加注解,那有没有什么方法可以实现方法.构造器位置的自动装配吗?今天我们就一起来探讨下如 ...
- Java callback回调
package com.callback; public interface CSCallBack { public void process(String status); } package co ...
- 面试中HashMap链表成环的问题你答出了吗
HashMap作为老生常谈的问题,备受面试官的青睐,甚至成为了面试必问的问题.由于大量的针对HashMap的解析横空出世,面试官对HashMap的要求越来越高,就像面试官对JVM掌握要求越来越高一样, ...
- C#实践设计模式原则SOLID
理论跟实践的关系,说远不远,说近不近.能不能把理论用到实践上,还真不好说. 通常讲到设计模式,一个最通用的原则是SOLID: S - Single Responsibility Principle ...
- dcoker 小应用(二)
sudo yum install epel-release vi /etc/yum.repos.d/epel.repo use base url instead of mirror url ...
- linux下udev和mdev的使用
linux下设备文件系统有devfs.udev和mdev这三种. 一.devfs devfs是由Linux 2.4内核引入的,引入时被许多工程师给予了高度评价,它的出现使得设备驱动程序能自主地管理自己 ...
- 团队作业1:团队展示&选题(歪瑞古德小队)
目录 一.团队展示 1.1 队名:歪瑞古德小队 1.2 队员信息 1.3 项目描述 1.4 队员风采 1.5 团队分工 1.6 团队合照 1.7 团队特色 二.团队选题 2.1 Git仓库:https ...
- 第一篇Scrum冲刺博客
目录 一.Alpha 阶段认领的任务 二.明日成员的任务安排 三.整个项目预期的任务量 四.敏捷开发前的感想 五.团队期望 一.Alpha 阶段认领的任务 陈起廷 任务 预计时间 日记天气.心情选择 ...