看动画学算法之:排序-冒泡排序
简介
排序可能是所有的算法中最最基础和最最常用的了。排序是一个非常经典的问题,它以一定的顺序对一个数组(或一个列表)中的项进行重新排序。
排序算法有很多种,每个都有其自身的优点和局限性。
今天我们来学习最最简单的冒泡排序算法。
冒泡排序的原理
冒泡排序的原理很简单,我们想象一下一个一个的气泡上浮的过程。
假设我们有八个数字 29,10,14,37,20,25,44,15 要进行排序。
我们先用一个动画图来直观的观察一下整个冒泡排序的过程:
排序共进行八轮,每一轮都会做两两比较,并将较大的元素右移,就像冒泡一下。
一轮结束之后,八个元素中最大的那个元素44将会移动到最右边。
然后再重复其他的几轮。最终得到一个完全排序的数组。
也可以这样看:
第一轮是将八个元素中的最大值44交换移动到最右位置。
第二轮是将八个元素中的次大值37交换移动到最右位置。
以此类推。
冒泡排序算法的java实现
我们先看一个最简单的冒泡算法:
public class BubbleSort {
public void doBubbleSort(int[] array){
log.info("排序前的数组为:{}",array);
//外层循环,遍历所有轮数
for(int i=0; i< array.length-1; i++){
//内层循环,两两比较,选中较大的数字,进行交换
for(int j=0; j<array.length-1; j++){
if(array[j]>array[j+1]){
//交换两个数字
int temp = array[j];
array[j] = array[j+1];
array[j+1] = temp;
}
}
log.info("第{}轮排序后的数组为:{}", i+1, array);
}
}
public static void main(String[] args) {
int[] array= {29,10,14,37,20,25,44,15};
BubbleSort bubbleSort=new BubbleSort();
bubbleSort.doBubbleSort(array);
}
}
这个算法就是两层遍历,外层遍历表示的是进行的轮数。内层遍历表示的是每一轮的排序。
我们看下输出结果:
冒泡算法的第一次改进
分析上面的遍历过程,我们可以发现,第一次排序之后,44已经放到最右边的位置了,已经排好序了。
第二次排序之后,37也已经排好序了。每过一轮,内部循环需要比较的次数就可以减一。
这就意味着,在内部循环的时候,我们只需要进行array.length-i-1次比较就可以了。
修改代码如下:
public class BubbleSort1 {
public void doBubbleSort(int[] array){
log.info("排序前的数组为:{}",array);
//外层循环,遍历所有轮数
for(int i=0; i< array.length-1; i++){
//内层循环,两两比较,选中较大的数字,进行交换, 最后的i个数字已经排完序了,不需要再进行比较
for(int j=0; j<array.length-i-1; j++){
if(array[j]>array[j+1]){
//交换两个数字
int temp = array[j];
array[j] = array[j+1];
array[j+1] = temp;
}
}
log.info("第{}轮排序后的数组为:{}", i+1, array);
}
}
public static void main(String[] args) {
int[] array= {29,10,14,37,20,25,44,15};
BubbleSort1 bubbleSort=new BubbleSort1();
bubbleSort.doBubbleSort(array);
}
}
运行结果:
可以看到运行结果其实没什么不同,只不过我们少做了几次比较。
冒泡算法的第二次改进
从上面的结果,我们可以看到实际上第5轮排序过后就已经排序完成了。但是我们仍然进行了第6,7次排序。
有没有什么办法可以判断排序是不是已经完成了呢?
我们考虑一下,在内部循环中,我们是进行两两比较,然后交换位置。
如果在某一次遍历中,没有进行交互,这就意味着排序已经完成了。
所以我们可以再引入一个flag来做判断。
public class BubbleSort2 {
public void doBubbleSort(int[] array){
log.info("排序前的数组为:{}",array);
//外层循环,遍历所有轮数
for(int i=0; i< array.length-1; i++){
//添加一个flag,如果这一轮都没有排序,说明排序已经结束,可以提前退出
boolean flag=false;
//内层循环,两两比较,选中较大的数字,进行交换, 最后的i个数字已经排完序了,不需要再进行比较
for(int j=0; j<array.length-i-1; j++){
if(array[j]>array[j+1]){
//交换两个数字
int temp = array[j];
array[j] = array[j+1];
array[j+1] = temp;
flag = true;
}
}
log.info("第{}轮排序后的数组为:{}", i+1, array);
if(!flag)
{
log.info("本轮未发生排序变化,排序结束");
return;
}
}
}
public static void main(String[] args) {
int[] array= {29,10,14,37,20,25,44,15};
BubbleSort2 bubbleSort=new BubbleSort2();
bubbleSort.doBubbleSort(array);
}
}
运行结果如下:
从结果我们可以看到少了一轮排序,提升了速度。
冒泡排序的时间复杂度
虽然我们可以在冒泡的时候进行一些性能优化,但是基本上还是要进行嵌套的两次遍历。遍历次数近似的=n*n,所以冒泡排序算法的时间复杂度是O(n²)。
本文的代码地址:
本文已收录于 http://www.flydean.com/algorithm-bubble-sort/
最通俗的解读,最深刻的干货,最简洁的教程,众多你不知道的小技巧等你来发现!
欢迎关注我的公众号:「程序那些事」,懂技术,更懂你!
原文地址:https://www.cnblogs.com/flydean/p/algorithm-bubble-sort.html
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 解决Laravel5.5下的toArray问题
- Laravel基础_关于view共享数据的示例讲解
- 解决在laravel中leftjoin带条件查询没有返回右表为NULL的问题
- 解决laravel资源加载路径设置的问题
- laravel实现前后台路由分离的方法
- laravel5.1框架model类查询的实现方法
- laravel-admin 管理平台获取当前登陆用户信息的例子
- Yii框架Session与Cookie使用方法示例
- laravel 实现登陆后返回登陆前的页面方法
- Laravel 登录后清空COOKIE的操作方法
- 使用laravel和ajax实现整个页面无刷新的操作方法
- Laravel 读取 config 下的数据方法
- PHP实现数组根据某个字段进行水平合并,横向合并案例分析
- laravel框架上传图片实现实时预览功能
- PHP 多进程与信号中断实现多任务常驻内存管理实例方法