排序算法稳定性
假定在待排序的记录序列中,存在多个具有相同的关键字的记录,若经过排序,这些记录的相对次序保持不变,即在原序列中,r[i]=r[j],且r[i]在r[j]之前,而在排序后的序列中,r[i]仍在r[j]之前,则称这种排序算法是稳定的;否则称为不稳定的。 ————百度百科
排序原理
每一趟从待排序序列中找出最小的元素,顺序放在已排好序的序列最后,直到全部序列排序完毕。
直接排序原理
假设有个数组array={里面有n个数据},第一趟排序从array[1] ~ array[n]中找出比array[0]小的元素,交换顺序。第一趟排序从array[2] ~ array[n]中找出比array[1]小的元素,交换顺序,以此类推,循环直到array[n-1]结束,因为array[n]此时一定是最大的。
堆排序原理
根据堆的性质,根节点一定是最小(或最大)的元素,通过不断删除最小元(或最大元)来维持一个有序数组即可达到排序要求。由于需要使用一个数组来存储每次删除的最小元(或最大元),因此,存储需求增加了一倍,回避使用第二个数组的方法可以利用堆的结构性。每次deleteMin之后,堆缩小1,因此,位于堆中最后的单元可以用来存放刚刚删去的元素。
若是使用最小堆(即根节点是最小元),则最后的这个数组是以递减的顺序存放元素。如果我们想要这些元素排成更典型的递增顺序,那么可以使用最大堆来实现,下面的堆排序算法就是利用最大堆来实现排序。
直接选择排序
从以上的这个栗子中我们可以更好的理解直接选择排序,并且需要注意的是,在第二趟排序过程中,第一个5的顺序被交换到了第二个5的后面,因此也可以看出直接选择排序是不稳定的排序算法。
排序过程 宏观过程
代码如下:
public void selectSort(T[] a){
int k; //记录目前找到的最小值的下标
T tmp;
for(int i=0;i<a.length;i++){
k = i;
for(int j=k+1;j<a.length;j++){
if(a[j].compareTo(a[k])<0){
k = j;
}
}
//内层循环结束,如果在本次循环找到更小的数,则交换
if(i != k){
tmp = a[i];
a[i] = a[k];
a[k] = tmp;
}
}
}
时间复杂度:O(N²)
空间复杂度:O(1)
堆排序
上图展示的在构建完堆序之后的最大堆以及在进行一次deleteMax操作之后堆。我们知道堆可以用一个数组来实现,并且也看出当删除最大元后我们将该最大元置于数组刚刚元素31的位置上,再经过5次deleteMax操作之后,该堆实际上只有一个元素,而堆数组中留下的元素将是排序后的顺序。
上代码:
/**
* 构建堆
*/
private <T extends Comparable<? super T>> T[] buildHeap(T[] array, int i,int n){
T tmp;
int child;
for(tmp = array[i];leftChild(i) < n;i = child){
child = leftChild(i);
if(child != n-1 && array[child].compareTo(array[child+1])<0){
child++;
}
if(tmp.compareTo(array[child])<0){
array[i] = array[child];
} else {
break;
}
}
array[i] = tmp;
return array;
}
/**
* 删除最大元
*/
public <T extends Comparable<? super T>> T[] deleteMax(T[] array, int i){
T item = array[0];
array[0] = array[i];
array[i] = item;
return buildHeap(array, 0, i);
}
private int leftChild(int i){
return i * 2 + 1;
}
/**
* 堆排序
* 传入数组,从最后一个叶子节点的父节点处开始构建最大堆
* 最大堆序性质:任一节点的父节点大于等于该节点
* 删除最大元后,应该重新保持堆序性质
*/
public <T extends Comparable<? super T>> void heapSort(T[] array){
for(int i=array.length/2 - 1;i>=0;i--){
array = buildHeap(array, i, array.length);
}
for(int i=array.length-1;i>=0;i--){
array = deleteMax(array, i);
}
}
上面的堆排序算法中,我们需要在一开始将数组构建成一个最大堆的形式,方式则是从最后一个叶子节点的父节点开始构建堆,直到根节点构建完毕。之后进行删除最大元操作即可。
时间复杂度:O(NlogN)
空间复杂度:O(1)
总结
直接选择排序和堆排序都是不稳定的排序算法。但是在《数据结构与算法分析 for Java》第三版的P193页中写到,堆排序是一个非常稳定的算法,这点我百度了许多堆排序,都说堆排序是不稳定的算法,这里大家可以自行验证堆排序的稳定性,哈哈哈。
更多了解,还请关注我的个人博客:www.zhyocean.cn