学习目标:
掌握堆排序以及优先级队列的原理和实现
学习内容:
1.向上建堆找最大值 2.向下更新堆学习产出:
学习堆排序需要循序渐进。首先我们要明白堆排序的两个重要过程:
- 向上建堆,将最大值放到对顶
- 向下更新堆
我们需要在数组中找到最后一个非叶节点。因为我们自底向上找到最大值,如果直接找叶节点无法通过比较确定大小关系。在完全二叉树中,最后一个非叶节点在数组中的位置与整个树的节点数有一定的关系
index = N / 2;
index为最后一个非叶节点在数组的索引,N为整个树的结点,向下取整。
所以我们从N/2开始遍历,每次都比较以该节点为根节点的树的最大值,并将其不断上浮。
static void buildMaxHeap(int[] A,int len){
for(int i = len/2;i>=1;i--)
{
maxHeap(A,i,len);
}
}
static void maxHeap(int[] A,int index,int len){
int left = index*2;
int right = 2*index+1;
int largest;
if(left <= len && A[left] > A[index]){
largest = left;
}
else{
largest = index;
}
if(right <= len && A[largest] < A[right])
{
largest = right;
}
if(largest != index)
{
swap(index,largest);
maxHeap(A,largest,len);
}
}
先从当前节点,左节点,右节点中找到最大值,将其与当前节点进行交换,进行递归操作。
每当我进行好上述整个操作的时候,我们就已经把最大值给放到了堆顶。此时我们需要将堆顶元素与数组末尾元素进行交换,因为排序的最终结果就是最大值在最末尾不是吗?
private static void heapSort() {
for(int i=H;i>=1;i--) {
buildMaxHeap(A, i);
swap(1,i);
}
}
这里我们需要不断遍历,i表示当前数组未排序的长度,以便于将局部最大值放到对应的位置。
完整代码
package AizuOJ.Heap;
import java.util.Scanner;
public class HeapSort {
static int[] A = new int[1000];
static int H;
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
H = in.nextInt();
for(int i=1;i<=H;i++){
int key = in.nextInt();
A[i] = key;
}
heapSort();
for(int i=1;i<=H;i++){
System.out.println(A[i]);
}
}
static void buildMaxHeap(int[] A,int len){
for(int i = len/2;i>=1;i--)
{
maxHeap(A,i,len);
}
}
static void swap(int x,int y){
int item = A[x];
A[x] = A[y];
A[y] = item;
}
static void maxHeap(int[] A,int index,int len){
int left = index*2;
int right = 2*index+1;
int largest;
if(left <= len && A[left] > A[index]){
largest = left;
}
else{
largest = index;
}
if(right <= len && A[largest] < A[right])
{
largest = right;
}
if(largest != index)
{
swap(index,largest);
maxHeap(A,largest,len);
}
}
private static void heapSort() {
for(int i=H;i>=1;i--) {
buildMaxHeap(A, i);
swap(1,i);
}
}
}
题目链接ALDS1_9_D