《算法导论》学习笔记——堆排序

堆排序

1.什么是堆

  (二叉)堆是一个不完全二叉树,即除了最底层之外,该书是完全满的。那么怎样把堆和一个待排序的数组联系起来呢?这就涉及到二叉堆(二叉树)的一些性质:
  - 性质1:设父亲节点的编号为i,则左孩子的编号为2i,右孩子的编号为2i,即LeftChild[i] = 2iRightChild[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] = 2iRightChild[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
posted @ 2015-01-19 17:00  ZhxBao的博客  阅读(371)  评论(0)    收藏  举报