堆:
堆是具有下列性质的完全二叉树:每个结点的值都大于或等于其左右孩子结点的值,称为大顶堆(图-1);每个结点的值都小于或等于其左右孩子结点的值,称为小顶堆(图-2)。
我们对大顶堆(图-1)中的结点按层序遍历编号,将这种逻辑结构映射到数组中(图-3)
由上图可知我们将大顶堆按层序遍历,从零编号映射到数组中,可以得到,堆的节点和孩子节点关系公式如下:(完全二叉树的性质决定)
1、设任意非根结点编号为i((i>0||i=0) &&i<(length-1)/2),root[i]的孩子结点分别为 left[2*i+1],right[2*i+2]。(length 为数组长度,length-1 既是堆的最后一个编号节点,也是数组最后一个元素下标。)
2、设任意非根结点编号为r(r>0),则可以得到父亲结点为(r-1)/2。
堆排序思路分析:
思路:将待排序的N个元素的无序序列,构造成一个大顶堆。此时,整个序列的最大值就是堆顶的根结点。将它移走N(将根结点和堆数组最后一个元素交换),然后将剩余的N-1个序列重新构造成一个大顶堆,这样就会得到N个元素中的次大值。如此反复执行,最后便能得到一个有序序列了。
分解成步骤如下:
1、无序序列初始化构造一个大顶堆。
2、堆顶元素和堆尾元素交换。
3、将堆尾元素排除,并将剩余元素构造大顶堆(步骤1,比初始化大顶堆要快,因为此时只有第一个元素可能不复合大顶堆定义),再交换元素(步骤2),直到有序。
代码实现,与测试结果:
public class SortUtil {
/**
* 堆排序,非稳定排序,选择排序
*
* @param data
*/
public static void heapSort(int data[]) {
int length = data.length;
// 构建大顶堆
// (length-2)/2 分解为 r = length-1 , r-1/2 为满二叉树最后一个父节点
for (int i = (length - 2) / 2; i >= 0; i--) {
ajustHeap(data, i, length);
}
// 交换数据,重新调整堆
for (int j = length - 1; j >1; j--) {
swap(data,0,j);
ajustHeap(data, 0, j);
}
//交换最后一对数据值
swap(data,0,1);
}
// 交換数据
public static void swap(int data[],int i,int j){
if(i!=j){
data[i]= data[i]^data[j];
data[j]=data[i]^data[j];
data[i]=data[i]^data[j];
}
}
/**
* @param data 待排序数据
* @param index 父节点位置
* @param length 待排序数组长度
*/
public static void ajustHeap(int data[], int index, int length) {
int temp = data[index];
for (int j = index; j <= (length - 2) / 2;) {
int childLeft = data[2 * j + 1];
int childRight = childLeft;
//右孩子可能不存在,判断一下
if ((2 * j + 2) < length) {
childRight = data[2 * j + 2];
}
// 如果根节点比较大,则跳出循环
if (temp >childLeft && temp > childRight) {
break;
} else {
// 最大的值
if (childRight >childLeft) {
data[index] = childRight;
j = 2 * j + 2;
} else {
data[index] = childLeft;
j = 2 * j + 1;
}
index = j;
}
}
//将值放到指定位置
data[index] = temp;
}
}
public class Test {
public static void main(String[] args) {
// TODO Auto-generated method stub
int num;
Scanner scan = new Scanner(System.in);
for (;;) {
num = scan.nextInt();
if (num == 0) {
break;
}
int data[] = new int[num];
Random random = new Random();
for (int i = 0; i < num; i++) {
data[i] = random.nextInt(num);
}
int data1[] = Arrays.copyOf(data, num);
int data2[] = Arrays.copyOf(data, num);
int data3[] = Arrays.copyOf(data, num);
int data4[] = Arrays.copyOf(data, num);
System.out.println("原始数据:");
printArray(data);
// 测试冒泡
long currentTime = System.currentTimeMillis();
SortUtil.bubbleSort(data);
System.out.println("冒泡排序用时间:"+(System.currentTimeMillis()-currentTime));
printArray(data);
// 测试选择
currentTime = System.currentTimeMillis();
SortUtil.selectSort(data1);
System.out.println("選擇排序用时间:"+(System.currentTimeMillis()-currentTime));
printArray(data1);
// 测试插入
currentTime = System.currentTimeMillis();
SortUtil.insertSort(data2);
System.out.println("插入排序用时间:"+(System.currentTimeMillis()-currentTime));
printArray(data2);
// 测试快排
currentTime = System.currentTimeMillis();
SortUtil.quickSort(data3, 0, data3.length - 1);
System.out.println("快速排序用时间:" + (System.currentTimeMillis() - currentTime));
printArray(data3);
// 测试堆排
currentTime = System.currentTimeMillis();
SortUtil.heapSort(data4);
System.out.println("堆排序用时间:" + (System.currentTimeMillis() - currentTime));
printArray(data4);
}
}
/**
* 打印数组
*
* @param data
*/
public static void printArray(int data[]) {
for (int i = 0; i < data.length; i++) {
System.out.print("\t" + data[i]);
}
System.out.println();
}
}