学习几种常见的排序算法
这次我们学习几种常见的排序算法,分别是:
1.冒泡排序
2.选择排序
3.插入排序
4.快速排序。
首先我们要知道他们的排序原理,了解他们的排序过程,进而熟悉其代码写法。【为了更好地理解其原理,网上找了大佬的图片以及一些原理放上去~】【图片以及原理来源:https://www.cnblogs.com/onepixel/p/7674659.html】
接下来利用这些排序算法对一个数组进行升序,即由小到大的排序。
一、冒泡排序
1.原理
- 比较相邻的元素,如果前面的元素比后面的大,则交换这两个元素
- 定位到下一对相邻元素,重复第一条规则
- 重复1,2,经过n-1轮完成排序,每轮进行n-1次比较,总计n(n-1)/2次比较,最多交换n(n-1)/2次
这样,每比较一次相邻元素,都会将较小的放前面,较大的放到后面,于是,就会先将最大的排到后面。
2.分析过程
来看看这张动图
如上图所示,可以很容易看出每次比较都会将大的放到后面,于是整个排序是先将最大的放在最后面,接着次大的放在倒数第二个,以此类推。知道其原理后再编写代码就很方便了。
3.代码
for(int i=0;i<arr.length;i++) {
for(int j=0;j<arr.length-i-1;j++) { //减少遍历次数:5+4+3+2+1次
if(arr[j]>arr[j+1]) {
temp=arr[j+1];
arr[j+1]=arr[j];
arr[j]=temp;
}
}
}
二、选择排序
1.原理
- 将数组中每个元素与第一个元素比较,如果这个元素小于第一个元素,则交换这两个元素
- 循环第 1 条规则,找出最小元素,放于第 1 个位置
- 经过 n-1 轮比较完成排序,每轮进行n-1次比较,总计n(n-1)/2次比较,最多交换n(n-1)/2次
2.分析过程
继续看这张动图
在每一轮排序中,为了找到最小元素(红色位置),需要记录它的下标,每一次比较时,最小元素对应的下标都可能会随之发生变化。最小元素对应的下标设为minindex,经过第一轮比较后,最小元素为arr[minindex],我们需要将最小元素放到第一个元素的位置上,但需要注意第一个位置的元素需要保留,于是可以直接将最小元素与第一个元素互换。第二轮即第二轮的最小元素(第一轮的最小元素已不在内),与第二个元素互换…以此类推
3.代码
for(int i=0;i<arr.length;i++) {
int minindex=i;
for(int j=i+1;j<arr.length;j++) { //减少遍历次数:5+4+3+2+1
if(arr[j]<arr[minindex]) {
minindex=j; //记录最小元素对应的下标
}
}
//交换元素
temp=arr5[i];
arr5[i]=arr5[minindex];
arr5[minindex]=temp;
}
可以看出,选择排序与冒泡排序的原理不同,但是最终的结果也是一样地通过元素的交换来完成的,并且交换的最大次数也是(n-1)n/2次。
三、插入排序
1.原理
将数组分为两部分, 将后部分的第一个元素逐一与前部分每一个元素比较,在合理位置插入
- 从第一个元素开始,该元素可以认为已经被排序
- 取出下一个元素,在已经排序的元素序列中从后向前扫描
- 如果该元素(已排序)大于新元素,将该元素移到下一位置
- 重复步骤3,直到找到已排序的元素小于或者等于新元素的位置
- 将新元素插入到该位置后
- 重复步骤2~5。
2.分析过程
可能很多人会卡在插入排序这个地方上,不懂为什么元素往后面移动就排序好了,我也是花了一段时间才逐渐理解的。
首先第一轮,前部分为第一个元素,我们可以认为这个元素是已经排序好了的,后部分即剩下的元素。首先把后部分的第一个拿出来,于是后部分的第一个元素的位置就有了空,需要将前面的部分元素往后挪一挪,然后看哪个位置的前边小于拿出来的后边也大于拿出来的,那之前拿出来的元素就需要插入这个位置,即这个位置到前后分界线间的元素都需要往后挪一挪。在代码逻辑中,可以考虑在循环中进行元素后移操作,设置元素停止后移的条件。(哎呀我好像说不明白)
3.代码
不说废话了,感觉真的说不明白,看代码看代码。
for(int i=1;i<arr.length;i++) {
int temp1=arr[i];
int j;
for(j=i-1;j>=0;j--) {
if(arr[j]<temp1) //找到正确的插入位置,退出循环,元素不再向后移动
break;
arr[j+1]=arr[j]; //将需要插入位置后的元素向后移动
}
arr[j+1]=temp1;
}
以上是自己对插入算法大概理解了之后写的代码,然后终于知道了为虾米我一直看不懂这个插入算法的代码是怎么写的。对于其中的“if(arr[j]<temp1) break;”代码,可以考虑放到循环里的条件设置中,于是就变成了下面的很多人都看不懂的代码:
for(int i=1;i<arr.length;i++) {
int temp1=arr[i];
int j;
for(j=i-1;j>=0&&arr[j]<temp1;j--) { //条件设置
arr[j+1]=arr[j]; //将需要插入位置后的元素向后移动
}
arr[j+1]=temp1;
}
四、快速排序
JDK提供了排序方法:Arrays.sort(ary),对指定的型数组按数字升序进行排序。该排序算法是一个经过调优的快速排序法,在这里不再赘述。
五、比较
在做比较之前,先介绍一个方法 System.currentTimeMillis(),通过该方法可以得到格林威治时间自 1970 年 1 月 1 日凌晨 00:00:00 到当前系统时间的毫秒数(返回值为 long 类型)。
用以下格式即可得到某段程序运行时间(单位:毫秒)
long start=System.currentTimeMillis();
//**某段程序**
long end=System.currentTimeMillis();
System.out.println("程序花费时间:"+(end-start));
接下来利用该种方法对四种排序算法进行比较
package com.yyxx.day02;
import java.util.Arrays;
public class test01 {
public static void main(String[] args) {
int[] arr = new int[20000];
//生成随机无序数组
for(int i=0;i<arr.length;i++) {
arr[i]=(int) (Math.random()*10000+1);
}
//拷贝额外3个数组副本
int[] arr1=Arrays.copyOf(arr,arr.length);
int[] arr2=Arrays.copyOf(arr,arr.length);
int[] arr3=Arrays.copyOf(arr,arr.length);
//排序1:快速排序 直接使用Arrays.sort()
long start=System.currentTimeMillis();
Arrays.sort(arr);
long end=System.currentTimeMillis();
System.out.println("快速排序花费时间:"+(end-start));
//排序2:冒泡/选择/插入
//冒泡排序:比较相邻的两个元素,小的放前面 (先将最大的排后面)
start=System.currentTimeMillis();
for(int i=0;i<arr1.length;i++) {
for(int j=0;j<arr1.length-i-1;j++) { //减少遍历次数:5+4+3+2+1
if(arr1[j]>arr1[j+1]) {
int temp=arr1[j+1];
arr1[j+1]=arr1[j];
arr1[j]=temp;
}
}
}
end=System.currentTimeMillis();
System.out.println("冒泡排序花费时间:"+(end-start));
//选择排序:每一次找到最小的元素,放在最前面
start=System.currentTimeMillis();
for(int i=0;i<arr2.length;i++) {
int minindex=i;
for(int j=i+1;j<arr2.length;j++) { //减少遍历次数:5+4+3+2+1
if(arr2[j]<arr2[minindex]) {
minindex=j;
}
}
int temp=arr2[i];
arr2[i]=arr2[minindex];
arr2[minindex]=temp;
}
end=System.currentTimeMillis();
System.out.println("选择排序花费时间:"+(end-start));
//插入排序:两部分,排好序的/没排好的,后面部分的第一个元素逐一与前面部分的每一个元素进行比较
start=System.currentTimeMillis();
for(int i=1;i<arr3.length;i++) {
int temp1=arr3[i];
int j;
for(j=i-1;j>=0;j--) {
if(arr3[j]<temp1)
break;
arr3[j+1]=arr3[j];
}
arr3[j+1]=temp1;
}
end=System.currentTimeMillis();
System.out.println("插入排序花费时间:"+(end-start));
}
}
显示结果如下所示:(单位:ms)
快速排序花费时间:5
冒泡排序花费时间:633
选择排序花费时间:125
插入排序花费时间:150
显然,当数据量较大时,快速排序所花的时间远远小于其他三种排序方法。
冒泡排序花的时间好久好久好久噢~~