堆排序实现

堆排序与快速排序归并排序一样都是时间复杂度为O(N*logN)的几种常见排序方法。堆排序是跟二叉堆联系在一起的,它是利用二叉堆的性质设计的一种算法。所以它有父节点值总是大于(或小于其所有子节点)。一般情况下,二叉堆是如下所示的:

                                                                         这是一个正序从小到大的序列。

 首先,肯定是插入问题了,我们首先要构造好一个堆。其实这里的插入用插入排序的方法就可以了。他的实现就是直接插入。。

 1 void minHeapFixUp(int *arrayL, int newElementIndex) {
 2     int fatherIndex = (newElementIndex - 1) / 2;
 3     int temp = arrayL[newElementIndex];
 4 
 5     //当新加入的子节点的值小于父节点的时候,将其变为父节点...
 6     //同时向上遍历找到最终的某个元素出现其父元素值大于这个新加入
 7     //节点的时候停止
 8     while (fatherIndex >= 0 && newElementIndex != 0) {
 9         if (arrayL[fatherIndex] < temp)
10             break;
11 
12         arrayL[newElementIndex] = arrayL[fatherIndex];
13         newElementIndex = fatherIndex;
14         fatherIndex = (newElementIndex - 1) / 2;
15     }
16     arrayL[newElementIndex] = temp;
17 }
18 
19 void insertElement(int* arrayL, int insertIndex, int value) {
20     arrayL[insertIndex] = value;
21     minHeapFixUp(arrayL, insertIndex);
22 }

 

二叉堆的堆化什么叫二叉堆的堆化?也就是讲一个序列变成一个符合二叉堆的样子的序列,也就是从父节点要比所有子节点小(或大)。比如:

                                  

那应该如何用程序对一个序列进行堆化呢【一般情况下我们用数组储存元素】,首先我们要知道一点,父节点和子节点的下标的关系是:

                                              fatherNode = (sonNode - 1) / 2;

                                                                                                                             sonNode1 = 2 * father + 1;

                                                                                                                             sonNode2 = 2 * fatherNode + 2;

有了这三条,我们就可以根据节点来对其子节点或父节点进行处理了。

下面我们来谈谈堆化数组

     堆化数组,就是将一个数组变成符合二叉堆性质的数组。那么要如何进行堆化呢,我们可以先将未堆化的数组看成一个二叉树。首先我们从树叶A开始将其与其父节点B变得有序,然后处理其父节点B与B的父节点...如此下去就可以将整个树变成二叉堆。

我们来看个例子:对数组 Array[3, 20, 12, 23, 45, 22, 32, 65, 2, 19, 34, 90, 67],用图表示如下:

 

                                                           

我们可以看出,有第二,第三个叶节点不满足堆的要求,而其他的在父节点和子节点之间已经满足对的条件。所以我们首先将Array[5]和Array[10]互换。Array[4]和Array[9]同理。

至此,堆化完毕,我们可以看到,这已经是一个有效的二叉堆了。

那么,我们改怎样用代码来实现呢?如下:

 1 void minHeapFixDown(int *arrayL, int beginIndex, int totalNode) {
 2     int temp = arrayL[beginIndex];
 3     int sonNode = beginIndex * 2 + 1;
 4 
 5     while (sonNode < totalNode) {
 6         //找到最小那个子节点(如果是两个的话)
 7         if (sonNode + 1 < totalNode && arrayL[sonNode] > arrayL[sonNode + 1])
 8             sonNode++;
 9 
10         if (arrayL[sonNode] >= temp)
11             break;
12 
13         a[beginIndex] = arrayL[sonNode];
14         //向上遍历
15         beginIndex = sonNode;
16         sonNode = beginIndex * 2 + 1;
17     }
18     arrayL[beginIndex] = temp;
19 }
20 
21 void makeMinHeap(int *arrayL, int len) {
22     //遍历每一个子节点,并与父节点比较,进行处理
23     for (int i = len / 2 - 1; i >= 0; i--)
24         minHeapFixDown(arrayL, i, len); 
25 }

 

下面来看看怎么删除元素:在二叉堆中,我们删除元素都是讲Array[0]删除然后将最后一个元素补上。然后再进行一次堆化。那么就是如下:

1 void deleteNode(int* arrayL, int len) {
2     swap(arrayL[0], arrayL[len - 1]);
3     minHeapFixDown(arrayL, 0, len - 1);
4 }

我们直接将Array[0]交换到数组最后,原因下面再说明。

到这里,我们就可以进行排序了。其实堆排的原理就是将第一个节点取出,然后再堆化数组,然后再取出新数组的第一个元素,接到原来取出的那个后面。因为第一个元素总是整个数组中最小的一个,所以取出来的数就是一个有序列了。

1 void heapSort(int *arrayL, int len) {
2     makeMinHeap(arrayL, len);
3 
4     for (int i = len - 1; i >= 0; i--) {
5         swap(arrayL[0], arrayL[i])
6         minHeapFixDown(arrayL, 0, i);
7     }
8 }

我们直接将新取出的元素接在数组后面,就可以用节省空间。。

posted @ 2013-11-25 01:15  厕所门口~~  阅读(382)  评论(0编辑  收藏  举报