时间复杂度 | 最好时间复杂度 | 最差时间复杂度 | 空间复杂度 | 是否是稳定排序 | |
快速排序 | O(nlogn) | O(nlogn) | O(n*n)平方 | O(1)原地 | 否(涉及到左右数据交换) |
堆排序 | O(nlogn) | O(nlogn) | O(nlogn) | O(1)原地 | 否(涉及数据层与层的交换) |
1. 10w 数据量两种排序速度基本相当,但是堆排序交换次数明显多于快速排序;10w+数据,随着数据量的增加快速排序效率要高的多,数据交换次数快速排序相比堆排序少的多。
2. 实际应用中,堆排序的时间复杂度要比快速排序稳定,快速排序的最差的时间复杂度是O(n*n),平均时间复杂度是O(nlogn)。堆排序的时间复杂度稳定在O(nlogn)。但是从综合性能来看,快速排序性能更好。
3.堆排序数据访问的方式没有快速排序友好。对于快速排序来说,数据是跳着访问的。比如:堆排序中,最重要的一个操作就是数据的堆化。比如下面的例子,对堆顶节点进行堆化,会依次访问数组下标是1.2.4.8的元素,而不是像快速排序那样,局部顺讯访问,所以,这样对cpu缓存是不友好的。
4. 对于同样的数据,在排序过程中,堆排序算法的数据交换次数要多于快速排序。排序有 有序度和逆序度两个概念。对于基于比较的排序算法来说,整个排序过程就是由两个基本的操作组成的,比较和交换(移动)。快速排序数据交换的次数不会比逆序度多。但是地排序的第一步是建堆,建堆过程会打乱数据原有的相对先后顺序,导致数据的有序度降低。比如,对于一组已经有序的数据来说,经过建堆之后,数据反而变得更无序了。
快排代码
public void quickSort_W(int[] a) { if(a ==null || a.length<1) { return; } int n = a.length; quicksort_c(a, 0, n-1); // parttion(a,0,n-1); } private void quicksort_c(int[] a,int l,int r){ if(l>=r) { return; } int m = parttion(a,l,r); quicksort_c(a, l, m-1); quicksort_c(a, m+1, r); } /** * 快排分区函数 * @param a * @return */ private int parttion(int[] a,int l,int r){ int point = a[l]; int i = l; int j = r; while ( i != j){ while (a[j]>=point && i<j){ j--; } while (a[i]<=point && i<j){ i++; } if(i<j) { int temp = a[i]; a[i] = a[j]; a[j] =temp; changeTime ++; } } a[l] = a[i]; a[i] = point; // parttion(a,l,i-1); // parttion(a,i+1,r); // System.out.println("i:"+i+"---"+point); return i; }
堆排序代码(包括:建堆和堆化)此处用的大顶堆
// n 表示数据的个数,数组 a 中的数据从下标 1 到 n 的位置。 public void sort(int[] a, int n) { changeTime = 0; buildHeap(a, n); int k = n; while (k > 1) { swap(a, 1, k); --k; heapify(a, k, 1); } } //建堆 private void buildHeap(int[] a, int n) { for (int i = n/2; i >= 1; --i) { heapify(a, n, i); } } private void heapify(int[] a, int n, int i) { while (true) { int maxPos = i; if (i*2 <= n && a[i] < a[i*2]) maxPos = i*2; if (i*2+1 <= n && a[maxPos] < a[i*2+1]) maxPos = i*2+1; if (maxPos == i) break; swap(a, i, maxPos); i = maxPos; } } //交换数据 private void swap(int[] a,int i,int j){ int temp = a[i]; a[i] = a[j]; a[j] = temp; changeTime ++; }