一、什么是排序算法?
排序,顾名思义,就是按照一定的规则排列事物,使之彼此间有序
而排序算法所要做的工作,就是将数据按照人为制定的比较规则排列好,使数据处于彼此间有序的状态。
二、为什么要进行排序?
那为什么要将数据排序呢?计算机处理速度这么快,会不会有点多此一举。现在考虑手上有一本目录乱序的词典,假设有1w个单词,如果想要查apple这个单词,每次都要从头开始找,一个个的确定是不是apple,忽略心力交瘁和砸字典的冲动,那么假设每次查找都需要12个小时。好,现在手上有一本有序的牛津词典,就是当今经常看到的这种,每次我们查apple这个词时,就可以根据字母的顺序,不到一分钟就可以找出apple的释义了。这样一看,有序与无序就相差了12个小时,而且是每次查找都节省12个小时,则让每个人都可以节省更多的时间去做其他的事情。类比到计算机也是一样的道理,节省下来的时间资源是巨大的,这种规模效益无疑值得我们去排序。
有时候,告诉一个人怎样去做一件事不如告诉他为什么要这么做。例如,告诉一个新手程序员怎样去优化一段代码,他有可能会拖延,但是告诉他说,一旦完成优化,每个用户浏览所花费的时间都会节省5秒。这样出来的效果是不一样的。——观点来源于网络
因此,觉得网络上很多文章一上来就直接说排序算法的思想以及如何实现是不够的。先要弄清楚为什么要排序,再去了解排序算法的细枝末节,毕竟所有排序算法,都是为了一个目的服务的——节约时间。
三、经典内部排序算法思想、可视化、Java代码实现、改进方法、时间复杂度、空间复杂度、稳定性
结论先行,接下来会有大量篇幅去讲述排序算法的细节。为了方便,数组元素都使用整数。
博客参考:
八大排序算法
十种排序算法总结
视觉直观感受7种常用的排序算法
常用排序算法稳定性、时间复杂度分析(转,有改动)
常用排序算法时间复杂度分析
十大经典排序算法|JavaScript描述
算法可视化网站:
VisuAlgo
书籍:Data Structures and Algorithm Analysis in Java (Thrid Edition)
1.冒泡排序BubbleSort
介绍:
冒泡排序是一种较为容易理解的排序算法,因为它就是相邻数两两比较,符合条件就交换位置而已,如果从小到大排的话,就像水中升起的泡泡一样越来越大
算法步骤:基于交换
假设数组a[n]有N个整数
第一趟,第一个数与第二个数比较,符合条件就交换位置,然后第二个数和第三个数比较,符合条件就交换位置,以此类推。如此,最后一个数字为最大数;
第二趟,除去第一趟最后一个数字,第一个数与第二个数比较,符合条件就交换位置,如此类推,此时最后一个数字为本趟最大数
第三趟,如上类推
……
第N-1趟,如上类推,所有交换完成后数组元素有序
可视化:
Java代码实现:
public class BubbleSort {
public static void sort(int[] a){
int temp;//定义用于交换的临时变量
int length = a.length;//定义递减长度变量
//外循环,冒泡排序进行的趟数,取a.length-1是因为最后一趟只有一个数字,没有必要排序
for(int i=0;i<a.length-1;i++){
//内循环,实际进行两两比较
for(int j=0;j<length-1;j++){
if(a[j]>a[j+1]){
temp = a[j+1];
a[j+1] = a[j];
a[j] = temp;
}
}
length--;//每趟结束后,最后一个数字有序且为该趟最大,下一趟排序不必进行比较
//用于在控制台输出每一趟的结果
System.out.println();
System.out.print("第"+(i+1)+"趟排序");
for(int k = 0;k<a.length;k++){
System.out.print(a[k]+" ");
}
}
}
public static void main(String[] args) {
int[] a = {
99,89,76,66,54,47,32,20,18,5};
System.out.print("原数组:");
for(int i = 0;i<a.length;i++){
System.out.print(a[i]+" ");
}
sort(a);
System.out.println();
System.out.print("冒泡排序结束后:");
for(int i = 0;i<a.length;i++){
System.out.print(a[i]+" ");
}
}
}
运行结果:
改进方法:
(1)设置标志位,每一趟循环开始默认有序,当发生交换时,则数组无序,仍需继续循环
参考博客白话经典算法系列之一 冒泡排序的三种实现
public static void improveBubbleSort(int[] a){
int temp;//定义用于交换的临时变量
int length = a.length;//length存储数组长度,可以避免每次循环中调用a.length方法的消耗
boolean flag = true;//设置标志位为ture,假设初始状态无序
//若无序,则进行循环
while(flag){
flag = false;//假设元素正序
for(int i = 0;i<length-1;i++){
if(a[i]>a[i+1]){
temp = a[i+1];
a[i+1] = a[i];
a[i] = temp;
//若发生交换操作,则为无序状态,仍需进行下一次循环
//若不发生交换操作,则此步不会执行,则为正序,只需执行一趟循环
flag=true;
}
}
}
}
(2)轮流从左到右以及从右到左进行冒泡排序
参考博客:排序算法系列:冒泡排序与与双向冒泡排序
(3)同样是设置标志位,不过这个标志位用于记录最后一次发生交换的位置,则下一次循环只需要执行到该位置即可,因为后面的元素已经有序。
时间复杂度:与比较次数、逆序数有关,一次交换减少一个逆序
最好情况为O( N )。按照最开始的思路,假设元素一开始全部有序,但即使不需要交换,都需要不断循环比较,N个元素两两比较共需要
最坏情况为O(
平均情况为O( N2 )。书上定理:N个互异数的数组的平均逆序数是 N(N−