排序算法作为算法的基础,我想大家都不陌生,甚至是张口就来的程度。什么插入排序,选择排序,冒泡排序,快速排序等等。但是真到了要你把代码写下来的时候,很多人都是懵逼的。
那么,废话不多说。现在就让我通过一系列的文章,带大家一次性的深入理解排序的原理,争取学习一次,终身受用。
冒泡排序和选择排序的共同点
这里为啥要把冒泡排序和选择排序放到一起讲呢?其实他们的共同点都是把牌堆分为无序和有序两部分,一开始都是无序的。然后依次从无序的牌堆里筛选出最大的值放到有序的牌堆里。
至于这个筛选的过程,他们分别采用了不同的方式。冒泡排序是依次把最大值进行位置交换交换到有序牌堆里。而选择排序是进行依次比较比较出最大值放到有序牌堆里。
冒泡排序
每次都是拿取从数组的0到i的位置中的数组。然后将该数组从头开始进行当前数值和下一个数值比较交换,将更大的换到右边。
private static void bubbleSort(int[] arr){
for(int i=arr.length-1;i>0;i--){
for(int j=0;j<i;j++){
if(arr[j]>arr[j+1]){
swap(j,j+1,arr);
}
}
}
}
swap方法:先把arr中i位置的值,暂时存到temp值里。然后将arr中j位置的值赋给arr[i]。最后将临时存放的temp的值赋给arr[j],从而达到了交换的效果。
private static void swap(int i,int j,int[] arr){
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
然后我们在main方法里进行调用
int[] arr = new int[]{5,7,3,1,2,7,6};
bubbleSort(arr);
第一次循环:i=arr.length-1 所以j的取值范围是0-(arr.length-1),也就是在所有牌堆里交换出最大的牌放到最右边。
第一步,由于5<7,不做交换。跳过
第二步,7大于3,7>3进行一次交换。
第三步,7和1进行比较,7>1进行一次交换。
第四步,7和2进行比较,7>2进行一次交换。
第五步,7和7进行比较。7=7不做交换。相等的数值没有进行位置交换,所以冒泡排序是稳定排序。
第六步,7和6进行比较,7>6进行一次交换。
至此第一次循环结束,第1个7作为最大值交换到了最右边。就好像泡泡一样咕嘟咕嘟的冒上来,所以形象的称之为冒泡排序。
第二次循环开始后每次数组的状态,此时紫色部分已经变成了有序的部分。全部完成后变成有序数组。
冒泡排序的特点:
时间复杂度为O(n*n)
在冒泡排序中每一轮会使一个元素排到一端,也就是最终需要 n-1 轮这样的排序(n 为待排序的数列的长度),而在每轮排序中都需要对相邻的两个元素进行比较。
在最坏的情况下,每次比较之后都需要交换位置,所以时间复杂度是O(n*n)。
冒泡排序在最好的情况下,时间复杂度可以达到O(n),这当然是在待排序的数列有序的情况下。
冒泡排序的平均时间复杂度是O(n*n)。
空间复杂度为O(1):冒泡排序用到的额外的存储空间只有一个,那就是用于交换位置的临时变量temp,其他所有操作都是在原有待排序列上处理的,所以空间复杂度为 O(1)。
稳定排序:冒泡排序是稳定的,因为在比较过程中,只有后一个元素比前面的元素大时才会对它们交换位置并向上冒出,对于同样大小的元素,是不需要交换位置的,所以对于同样大小的元素来说,相对位置是不会改变的。
结论:
冒泡排序是一种比较好理解的排序算法。但是它的时间复杂度是比较高的。而且由于在内存中,写的效率是比读的效率低很多的。冒泡排序频繁的进行写操作,所以它的效率是比较低的。在实际使用过程中一般是不会使用到冒泡排序的。
关于不同排序算法的对比,我们放到这一系列的文章的最后进行讨论。
为了帮助大家面试,我整理了一份面试资料。总共几十页PDF,加我公众号 天鹭城,回复2,一次性送给大家。