Java数据结构和算法总结-冒泡排序、选择排序、插入排序算法分析
前言:排序在算法中的地位自然不必多说,在许多工作中都用到了排序,就像学生成绩统计名次、商城商品销量排名、新闻的搜索热度排名等等。也正因为排序的应用范围如此之广,引起了许多人深入研究它的兴趣,直至今天,排序算法已经出现了很多种。本篇博文主要介绍常见的八种排序算法,总得来说,不同的排序算法在不同的场景下都有着自己独特的优点,例如一下简单的冒泡排序、选择排序、插入排序不仅思路简单,有利于我们理解,而且在小规模的数据量的处理中并不逊色。接下来我们就一一分析一下各算法的优缺点以及时间复杂度。
本篇博文的所有代码已上传 github ,对应工程的 exercise 模块下的 cn.codingblock.sort 包,下载地址:https://github.com/lgliuwei/DataStructureStudy,项目工程为 IntelliJ IDEA 环境,童鞋不妨下载下来,参照着代码看博文岂不是效果更好~(额~由于本工程为练习工程,结构比较随意而且还有重复代码,参考时还请多找找:D)
一、冒泡排序
冒泡排序非常简单,也很易于理解,其逻辑思路就像水里面冒气泡一样,越往上气泡越大,也因此而得名冒泡排序,其具体规则如下:
1、从一组的数据的起始位置开始依次向后比较相邻两个数,
2、如果前一个数比后一个数大则交换位置,
3、一直比较到最后一个数。
此时,这组数的最后一个数一定是最大的了,然后进行下一轮如此比较到倒数第二个数,再下一轮比较到倒数第三个,如此循环,直到有一轮比较到第二个数结束,最终这一组数就是有序的了。
上代码让日志演示一下过程,具体代码如下:
/**
* 冒泡排序
* @param nums
*/
public void bubbleSort(int[] nums){
int count = 0;// 日志辅助代码
for (int out = nums.length - 1; out > 1; out--){
for (int in = 0; in < out; in++) {
Logger.print("比较 " + nums[in] + " , " + nums[in + 1]);
if (nums[in] > nums[in + 1]) {
int temp = nums[in+1];
nums[in+1] = nums[in];
nums[in] = temp;
Logger.println(": 交换: ");// 日志辅助代码
display(nums, in, in + 1);// 日志辅助代码
} else {
Logger.println(": 不交换");// 日志辅助代码
}
}
Logger.print(">>第" + (++count) + "趟冒泡:");// 日志辅助代码
display(nums);// 日志辅助代码
}
}
日志如下:
排序前:23, 52, 81, 69, 78, 32, 67, 70, 36, 73,
比较 23 , 52: 不交换
比较 52 , 81: 不交换
比较 81 , 69: 交换:
23, 52, [69], [81], 78, 32, 67, 70, 36, 73,
比较 81 , 78: 交换:
23, 52, 69, [78], [81], 32, 67, 70, 36, 73,
比较 81 , 32: 交换:
23, 52, 69, 78, [32], [81], 67, 70, 36, 73,
比较 81 , 67: 交换:
23, 52, 69, 78, 32, [67], [81], 70, 36, 73,
比较 81 , 70: 交换:
23, 52, 69, 78, 32, 67, [70], [81], 36, 73,
比较 81 , 36: 交换:
23, 52, 69, 78, 32, 67, 70, [36], [81], 73,
比较 81 , 73: 交换:
23, 52, 69, 78, 32, 67, 70, 36, [73], [81],
>>第1趟冒泡:23, 52, 69, 78, 32, 67, 70, 36, 73, 81,
比较 23 , 52: 不交换
比较 52 , 69: 不交换
比较 69 , 78: 不交换
比较 78 , 32: 交换:
23, 52, 69, [32], [78], 67, 70, 36, 73, 81,
比较 78 , 67: 交换:
23, 52, 69, 32, [67], [78], 70, 36, 73, 81,
比较 78 , 70: 交换:
23, 52, 69, 32, 67, [70], [78], 36, 73, 81,
比较 78 , 36: 交换:
23, 52, 69, 32, 67, 70, [36], [78], 73, 81,
比较 78 , 73: 交换:
23, 52, 69, 32, 67, 70, 36, [73], [78], 81,
>>第2趟冒泡:23, 52, 69, 32, 67, 70, 36, 73, 78, 81,
比较 23 , 52: 不交换
比较 52 , 69: 不交换
比较 69 , 32: 交换:
23, 52, [32], [69], 67, 70, 36, 73, 78, 81,
比较 69 , 67: 交换:
23, 52, 32, [67], [69], 70, 36, 73, 78, 81,
比较 69 , 70: 不交换
比较 70 , 36: 交换:
23, 52, 32, 67, 69, [36], [70], 73, 78, 81,
比较 70 , 73: 不交换
>>第3趟冒泡:23, 52, 32, 67, 69, 36, 70, 73, 78, 81,
比较 23 , 52: 不交换
比较 52 , 32: 交换:
23, [32], [52], 67, 69, 36, 70, 73, 78, 81,
比较 52 , 67: 不交换
比较 67 , 69: 不交换
比较 69 , 36: 交换:
23, 32, 52, 67, [36], [69], 70, 73, 78, 81,
比较 69 , 70: 不交换
>>第4趟冒泡:23, 32, 52, 67, 36, 69, 70, 73, 78, 81,
比较 23 , 32: 不交换
比较 32 , 52: 不交换
比较 52 , 67: 不交换
比较 67 , 36: 交换:
23, 32, 52, [36], [67], 69, 70, 73, 78, 81,
比较 67 , 69: 不交换
>>第5趟冒泡:23, 32, 52, 36, 67, 69, 70, 73, 78, 81,
比较 23 , 32: 不交换
比较 32 , 52: 不交换
比较 52 , 36: 交换:
23, 32, [36], [52], 67, 69, 70, 73, 78, 81,
比较 52 , 67: 不交换
>>第6趟冒泡:23, 32, 36, 52, 67, 69, 70, 73, 78, 81,
比较 23 , 32: 不交换
比较 32 , 36: 不交换
比较 36 , 52: 不交换
>>第7趟冒泡:23, 32, 36, 52, 67, 69, 70, 73, 78, 81,
比较 23 , 32: 不交换
比较 32 , 36: 不交换
>>第8趟冒泡:23, 32, 36, 52, 67, 69, 70, 73, 78, 81,
排序后:23, 32, 36, 52, 67, 69, 70, 73, 78, 81,
冒泡排序的时间复杂度:O(n^2)。
通过分析我们可以得出,如果对 N 个数据项进行排序,一般情况下第一趟排序比较了 N-1 次,第二趟排序比较了 N-2 次,依次类推。所以冒泡排序对N个数据项排序时需要比较的次数为:
(N-1)+(N-2)+(N-3)+....+ = N*(N-1)/2 方便起见,近似看做比较了 (N^2)/2 次数。
用大 O 表示法忽略常数,所以其时间复杂度为:O(n^2)。
二、选择排序
选择排序是在冒泡排序的基础上做了一些改进,虽然比较次数仍然是O(n^2),但它将必要的交换次数从O(n^2)将到了O(n)次,其排序规则如下:
1、从数组的0下标开始标记为最小,
2、从1下标开始与标记下标的值比较,如果小于标记下标的值则更新将标记赋值为1下标,依次往后类推直到最后遍历完数组,最后将0下标与最小标记下标的值交换位置。
3、然后再从数组1小标开始比较为最小,类比第二步最终交换1下标与最小标记下标的位置。依次类推。。。
选择排序的一趟排序过程示意图(非排序全程):
选择详细代码如下:
/**
* 选择排序
* @param nums
*/
public void selectionSort(int[] nums) {
int min;
for (int out = 0; out < nums.length - 1; out++) {
min = out;
for (int in = out + 1; in <nums.length; in++) {
if (nums[in] < nums[min]) {
min = in;
}
}
int temp = nums[min];
nums[min] = nums[out];
nums[out] = temp;
}
}
选择排序的时间复杂度为:O(N^2)。
相比冒泡而言,选择排序虽然大大减少了交换次数,但是也比较了和冒泡相同的次数,所以其时间复杂度也为:O(N^2)。
三、插入排序
插入排序是根据有序插入的思想来的,可以对照上篇博文中的有序插入来理解插入排序,其大概规则如下:
1、最数组的1下标开始赋值给临时变量,看成待插入元素,
2、从数组的1下标向前遍历,凡是大于临时变量的元素均后移一位,直到遇到不大于临时变量的值时将临时变量插入到它后一位。
3、然后在从数组的2下标开始赋值给临时变量,并从2下标向前遍历依照第二步遇到不大于临时变量时将临时变量插入到它后一位,依次类推...
插入排序的详细代码如下:
/**
* 插入排序
* @param nums
*/
public void insertionSort1(int[] nums) {
int temp;
int in;
for(int out = 1; out < nums.length; out++) {
temp = nums[out];
in = out;
while (in > 0 && nums[in - 1] > temp) {
nums[in] = nums[in - 1];
in--;
}
nums[in] = temp;
}
}
插入排序的时间复杂度:O(N^2)。
对于随机数据,插入排序速度是冒泡排序的二倍,比选择排序也要快一点,而对于基本有序的数据来说插入排序表现则要出色的多,因为基本有序的数据排序时,while 循环的条件大部分情况下都不成立,这样基本就相当于只执行了外层的一层循环,某些理想情况下插入排序的时间复杂度可以达到 O(N),但通常情况下,插入平均时间复杂度为:O(N^2)。
四、小结
除了上面介绍的三种简单排序之外,还有归并排序、希尔排序、快速排序、基数排序、堆排序等等,后面这几种都是高级排序,稍微复杂一些,而且用到了递归、树等知识,所以这些排序将会在以后的博文中介绍,本篇中的三种排序对比如下:
排序名称 |
时间复杂度 |
分析 |
级别 |
冒泡排序 |
O(N^2) |
速度慢,交换次数多。 |
简单排序 |
选择排序 |
O(N^2) |
速度比冒泡快。 |
简单排序 |
插入排序 |
O(N^2) |
速度是冒泡的二倍,优于选择排序,在基本有序的数据中表现出色。 |
简单排序 |
Java数据结构和算法总结-冒泡排序、选择排序、插入排序算法分析的更多相关文章
- (三)Java数据结构和算法——冒泡、选择、插入排序算法
一.冒泡排序 冒泡算法的运作规律如下: ①.比较相邻的元素.如果第一个比第二个大,就交换他们两个. ②.对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对.这步做完后,最后的元素会是最大的数( ...
- python算法(一)基本知识&冒泡排序&选择排序&插入排序
本节内容: 算法基本知识 冒泡排序 选择排序 插入排序 1. 算法基本知识 1.1 什么是算法? 算法(algorithm):就是定义良好的计算过程,他取一个或一组的值为输入,并产生出一个或一组值作为 ...
- 学习C#之旅 冒泡排序,选择排序,插入排序,希尔排序[资料收集]
关于冒泡排序,选择排序,插入排序,希尔排序[资料收集] 以下资料来源与网络 冒泡排序:从后到前(或者从前到后)相邻的两个两两进行比较,不满足要求就位置进行交换,一轮下来选择出一个最小(或最大)的放到 ...
- 算法 排序lowB三人组 冒泡排序 选择排序 插入排序
参考博客:基于python的七种经典排序算法 [经典排序算法][集锦] 经典排序算法及python实现 首先明确,算法的实质 是 列表排序.具体就是操作的列表,将无序列表变成有序列表! 一 ...
- 数组排序-冒泡排序-选择排序-插入排序-希尔排序-快速排序-Java实现
这五种排序算法难度依次增加. 冒泡排序: 第一次将数组相邻两个元素依次比较,然后将大的元素往后移,像冒泡一样,最终最大的元素被移到数组的最末尾. 第二次将数组的前n-1个元素取出,然后相邻两个元素依次 ...
- 过三关 Java冒泡排序选择排序插入排序小练习
材料:猴子排序,按照身高来从小到大来排序. 第一关: 老猴子带领小猴子队伍按大小逐一比较,交换,开始高矮排列队伍.(冒泡排序) 第二关: 太慢了,给第一关增加难度,进行选择排序 第三关: 最后,尝试选 ...
- java冒泡排序-选择排序-插入排序-使用API中文文档直接调用函数
import java.util.Arrays; public class ArrayDemo2_3 { public static void main(String []args) { //---- ...
- lowB三人组算法-冒泡排序-选择排序-插入排序
冒泡排序 时间复杂度:O(n2) 算法稳定 第一趟,从第一个数开始,相邻两个数比较,大的数交换放后,交换到最后位置得出一个第一大数 第二趟,从第一个数开始,相邻两个数比较,大的数交换放后,交换到倒数 ...
- php基础排序算法 冒泡排序 选择排序 插入排序 归并排序 快速排序
<?php$arr=array(12,25,56,1,75,13,58,99,22);//冒泡排序function sortnum($arr){ $num=count($arr); ...
随机推荐
- 可编辑的EditorGridPanel
1.创建pannel是为可编辑的: new Ext.grid.EditorGridPanel 2.设置单击可以编辑属性: clickstoEdit: 1 3.在列设置添加文本编辑框 {header:& ...
- python 数据驱动(ddt)
DDT包含类的装饰器ddt和两个方法装饰器data(直接输入测试数据),file_data(可以从json或者yaml中获取测试数据) 实例代码: import ddt import unittest ...
- jmeter系列-------脚本编写格式
1.通常会将用户和服务器的一次交互(页面访问或者提交)请求放在一个简单控制器或者事务控制器,例如微课首页里面包含4个接口都放到简单控制器里 或者一个提交可能,会触发3个接口,那么这3个接口放到一个简单 ...
- docker的简单搭建(java/tomcat 环境)
1.一副图简单了解下docker的布局,它是虚拟的,docker分为私服.镜像.容器三个模块 一般从私服pull镜像,镜像run一个容器,我们把容器作为一个虚拟服务,里面可以独立运行进程有独立的内网I ...
- [转]Java中的随机数生成器:Random,ThreadLocalRandom,SecureRandom
详见: http://blog.yemou.net/article/query/info/tytfjhfascvhzxcytp84 Random即:java.util.Random, ThreadL ...
- 小程序脚本语言WXS,你想要的都在这里了
WXS脚本语言是 Weixin Script脚本的简称,是JS.JSON.WXML.WXSS之后又一大小程序内部文件类型.截至到目前小程序已经提供了5种文件类型. 解构小程序的几种方式,其中一种方式就 ...
- ES6块级作用域
块级作用域的优点 避免变量冲突,比如程序中加载了多个第三方库的时候,如果没有妥善地将内部私有函数或变量隐藏起来,就很容易引发变量冲突: 可以方便的进行模块管理: 利于内存回收:(块级作用域里声明的变量 ...
- nhibernate教程(4)--条件查询(Criteria Query)
NHibernate之旅(4):探索查询之条件查询(Criteria Query) 2008-10-16 18:20 by 李永京, 44341 阅读, 43 评论, 收藏, 编辑 本节内容 NHi ...
- ★电车难题的n个坑爹变种
哲学家都不会做的电车难题变异 此题会答清华北大 "电车难题(Trolley Problem)"是伦理学领域最为知名的思想实验之一,其内容大致是: 一个疯子把五个无辜的人绑在电车轨道 ...
- 第六次meeting会议
[Beta] 第六次Daily Scrum Meeting 一.本次会议为第六次meeting会议 二.时间:10:00AM-10:20AM 地点:禹州楼 三.会议站立式照片 四.今日任务安排 成员 ...