优化堆排序
上一节的堆排序,我们开辟了额外的空间进行构造堆和对堆进行排序。这一小节,我们进行优化,使用原地堆排序。
对于一个最大堆,首先将开始位置数据和数组末尾数值进行交换,那么数组末尾就是最大元素,然后再对W元素进行 shift down 操作,重新生成最大堆,然后将新生成的最大数和整个数组倒数第二位置进行交换,此时倒数第二位置就是倒数第二大数据,这个过程以此类推。
整个过程可以用如下图表示:
Java 实例代码
源码包下载: Download
src/yssmx/heap/HeapSort.java 文件代码:
package
yssmx.heap
;
import yssmx.sort.SortTestHelper ;
/**
* 原地堆排序
*/
public class HeapSort < T extends Comparable > {
public static void sort ( Comparable [ ] arr ) {
int n = arr. length ;
// 注意,此时我们的堆是从0开始索引的
// 从(最后一个元素的索引-1)/2开始
// 最后一个元素的索引 = n-1
for ( int i = ( n - 1 - 1 ) / 2 ; i >= ; i -- )
shiftDown ( arr, n, i ) ;
for ( int i = n - 1 ; i > ; i -- ) {
swap ( arr, , i ) ;
shiftDown ( arr, i, ) ;
}
}
// 交换堆中索引为i和j的两个元素
private static void swap ( Object [ ] arr, int i, int j ) {
Object t = arr [ i ] ;
arr [ i ] = arr [ j ] ;
arr [ j ] = t ;
}
// 原始的shiftDown过程
private static void shiftDown ( Comparable [ ] arr, int n, int k ) {
while ( 2 * k + 1 < n ) {
//左孩子节点
int j = 2 * k + 1 ;
//右孩子节点比左孩子节点大
if ( j + 1 < n && arr [ j + 1 ] . compareTo ( arr [ j ] ) > )
j += 1 ;
//比两孩子节点都大
if ( arr [ k ] . compareTo ( arr [ j ] ) >= ) break ;
//交换原节点和孩子节点的值
swap ( arr, k, j ) ;
k = j ;
}
}
// 测试 HeapSort
public static void main ( String [ ] args ) {
int N = 100 ;
Integer [ ] arr = SortTestHelper. generateRandomArray ( N, , 100000 ) ;
sort ( arr ) ;
// 将heapify中的数据逐渐使用extractMax取出来
// 取出来的顺序应该是按照从大到小的顺序取出来的
for ( int i = ; i < N ; i ++ ) {
System . out . print ( arr [ i ] + " " ) ;
}
// 确保arr数组是从大到小排列的
for ( int i = 1 ; i < N ; i ++ )
assert arr [ i - 1 ] >= arr [ i ] ;
}
}
import yssmx.sort.SortTestHelper ;
/**
* 原地堆排序
*/
public class HeapSort < T extends Comparable > {
public static void sort ( Comparable [ ] arr ) {
int n = arr. length ;
// 注意,此时我们的堆是从0开始索引的
// 从(最后一个元素的索引-1)/2开始
// 最后一个元素的索引 = n-1
for ( int i = ( n - 1 - 1 ) / 2 ; i >= ; i -- )
shiftDown ( arr, n, i ) ;
for ( int i = n - 1 ; i > ; i -- ) {
swap ( arr, , i ) ;
shiftDown ( arr, i, ) ;
}
}
// 交换堆中索引为i和j的两个元素
private static void swap ( Object [ ] arr, int i, int j ) {
Object t = arr [ i ] ;
arr [ i ] = arr [ j ] ;
arr [ j ] = t ;
}
// 原始的shiftDown过程
private static void shiftDown ( Comparable [ ] arr, int n, int k ) {
while ( 2 * k + 1 < n ) {
//左孩子节点
int j = 2 * k + 1 ;
//右孩子节点比左孩子节点大
if ( j + 1 < n && arr [ j + 1 ] . compareTo ( arr [ j ] ) > )
j += 1 ;
//比两孩子节点都大
if ( arr [ k ] . compareTo ( arr [ j ] ) >= ) break ;
//交换原节点和孩子节点的值
swap ( arr, k, j ) ;
k = j ;
}
}
// 测试 HeapSort
public static void main ( String [ ] args ) {
int N = 100 ;
Integer [ ] arr = SortTestHelper. generateRandomArray ( N, , 100000 ) ;
sort ( arr ) ;
// 将heapify中的数据逐渐使用extractMax取出来
// 取出来的顺序应该是按照从大到小的顺序取出来的
for ( int i = ; i < N ; i ++ ) {
System . out . print ( arr [ i ] + " " ) ;
}
// 确保arr数组是从大到小排列的
for ( int i = 1 ; i < N ; i ++ )
assert arr [ i - 1 ] >= arr [ i ] ;
}
}