代码改变世界

堆排序学习

2011-10-08 23:29  justvi  阅读(331)  评论(1编辑  收藏  举报

堆,数据结构表示为一个数组对象,可以被看做是一颗完全二叉树,下标为0的是树根,顺序下来依次是上一层结点的左右儿子。则可以定义操作parent,left,及right,分别返回当前数组元素的父、左儿子、右儿子下标。

1 #define PARENT(i) (((i) + 1) / 2 + 1)
2 #define LEFT(i) ((i) * 2 + 1)
3 #define RIGHT(i) (((i) + 1) * 2)

保持(最大)堆的性质:假定当前结点的左右子树都满足了(最大)堆的性质,但此时此结点的值小于其中的一个或两个,则违反了(最大)堆的性质,需要进行调整,首先找到父、左儿子、右儿子中值最大的结点,将此结点值与父结点值交换,这时这三个已经满足了子女值都比父值小得性质,但是交换后可能破坏了原来左或右子树的最大堆性质,这时递归上面的方法直到满足最大堆性质。

 1 void maxheapify(int *a, int i, int heapsize)
2 {
3 int l = LEFT(i);
4 int r = RIGHT(i);
5 int largest = -1;
6 if (l < heapsize && a[l] > a[i])
7 largest = l;
8 else
9 largest = i;
10 if (r < heapsize && a[r] > a[largest])
11 largest = r;
12 if (i != largest)
13 {
14 exchange(a, i, largest);
15 maxheapify(a, largest, heapsize);
16 }
17 }

建堆:对于一个数组,要建立一个堆,已知前述方法能够使某结点的子树保持最大堆的性质,则从len/2处开始直到树根运用这一方法就能够建堆了。

1 void buildmaxheap(int *a, int len)
2 {
3 int heapsize = len;
4 int i;
5 for (i = (len - 1) / 2; i >= 0; i--)
6 maxheapify(a, i, heapsize);
7 }

堆排序将数组元素从小到大排列,已知此时数组已满足最大堆的性质,此时最大的值在树根处,将树根的值与最后一位值交换就将最大的元素放到了正确的位置。此时可能又不满足堆性质了,使用maxheapify使之满足,再将树根与倒数第二位交换,这样循环直到树根处。

 1 void heapsort(int *a, int len)
2 {
3 int heapsize = len;
4 int i;
5 buildmaxheap(a, len);
6 for (i = len - 1; i > 0; i--)
7 {
8 exchange(a, 0, i);
9 heapsize--;
10 maxheapify(a, 0, heapsize);
11 }
12 }

测试:

 1 int main()
2 {
3 int a[] = {14, 10, 2, 4, 1, 16, 8, 7, 9, 3};
4 int i;
5 int len = sizeof(a)/sizeof(a[0]);
6 heapsort(A, len);
7 for (i = 0; i < len; i++)
8 printf("%d\n", a[i]);
9 return 0;
10 }

值得一提的是,在这个过程中遇到了一些问题,主要是堆大小heapsize的调整及数组下标的对应,不是下标越界就是少算了一位,在调试的过程中,学会了gdb的基本简单操作及用gdb调试程序有了感性直观的了解;以及emacs的熟悉,可以在其中编辑、调gcc编译、gdb简单调试、shell了,一点感慨,emacs真特么太强了。