.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;
}
}
}