什么是排序算法稳定性?
假定在待排序的记录序列中,存在多个具有相同的关键字的记录,若经过排序,这些记录的相对次序保持不变,即在原序列中,r[i]=r[j],且r[i]在r[j]之前,而在排序后的序列中,r[i]仍在r[j]之前,则称这种排序算法是稳定的。
大根堆和小根堆
堆的结构可以分为大根堆和小根堆,是一个完全二叉树,而堆排序是根据堆的这种数据结构设计的一种排序,下面先来看看什么是大根堆和小根堆
性质:每个结点的值都大于其左孩子和右孩子结点的值,称之为大根堆;
每个结点的值都小于其左孩子和右孩子结点的值,称之为小根堆。如下图
我们对上面的图中每个数都进行了标记,上面的结构映射成数组就变成了下面这个样子
堆排序基本步骤
1.首先将待排序的数组构造成一个大根堆,此时,整个数组的最大值就是堆结构的顶端
2.将顶端的数与末尾的数交换,此时,末尾的数为最大值,剩余待排序数组个数为n-1
3.将剩余的n-1个数再构造成大根堆,再将顶端数与n-1位置的数交换,如此反复执行,便能得到有序数组
如下图(小顶堆)所示:
代码如下:
public class MySort {
public static void swap(int[] k, int a, int b) {
int tem = k[a];
k[a] = k[b];
k[b] =tem;
}
//堆调整算法 ,n为有效长度,k为当前节点
public static void heapAdjust(int[] a, int n, int k) {
int k1 = k*2 +1; //左右孩子的索引
int k2 = k1 + 1;
//没有左右孩子,即叶子节点
if(k1 >= n && k2 >= n) return;
//只有左孩子
if(k2 >= n) {
if(a[k1] > a[k]) {
swap(a, k, k1);
}
} else { //左右孩子都有
//如果本身满足大顶堆
if(a[k] > a[k1] && a[k] > a[k2]) {
return;
}
//如果左孩子大,交换并调整k1
if(a[k1] > a[k2]) {
swap(a, k, k1);
heapAdjust(a, n, k1);
} else { //如果右孩子大,交换并调整k2
swap(a, k, k2);
heapAdjust(a, n, k2);
}
}
}
//heap排序(大顶堆 -- 升序排序)
public static void heapSort(int[] a) {
long start = System.currentTimeMillis();
int n = a.length;
//建堆
for(int i = (n-2)/2; i>=0; i--) {
heapAdjust(a, n, i);
}
//排序,交换堆顶和堆尾
while(n > 0) {
//System.out.print(a[0]+ " ");
swap(a, 0, n-1);
n--;
heapAdjust(a, n, 0);
}
System.out.println("\nheapSort time: " + (System.currentTimeMillis()-start));
}
public static void main(String[] args) {
int a[] = {3,4,7,1,10};
int b[] = {3,2,1,4,5,7,6,9,8};
heapSort(b);
for(int i:b) {
System.out.print(i+" ");
}
}
}