· 堆数据结构的效率使它引出一种出奇简单,却很有效率的排序算法,称为堆排序。
· 堆排序的基本思想是使用普通的 insert()例程在堆中插入全部无序的数据项,然后重复用remove()的例程,就可以按序移除所有的数据项。
ForExample:
for (j=0; j<size; j++)
theHeap.insert(anArray[j]); // 将没有排序的数组插入堆
for (j=0; j<size; j++)
anArray[j] = theHeap.remove(); // 利用堆结构的特性,再将其挨个拿出,得到的就是已经排好序的数组
因为inset() 和 remove() 方法操作的时间复杂度都是O(logN),并且每个方法都要执行N次。所以整个排序操作需要O(N*logN)。这和快速排序在算法复杂度上是一样的,但是它又没有快排速度快因为 trickleDown 中的 while 循环中的操作比快排里循环中的操作多。
但是我可以做一些事情,加快它的速度:1. 节省时间 2. 节省内存
1.节省时间
向下筛选到适当的位置上:
如果在堆中插入N个新的数据项,则需要应用 trickleUp() 方法N次,但是可能使用所有的数据项在数组中都是任意排列的,然后再重新排列成堆,这样就只应用了N/2次的trickleDown()方法。这个可以使速度稍稍提升。
由两个正确的子堆形成的一个正确的堆:
如果有一个不遵守堆有序条件的项占据了根的位置,而它们两个子堆却都是正确的堆,用 trickleDown() 方法也能够创建一个正确的堆。
这就提出了一个把无序数组变成堆的方法。对(潜在的)堆底层的节点应用 trickleDown() 方法,也就是说,从数组末端的节点开始。然后上行直到根的各个节点都应用此方法。在每一步应用方法时,该节点下面的子堆都是正确的堆,因为已经对他们用过 trickleDown 方法了。在对根应用了 trickleDown 之后,无序数组将转化为堆。
注意在底层的终端节点,就是那些没有子节点的节点,都已经时正确的堆了,因为他们是单节点的树,没有违背堆的条件。因此,不用堆这些节点应用 trickleDown。可以从节点N/2-1,即最右一个有子节点的节点开始,而不是从节点N-1,最后一个节点开始应用 trickleDown 。这样筛选操作就只需要执行 insert 方法的 N 次的一半就够了。
2.节省内存
使用同一个数组:
原始代码片段显示了数组中的无序数据,然后把数据插入堆中,最后从堆中移除它并把它有序的写回到数组中,这个过程需要两个大小为N的数组:初始数组和用于堆的数组。
事实上,堆和初始数组可以使用同一个数组。这样堆排序所需的存储空间减少了一半,除了初始数组之外不需要额外的存储空间。
每次从堆顶移除一个数据项,堆数组的末端单元就会变为空,堆减少一个节点。可以把最近移除的节点放到这个新空出的单元中。移除的节点越来越多,堆数组就越来越少,但是有序数组却越来越大。因此,只要稍微计划一下,有序数组和堆数组就可以共同使用一块存储空间。
上代码:
/**
* 使用二叉树实现的堆 类
*/
public class Heap{
private Node[] heapArray;
private int maxSize;
private int currentSize;
public Heap(int max) {
maxSize = max;
currentSize = 0;
heapArray = new Node[maxSize];
}
public boolean isEmpty() {
return currentSize == 0;
}
public void insertAt(int index, Node newNode) {
heapArray[index] = newNode;
}
public void incrementSize() {
currentSize++;
}
public Node remove() {
Node root = heapArray[0];
heapArray[0] = heapArray[--currentSize];
trickleDown(0); // 向下调整
return root;
}
public void trickleDown(int index) {
int largerChild;
Node top = heapArray[index];
while (index < currentSize/2) {
int leftChild = 2 * index + 1;
int rightChild = leftChild + 1;
if (rightChild < currentSize && heapArray[leftChild].getKey() < heapArray[rightChild].getKey())
largerChild = rightChild;
else
largerChild = leftChild;
if (top.getKey() >= heapArray[largerChild].getKey()) break;
heapArray[index] = heapArray[largerChild];
index = largerChild;
}
heapArray[index] = top;
}
public void displayHeap() { // 树状方式显示
int nBlanks = 32; // 处理空格的输入
int itemsPerRow = 1;
int column = 0;
int j = 0;
String dots = ".............................";
System.out.println(dots + dots);
while (currentSize > 0) {
if (column == 0)
for (int k=0; k<nBlanks; k++)
System.out.print(" ");
System.out.print(heapArray[j].getKey());
if (++j == currentSize) break; //显示完了
if (++column == itemsPerRow) {
nBlanks /= 2;
itemsPerRow *= 2;
column = 0;
System.out.println();
} else
for (int k=0; k<nBlanks*2-2; k++) System.out.print(' ');
}
System.out.println("\n" + dots + dots);
}
public void displayArray() {
for (int j=0; j<maxSize; j++) {
System.out.print(heapArray[j].getKey() + " ");
}
System.out.println();
}
}
/**
* 堆排序 类
*/
import java.util.*;
public class HeapSort {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int size, j;
System.out.println("Enter number of items:");
size = sc.nextInt();
Heap theHeap = new Heap(size);
for (j=0; j<size; j++) {
int random = (int)(java.lang.Math.random() * 100); // 创建随机数 作为堆排序的数据项
Node newNode = new Node(random);
theHeap.insertAt(j, newNode); // 把随机生成的数据创建成节点后 放入堆中
theHeap.incrementSize(); // 数量递增
}
// 将随机生成无序的数组显示出来看一下
System.out.print("Random:");
theHeap.displayArray();
// 从最后一个元素的父节点开始向下调整,一直倒根
// 调整完之后就变成标准的堆
for (j=size/2-1; j>=0; j--)
theHeap.trickleDown(j);
System.out.print("Heap:");
theHeap.displayArray(); // 按数组顺序打印
theHeap.displayHeap(); // 按树状顺序打印
// 通过循环删除最大项 再把删除的数据放到数组中指定的位置
// 产生的结果就是一个从小到大的有序数组
for (j=size-1; j>=0; j--) {
Node biggestNode = theHeap.remove(); // 取出最大的数据项
theHeap.insertAt(j, biggestNode);
}
System.out.print("Sorted:");
theHeap.displayArray();
}
}
执行结束之后是这样:
Enter number of items:
50
Random:29 12 91 89 69 27 13 68 37 80 32 29 20 90 72 7 4 19 44 4 13 79 61 60 15 73 48 14 16 0 98 65 39 96 55 48 19 64 99 8 11 21 39 6 75 47 60 25 40 47
Heap:99 96 98 89 80 73 91 68 64 69 79 60 48 90 72 65 55 48 44 11 39 75 61 40 47 20 27 14 16 0 13 7 39 4 12 19 19 37 29 8 4 21 13 6 32 47 60 25 29 15
..........................................................
99
96 98
89 80 73 91
68 64 69 79 60 48 90 72
65 55 48 44 11 39 75 61 40 47 20 27 14 16 0 13
739412191937298421136324760252915
..........................................................
Sorted:0 4 4 6 7 8 11 12 13 13 14 15 16 19 19 20 21 25 27 29 29 32 37 39 39 40 44 47 47 48 48 55 60 60 61 64 65 68 69 72 73 75 79 80 89 90 91 96 98 99
Process finished with exit code 0