代码改变世界

算法排序之堆排序

2010-12-08 23:06  MichaelYin  阅读(378)  评论(0编辑  收藏  举报

堆排序的重点在于对堆的理解,首先堆是一种数组对象,同时,它也可以被视为一棵完全二叉树,树中的每个节点从上到下,从左到右和数组中的每个元素是一一对应的,二叉树的每一层都是填满的,除了最后一层以外。

比如数组中的第一个元素就是二叉树的根节点,第二个就是元素就是根节点的左边的子节点,而第三个节点就是根节点的右边的子节点,然后第四个节点就是根节点的左边的子节点的左边的子节点,这样以此类推。

通过给定的节点的数组下标,我们可以求出该节点的左边子节点的坐标和右边子节点的坐标。

        public static int Left(int parent)
        {
            return (2 * parent) + 1;
        }

    
        public static int Right(int parent)
        {
            return (2 * parent);
        }

堆有两种,一种是最大堆,一种是最小堆,最大堆的性质就是每个母节点都比它的子节点大,最小堆的性质则相反。我们这里的排序就用最大堆来排序。

Heapify这个方法用来保持最大堆的母节点总是比子节点大这个特性,将数组坐标i 和数组输入,它从母节点i 处开始比较,如果发现子节点比母节点大,那么就会让母节点下降,下降的母节点还有可能比下面的节点大,所以又进行比较。方法执行完之后,可以保证以i 为根节点的子树成为最大堆。关于这个方法有两种实现方式,递归和循环调用,我都写出来了,大家可以都看一下。

        /// <summary>
        /// 保证最大堆的性质,采用递归方法实现
        /// </summary>
        /// <param name="arrayToSort"></param>
        /// <param name="index"></param>
        public static void HeapifyType1(int[] arrayToSort, int index)
        {
            int leftIndex = HeapSort.Left(index);
            int rightIndex = HeapSort.Right(index);
            //先假设父节点最大,然后进行比较
            int largestIndex = index;
            if (leftIndex < HeapSort.HeapSize && arrayToSort[leftIndex] > arrayToSort[largestIndex])
                largestIndex = leftIndex;
            if (rightIndex < HeapSort.HeapSize && arrayToSort[rightIndex] > arrayToSort[largestIndex])
                largestIndex = rightIndex;
            //判断是否需要朝下继续生成堆
            if (largestIndex != index)
            {
                //调换位置
                Utils.Swap(ref arrayToSort[index], ref arrayToSort[largestIndex]);
                HeapSort.HeapifyType1(arrayToSort, largestIndex);
            }
        }

        /// <summary>
        /// 保证最大堆的性质,采用迭代方法实现,功能和前面一样
        /// </summary>
        /// <param name="arrayToSort"></param>
        /// <param name="index"></param>
        public static void HeapifyType2(int[] arrayToSort, int index)
        {
            int outIndex = index;
            while (true)
            {
                int leftIndex = HeapSort.Left(outIndex);
                int rightIndex = HeapSort.Right(outIndex);
                //先假设父节点最大,然后进行比较
                int largestIndex = outIndex;
                if (leftIndex < HeapSort.HeapSize && arrayToSort[leftIndex] > arrayToSort[largestIndex])
                    largestIndex = leftIndex;
                if (rightIndex < HeapSort.HeapSize && arrayToSort[rightIndex] > arrayToSort[largestIndex])
                    largestIndex = rightIndex;
                //判断是否需要朝下继续生成堆
                if (largestIndex != outIndex)
                {
                    //调换位置
                    Utils.Swap(ref arrayToSort[outIndex], ref arrayToSort[largestIndex]);
                    //调整节点index
                    outIndex = largestIndex;
                }
                else
                {
                    break;
                }
            }
        }

在开始排序之前,我们需要先把最大堆建立起来,如何建立最大堆呢,我们从底层往上调用Hepify方法,由于底层确定了每一次选出来的是最大值,那么上一层的方法在这个基础上才会执行正确。因为一个a[n]的数组,其中后面一半肯定是叶子节点,这可以看成一个性质,叶子节点可以看成是只含一个元素的堆。知道了这些,就不难理解我们通过Heapify将最大堆建立起来的原理了。

        public static void BuildHeap(int[] arrayToSort)
        {
            HeapSort.HeapSize = arrayToSort.Length;
            for (int i = (arrayToSort.Length) / 2; i > -1; i--)
                HeapifyType1(arrayToSort, i);
        }

 

另外需要说明的一点是,HeapSize这个变量表示的是Heap的大小,因为我们维护的堆在开始和数组的长度是相等的,但后后来会小于数组的长度,

public static int HeapSize;

 

下面就是排序的过程了,我们通过前面提到的BuildHeap方法将最大堆建立起来后,显而易见根节点就是最大值,然后将根节点和后面的元素进行互换,并减小HeapSize,这样就不会考虑那些已经剔除的最大值,和最大值互换的叶子节点的值显然不一定比后来的子节点大,所以调用Heapify重新选出最大值。然后循环这个过程,最终完成数组的排序。

        public static void Sort(int[] arrayToSort)
        {
            HeapSort.BuildHeap(arrayToSort);
            for (int i = arrayToSort.Length - 1; i > 0; i--)
            {
                Utils.Swap(ref arrayToSort[i], ref arrayToSort[0]);
                HeapSort.HeapSize--;
                HeapSort.HeapifyType1(arrayToSort, 0);
            }
        }
堆排序其实主要就是一个维护堆的过程,始终保持每一个母节点比子节点大这个性质。优先级队列的实现用堆是比较合适的。