一种二叉堆的泛化实现

63 篇文章 1 订阅

本文列出了一种二叉堆的泛化实现方法

所谓二叉堆,其实就是一种完全二叉树或者近似完全二叉树,树中父节点的键值总是保持某种固定的序关系于任何一个子节点的键值,且每个节点的左子树右子树也都是二叉堆.

这里列出一种二叉堆的泛化实现方法,其中值得一提的地方有这么几处:

  • 泛化数据类型,方法自然是使用泛型实现
  • 泛化序关系的表达方式,传统的最大堆和最小堆实现一般就是直接使用数值的大小比较,泛化的方法则是使用泛型委托
  • 实现了一种类似于空间划分的元素查找方法

完整的代码可以在gist上找到:

using System;
using System.Diagnostics;
using System.Collections.Generic;
using System.Collections.ObjectModel;

public class Heap<T> where T : IEquatable<T>
{	
	public static Heap<T> Build(IEnumerable<T> items, Func<T, T, bool> predicate)
	{
		var heap = new Heap<T>(predicate);
		
		if (items != null)
		{
		    var iter = items.GetEnumerator();
		    while (iter.MoveNext())
		    {
		    	var item = iter.Current;
		    	heap.Add(item);
		    }
		}
		
		return heap;
	}
	
	/*
	public static Heap<T> Build2(IEnumerable<T> items, Func<T, T, bool> predicate)
	{
		var heap = new Heap<T>(predicate);
		
		if (items != null)
		{
			heap.m_items.AddRange(items);
			var itemCountIndexBegin = heap.m_items.Count / 2;
			var itemCountIndexEnd = 1;
			for (int i = itemCountIndexBegin; i >= itemCountIndexEnd; --i)
			{
				// NOTE this requires Heapify() just do "down-heap" operation
				//      now Heapify() do both "up-heap" and "down-heap" operation
				heap.Heapify(i - 1);
			}
		}
		
		return heap;
	}
	*/
	
	Func<T, T, bool> m_predicate;
	List<T> m_items = new List<T>();
	
	public int Count
	{
		get
		{
			return m_items.Count;
		}
	}
	
	public ReadOnlyCollection<T> Items
	{
		get
		{
		    return m_items.AsReadOnly();
		}
	}
	
	public T this[int index]
	{
		get
		{
			return m_items[index];
		}
	}
	
	public Heap(Func<T, T, bool> predicate)
	{
		Debug.Assert(predicate != null);
		m_predicate = predicate;
	}
	
	int IndexOfRecur(T item, int itemIndex)
	{
		if (IsValidIndex(itemIndex))
		{
			if (!m_predicate(item, m_items[itemIndex]))
			{
				// not found
				return -1;
			}
			else
			{
				if (m_items[itemIndex].Equals(item))
				{
					return itemIndex;
				}
				else
				{
					var itemCountIndex = itemIndex + 1;
					var childCountIndexLeft = itemCountIndex * 2;
					var childIndexLeft = childCountIndexLeft - 1;
					
					var index = IndexOfRecur(item, childIndexLeft);
					if (index >= 0)
					{
						return index;
					}
					else
					{
						var childCountIndexRight = itemCountIndex * 2 + 1;
					    var childIndexRight = childCountIndexRight - 1;
						return IndexOfRecur(item, childIndexRight);
					}
				}
			}
		}
		
		// default return -1
		return -1;
	}
	
	// return -1 when not found(recur)
	public int IndexOf(T item)
	{
		return IndexOfRecur(item, 0);
	}
	
	// return -1 when not found(iterator)
	public int IndexOf2(T item)
	{
		return m_items.IndexOf(item);
	}
	
	public void Add(T item)
	{
		m_items.Add(item);
		Heapify(m_items.Count - 1);
	}
	
	public void Remove(T item)
	{
		var itemIndex = IndexOf(item);
		if (itemIndex >= 0)
		{
			Swap(itemIndex, m_items.Count - 1);
			m_items.RemoveAt(m_items.Count - 1);
			Heapify(itemIndex);
		}
	}
	
