.net下使用最小堆实现TopN算法

测试代码:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace HeapSort
{
    class Program
    {
        static void Main(string[] args)
        {
            var arr = new int[] { 10, 7, 5 ,1,2,5};

            List<int> src = new List<int>(arr);
            HeapSort<int> heapSort = new HeapSort<int>((a, b) => { return a - b; }, src);
            
            heapSort.TryAddNumber(6);

            heapSort.TryAddNumber(7);

            heapSort.TryAddNumber(8);

            heapSort.TryAddNumber(9);

            heapSort.TryAddNumber(10);
            Console.WriteLine(string.Join(",", heapSort.MinHeapsortToDescend().ConvertAll((t) => t.ToString()).ToArray()));

            Console.ReadLine();
        }
    }
}

算法实现:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace HeapSort
{
    /// <summary>
    /// 使用最小堆算法计算 TopN,参考 http://blog.csdn.net/morewindows/article/details/6709644
    /// 最小堆性质:"父结点的键值总是小于或等于任何一个子节点的键值"
    /// </summary>
    /// <typeparam name="T"></typeparam>
    class HeapSort<T>
    {
        Comparison<T> comparison;
        List<T> minHeapList;
        public HeapSort(Comparison<T> _comparison, List<T> list)
        {
            comparison = _comparison;
            MakeMinHeap(list);
        }
        /* 这里只是将最小堆用于计算TopN,因此不需要添加节点
        //  新加入i结点  其父结点为(i - 1) / 2  
        void MinHeapFixup(int i)
        {
            int parent;

            var temp = minHeapList[i];
            parent = (i - 1) / 2;      //父结点  
            while (parent >= 0 && i != 0)
            {
                //if (list[parent] <= temp)
                if (comparison(minHeapList[parent], temp) <= 0)
                    break;

                minHeapList[i] = minHeapList[parent];     //把较大的子结点往下移动,替换它的子结点  
                i = parent;
                parent = (i - 1) / 2;
            }
            minHeapList[i] = temp;
        }
        //在最小堆中加入新的数据nNum  
        void MinHeapAddNumber(int n, T nNum)
        {
            minHeapList[n] = nNum;
            MinHeapFixup(n);
        }*/
        //  从i节点开始调整,n为节点总数 从0开始计算 i节点的子节点为 2*i+1, 2*i+2  
        void MinHeapFixdown(int i, int n)
        {
            int leftChild;

            var temp = minHeapList[i];
            leftChild = 2 * i + 1;
            while (leftChild < n)
            {
                //if (leftChild + 1 < n && a[leftChild + 1] < a[leftChild]) //在左右孩子中找最小的  
                if (leftChild + 1 < n && comparison(minHeapList[leftChild + 1], minHeapList[leftChild]) < 0) //在左右孩子中找最小的  
                    leftChild++;

                //if (a[leftChild] >= temp)
                if (comparison(minHeapList[leftChild], temp) >= 0)
                    break;

                minHeapList[i] = minHeapList[leftChild];     //把较小的子结点往上移动,替换它的父结点  
                i = leftChild;
                leftChild = 2 * i + 1;
            }
            minHeapList[i] = temp;
        }
        /* 在TopN中不需要这样做
        //在最小堆中删除数  
        void MinHeapDeleteNumber(int n)
        {
            Swap(0, n - 1);
            MinHeapFixdown(0, n - 1);
        }
         */
        /// <summary>
        /// 尝试添加节点,如果小于等于最小根,不处理
        /// </summary>
        /// <param name="item"></param>
        public void TryAddNumber(T item)
        {
            if (comparison(minHeapList[0], item) >= 0)//如果小于等于最小根,不处理
            {
                return;
            }
            minHeapList[0] = item;//直接覆盖根节点,然后向下比较,以确保最小堆性质:"父结点的键值总是小于或等于任何一个子节点的键值"
            MinHeapFixdown(0, minHeapList.Count);
        }
        /// <summary>
        /// 排序建立最小堆
        /// </summary>
        void MakeMinHeap(List<T> list)
        {
            minHeapList = list;
            for (int i = list.Count / 2 - 1; i >= 0; i--)
                MinHeapFixdown(i, list.Count);
        }
        void Swap(int index1, int index2)
        {
            var temp = minHeapList[index1];
            minHeapList[index1] = minHeapList[index2];
            minHeapList[index2] = temp;
        }
        /// <summary>
        /// 排序,在插入未完成之前,千万不要调用排序,这会破坏最小堆的性质
        /// </summary>
        public List<T> MinHeapsortToDescend()
        {
            for (int i = minHeapList.Count - 1; i >= 1; i--)
            {
                Swap(i, 0);
                MinHeapFixdown(0, i);
            }
            return minHeapList;
        }
    }
}

posted @ 2014-08-11 09:22  花生!~~  阅读(391)  评论(0编辑  收藏  举报