面试的时候总爱问排序,其实排序并不难,只是将一个排序的思路转成编程语言实现。对于实际工作中,我们遇到的业务逻辑,要比排序的逻辑复杂的多,却奇怪于有的公司总爱问这个,更奇怪的是,总有朋友答不上来。(说来惭愧,答不上来的人里也包括我自己,不是不会写,只是不知道什么是快速排序)。这里整理了下面两个排序。
先来说说冒泡排序,冒泡排序就是将当前位置的数依次和后面位置的数比较,将大数放在前面,小数放在后面。
如:int[] arr = {3,2,6,4,1,5,8,9,7};排序完以后的结果:int[] arr = {9,8,7,6,5,4,3,2,1};
3和2比较,3在前面,不需要交换;3和6比较,6放前面;6和4比较,6放前面;。。。如此继续比较下去。
下面看看程序怎么写:
package mytest;
public class TestSortUtil {
public static int[] sort(int[] arr) {
for(int i=0;i<arr.length;i++) {
for(int j=i+1;j<arr.length;j++) { //i=0,j=1,2,3,4,...j初始值是j=i+1=0+1。后面j++,也就是第一个元素和后面的元素依次比较。
if(arr[i]<arr[j]) { //当arr[i]后面的元素小于后面的元素交换。
int tmp = arr[i];
arr[i]=arr[j];
arr[j]=tmp;
}
//print(arr);//如果想看排序过程打开这里的输出就可以
}
}
return arr;
}
public static void print(int[] arr) {
for(int i=0;i<arr.length;i++) {
System.out.print(" "+arr[i]);
}
System.out.println();
}
public static void main(String args[]) {
int[] arr = {3,2,6,4,1,5,8,9,7};
print(arr);
print(sort(arr));
}
}
冒泡排序就是这样。
我们再来说说快速排序;先看百科定义:快速排序(Quicksort)是对冒泡排序的一种改进;它的基本思想是:通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。
首先,要选一个分割点key(百科叫关键数据),通常是数组的第一个数据。(分割点最好是随机从数组里取出来一个数据)
然后,就是第一遍排序,分割点值小的放前面,分割点大的数据放后面。一个从前向后比较arr[i]<key,取到第一个下标i;一个从后向前比较arr[j]>key,取到第一下标j;将第一个大于key的值和第一个小于key的值互换。i==j时比较到中间位置结束;这样就分成了两部分数据。举例:int[] arr = {3,2,6,4,1,5,8,9,7}; 如果key是3,从前向后找到的第一个值是6,从后向前找到的第一个值是1;则6和1交换。
然后,前后两部分分别再进行递归排序,这样就可以将程序写成一个递归调用。
下面再来看看程序
package mytest;
public class TestFastSort {
public static int[] fastsort(int[] arr,int begin,int end) {
int left = begin;//排序开始的时候:begin=0,end=N-1;
int right = end;
int key = arr[begin];//以第一个数组元素作为关键数据,赋值给key,即key=arr[begin];
while(left != right) {
while(arr[left] < key) {//从前向后搜索
left++;
}
while(arr[right] > key) {//从后向前搜索
right--;
}
//此时arr[left]是第一个大于key的值;arr[right]是第一个小于key的值
int temp = arr[left];
arr[left] = arr[right];
arr[right]=temp;
//完成这两个值交换
print(arr);
}//当left和right相同时,循环结束。
if(begin<left) {
fastsort(arr,begin,left-1);
}
if(right<end) {
fastsort(arr,right+1,end);
}
return arr;
}
public static void print(int[] arr) {
for(int i=0;i<arr.length;i++) {
System.out.print(" "+arr[i]);
}
System.out.println();
}
public static void main(String args[]) {
int[] arr = {3,2,6,4,1,5,8,9,0,7};
print(arr);
print(fastsort(arr,0,arr.length-1));
}
}
如此,是不是明白了,为什么说快速排序就是冒泡排序的优化了吧?其实就是先进行第一遍排序,选出中间值分成两部分,然后对两部分再分别排序。
那多的这一步,为什么能比冒泡排序快呢?答降低了复杂度,那么如何降低了复杂度?
先来看看冒泡顺序:
1. 时间复杂度:O(n^2)
冒泡排序耗时的操作有:比较 + 交换(每次交换两次赋值)。时间复杂度如下:
1) 最好情况:序列是升序排列,在这种情况下,需要进行的比较操作为(n-1)次。交换操作为0次。即O(n)
2) 最坏情况:序列是降序排列,那么此时需要进行的比较共有n(n-1)/2次。交换操作数和比较操作数一样。即O(n^2)
3) 渐进时间复杂度(平均时间复杂度):O(n^2)
2. 空间复杂度:O(1)
从实现原理可知,冒泡排序是在原输入数组上进行比较交换的(称“就地排序”),所需开辟的辅助空间跟输入数组规模无关,所以空间复杂度为:O(1)1.
再来看看快速排序
1. 时间复杂度:O(nlog2n)
快速排序耗时的操作有:比较 + 交换(每次交换两次赋值)。时间复杂度如下:
1) 最好情况:选择的基准值刚好是中间值,分区后两分区包含元素个数接近相等。因为,总共经过x次分区,根据2^x<=n得出x=log2n,每次分区需用n-1个元素与基准比较。所以O(nlog2n)
2) 最坏情况:每次分区后,只有一个分区包含除基准元素之外的元素。这样就和冒泡排序一样慢,需n(n-1)/2次比较。即O(n^2)
3) 渐进时间复杂度(平均时间复杂度):O(nlog2n)
2. 空间复杂度:O(1)
从实现原理可知,快速排序是在原输入数组上进行比较分区的(称“就地排序”),所需开辟的辅助空间跟输入数组规模无关,所以空间复杂度为:O(1)