堆排序
1.什么是堆
(二叉)堆是一个不完全二叉树,即除了最底层之外,该书是完全满的。那么怎样把堆和一个待排序的数组联系起来呢?这就涉及到二叉堆(二叉树)的一些性质:
- 性质1:设父亲节点的编号为i,则左孩子的编号为2i,右孩子的编号为2i,即LeftChild[i] = 2i,RightChild[i] = 2i+1;
- 性质2:二叉堆分为两种,最大堆和最小堆,最大堆的性质为除了根节点以外的所有节点i满足条件
A[Parent(i)] <= A[i],其中Parent[i] = i/2向下取整
。
也就是说,某个节点的值至多与其父亲节点的值一样大。最小堆的性质类似,不再赘述。
所以,可以将最大(最小)堆看成是一定程度按大小排列的数组,见下图。那我们应该如何将一个待排序的数组转化为最大堆呢?我们要解决的第一个问题怎样使得一个堆满足最大堆得性质。
2.维护最大堆的性质
如下图所示,假设现在有一个堆,2号节点不满足最大堆的性质,我们的操作时与其最大的孩子4号节点交换位置,但是某些情况比如交换后导致原来最大孩子处4号节点的值14由于交换后变成一个较小的值4,使得现在的4号节点也不满足最大堆性质,我们只要将刚才的步骤在应用到现在4号节点,即交换4号节点与9号节点的位置。可以看出,这里存在着分治策略的思想。这一步骤算法复杂度为O(lgn)。
3.最大堆构建
对于一个输入数组A,若将其映射为堆的话的,根据1中的性质,令i = A.length向下取整,那么A[i]就是最后一个有孩子的节点,从这个节点出发遍历到A[1],对这些节点进行维护最大堆性质的操作,即可得到最大堆,如下图所示。一定要注意遍历的顺序是A[i] down to A[1],而不是A[1] up to A[i],原因是如果采用后面的顺序,不能满足每次循环过程中循环不变量的正确性,具体解释参见《算法导论》。这一步骤复杂度分析有点复杂,为O(n)。
4.堆排序
根据前面的分析我们已经将一个数组处理成最大堆,但如果我们将其按编号输出得到的并不是按大小完全排好的序列。根据最大堆的性质,编号为A[1]的节点始终是最大的。如下图所示,我们可以将A[1]和A[n]交换位置,然后对新的A[1]进行最大堆维护,然后去掉A[n],将新的A[n]与新的A[1]再次交换,进行最大堆维护,去掉A[n]......直到堆中元素的数目只剩一个,则必然是最小的元素。所以从A[n]遍历到A[2]即可。
因此,根据以上分析,堆排序的算法复杂度为O(nlgn)。
5.代码实现(C,Java,Python)
需要注意的是,在上面的分析中,为了易于理解,数组的编号从1开始,但是在实际计算中数组的编号从0开始,所以此时LeftChild[i] = 2i,RightChild[i] = 2i+1。
C
#include <stdio.h>
#include <stdlib.h>
int length;
void swap(int* a, int* b) {
int tmp;
tmp = *a;
*a = *b;
*b = tmp;
}
void max_heapify(int* array, int i, int len) {
int l = 2 * i + 1, r = 2 * i + 2, largest;
if(l <= len && array[l] > array[i])
largest = l;
else
largest = i;
if(r <= len && array[r] > array[largest])
largest = r;
if(largest != i) {
swap(&array[largest], &array[i]);
max_heapify(array, largest, len);
}
}
void build_max_heap(int* array) {
int i;
for(i = (length - 1) / 2; i >= 0; i--)
max_heapify(array, i, length - 1);
}
void heapsort(int* array) {
int i, local_length = length - 1;
build_max_heap(array);
for(i = length - 1; i > 0; i--) {
swap(&array[0], &array[i]);
local_length -= 1;
max_heapify(array, 0, local_length);
}
}
int main() {
int *array, i;
printf("Enter the length of array: ");
scanf("%d", &length);
array = (int *)malloc(length * sizeof(int));
for(i = 0; i < length; i++)
scanf("%d", &array[i]);
heapsort(array);
for(i = 0; i < length; i++)
printf("%d ", array[i]);
printf("\n");
free(array);
return 0;
}
Java
import java.util.*;
public class HeapSort {
public static void display(Iterator<Integer> it) {
while(it.hasNext()) {
Integer element = it.next();
System.out.print(element + " ");
}
}
public static void main(String[] args) {
ArrayList<Integer> array = new ArrayList<Integer>();
Scanner in = new Scanner(System.in);
System.out.print("Enter the length of array: ");
int length = in.nextInt();
for(int i = 0; i < length; i++)
array.add(in.nextInt());
in.close();
Sort sort = new Sort(array);
sort.heapSort();
display(array.iterator());
}
}
class Sort{
public Sort(ArrayList<Integer> array) {
this.array = array;
}
public void heapSort() {
buildHeap();
int localLength = array.size() - 1;
for(int i = array.size() - 1; i > 0; i--) {
int[] list = swap(array.get(0), array.get(i));
array.set(0, list[0]);
array.set(i, list[1]);
localLength -= 1;
maxHeapify(0, localLength);
}
}
public void buildHeap() {
for(int i = (array.size() - 1) / 2; i >= 0; i--) {
maxHeapify(i, array.size() - 1);
}
}
public void maxHeapify(int i, int len) {
int l = 2 * i + 1, r = 2 * i + 2, largest;
if(l <= len && array.get(l) > array.get(i))
largest = l;
else
largest = i;
if(r <= len && array.get(r) > array.get(largest))
largest = r;
if(largest != i) {
int[] list = swap(array.get(largest), array.get(i));
array.set(largest, list[0]);
array.set(i, list[1]);
maxHeapify(largest, len);
}
}
public int[] swap(int a, int b) {
int[] list = new int[2];
list[0] = b;
list[1] = a;
return list;
}
private ArrayList<Integer> array;
}
Python
heapSort.py
def swap(A, a, b):
c = A["data"][b]
A["data"][b] = A["data"][a]
A["data"][a] = c
def maxHeapify(A, i):
leftChildNum = 2 * i + 1
rightChildNum = 2 * i + 2
if leftChildNum <= A["length"] and A["data"][leftChildNum] > A["data"][i]:
largest = leftChildNum
else:
largest = i
if rightChildNum <= A["length"] and A["data"][rightChildNum] > A["data"][largest]:
largest = rightChildNum
if largest != i:
swap(A, largest, i)
maxHeapify(A, largest)
def buildMaxHeap(A):
for i in range(A["length"]/2, -1, -1):
maxHeapify(A, i)
def heapSort(A):
sortedList = []
buildMaxHeap(A);
for i in range(A["length"], 0, -1):
sortedList.append(A["data"][0])
swap(A, i, 0)
A["length"] -= 1
maxHeapify(A, 0)
sortedList.append(A["data"][0])
sortedList.reverse()
return sortedList
test.py
import heapSort
A = {"length": 9, "data": [4, 1, 3, 2, 16, 9, 10, 14, 8, 7]}
sortedList = heapSort.heapSort(A)
print sortedList