	public void RemoveAt(int itemIndex)
	{
		if (IsValidIndex(itemIndex))
		{
			Swap(itemIndex, m_items.Count - 1);
			m_items.RemoveAt(m_items.Count - 1);
			Heapify(itemIndex);
		}
	}
	
	public void Clear()
	{
		m_items.Clear();
	}
	
	bool IsValidIndex(int itemIndex)
	{
		return itemIndex >= 0 && itemIndex < m_items.Count;
	}
	
	void Swap(int index1, int index2)
	{
		if (IsValidIndex(index1) && IsValidIndex(index2))
		{
			var temp = m_items[index1];
			m_items[index1] = m_items[index2];
			m_items[index2] = temp;
		}
	}
	
	void Heapify(int itemIndex)
	{
		if (IsValidIndex(itemIndex))
		{
			var itemCountIndex = itemIndex + 1;
			
			// first check parent
			var parentCountIndex = itemCountIndex / 2;
			var parentIndex = parentCountIndex - 1;
			if (IsValidIndex(parentIndex) && !m_predicate(m_items[itemIndex], m_items[parentIndex]))
			{
				while (IsValidIndex(itemIndex) && IsValidIndex(parentIndex) && !m_predicate(m_items[itemIndex], m_items[parentIndex]))
				{
					Swap(itemIndex, parentIndex);
					
					// update index
					itemIndex = parentIndex;
					itemCountIndex = itemIndex + 1;
					parentCountIndex = itemCountIndex / 2;
			        parentIndex = parentCountIndex - 1;
				}
			}
			else
			{
				// then check child
				var childCountIndexLeft = itemCountIndex * 2;
				var childIndexLeft = childCountIndexLeft - 1;
				var childCountIndexRight = itemCountIndex * 2 + 1;
				var childIndexRight = childCountIndexRight - 1;
				
				while (IsValidIndex(childIndexLeft))
				{
					if (IsValidIndex(childIndexRight))
					{
						if (!m_predicate(m_items[childIndexLeft], m_items[itemIndex]) || !m_predicate(m_items[childIndexRight], m_items[itemIndex]))
						{
							if (!m_predicate(m_items[childIndexLeft], m_items[itemIndex]) && m_predicate(m_items[childIndexRight], m_items[itemIndex]))
							{
								Swap(childIndexLeft, itemIndex);
								
								itemIndex = childIndexLeft;
								itemCountIndex = childCountIndexLeft;
							}
							else if (m_predicate(m_items[childIndexLeft], m_items[itemIndex]) && !m_predicate(m_items[childIndexRight], m_items[itemIndex]))
							{
								Swap(childIndexRight, itemIndex);
								
								itemIndex = childIndexRight;
								itemCountIndex = childCountIndexRight;
							}
							else
							{
								if (m_predicate(m_items[childIndexLeft], m_items[childIndexRight]))
								{
									// left should be child
									Swap(childIndexRight, itemIndex);
								
									itemIndex = childIndexRight;
									itemCountIndex = childCountIndexRight;
								}
								else
								{
									// right should be child
									Swap(childIndexLeft, itemIndex);
								
									itemIndex = childIndexLeft;
									itemCountIndex = childCountIndexLeft;
								}
							}
						
							// update index
							childCountIndexLeft = itemCountIndex * 2;
							childIndexLeft = childCountIndexLeft - 1;
							childCountIndexRight = itemCountIndex * 2 + 1;
							childIndexRight = childCountIndexRight - 1;
						}
						else
						{
							// break since fit
							break;
						}
					}
					else
					{
						// right child is invalid, reach end
						if (!m_predicate(m_items[childIndexLeft], m_items[itemIndex]))
						{
							Swap(childIndexLeft, itemIndex);
						}
						
						// break since reach end
						break;
					}
				}
			}
		}
	}
}

参考资料

不出意外的话,这应该是 2019 新年之前的最后的一篇博文,来年继续吧~

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值