二叉堆是堆的一种,使用完全二叉树来实现。所谓完全二叉树,即高度为n的二叉树,其前n-1层必须被填满,第n层也要从左到右顺序填满。在二叉堆中,所有非终端结点的值均不大于(或不小于)其左右孩子的值。若非终端结点的值均不大于其左右孩子结点的值,这样的二叉堆叫做小根堆(图(b)),小根堆根结点的值是该堆中所有结点的最小值;同样的,当所有非终端结点的值都不小于其左右孩子的值时,这样的对叫做大根堆(图(a)),大根堆根结点的值为改堆所有结点的最大值。利用堆的此性质,可以实现堆排序。
这里使用小根堆来对开启列表进行排序,以下是堆排序的几个要点:
1.得到子节点的公式很简单,当前节点为n,那左子叶就是2n+1,右子叶就是2n+2。
2.反之得到父节点,那就是(n-1)/2。
3.新加入的元素在树的最后一个位置。然后向上和父节点做比较,如果比父节点小就交换,一直比较到根节点。
4.移除元素,就是弹出根节点,把树的最后一个位置的元素补上,向下比较排序,先比较左右子节点,得到较小的一个子节点再和自身进行比较,如果子节点小就交换。
开始撸代码,建立一个Heap脚本
先写一个泛型接口,继承IComparable,内部方法子类必须实现
public interface IHeapItem<T>:IComparable<T>
{
int HeapIndex
{
get;
set;
}
}
二叉堆对泛型进行约束
public class Heap<T> where T:IHeapItem<T>
{
}
先把树的框架搭起来
T[] items;
int currentItemCount;//当前树的大小
//指定树的容量
public Heap(int maxHeapSize)
{
items = new T[maxHeapSize];
}
//加入新元素,向上排序
public void Add(T item)
{
}
//移除根节点,向下排序
public T RemoveFirst()
{
}
//更新树重新排序
public void UpdateItem(T item)
{
}
//返回当前元素数量
public int Count()
{
}
//是否存在元素
public bool Contains(T item)
{
}
//向下排序,寻找子节点
void SortDown(T item)
{
}
//向上排序,寻找父节点
void SortUp(T item)
{
}
//交换节点
void Swap(T itemA, T itemB)
{
}
开始填内容
public void Add(T item)
{
//先插入到最后
item.HeapIndex = currentItemCount;
items[currentItemCount] = item;
//向上排序
SortUp(item);
currentItemCount++;
}
//移除根节点,向下排序
public T RemoveFirst()
{
T firstItem = items[0];//根节点
currentItemCount--;
items[0] = items[currentItemCount];//填充最后一个元素
items[0].HeapIndex = 0;
SortDown(items[0]);
return firstItem;
}
public void UpdateItem(T item)
{
SortUp(item);
}
public int Count()
{
return currentItemCount;
}
public bool Contains(T item)
{
return Equals(items[item.HeapIndex], item);
}
//向下排序,寻找子节点
void SortDown(T item)
{
while (true)
{
int childIndexLeft = item.HeapIndex * 2 + 1;//左叶
int childIndexRight = item.HeapIndex * 2 + 2;//右叶
int swapIndex = 0;
//如果还存在子节点
if (childIndexLeft<currentItemCount)
{
swapIndex = childIndexLeft;
if (childIndexRight<currentItemCount)
{
if (items[childIndexLeft].CompareTo(items[childIndexRight])>0)
{
swapIndex = childIndexRight;//得到小的节点
}
}
if (item.CompareTo(items[swapIndex])>0)//和小的节点比较
{
Swap(item, items[swapIndex]);
}//如果子节点大,返回
else
{
return;
}
}//如果没有子节点了,返回
else
{
return;
}
}
}
//向上排序,寻找父节点
void SortUp(T item)
{
int parentIndex = (item.HeapIndex - 1) / 2;//得到父节点
while (true)
{
T parentItem = items[parentIndex];
if (item.CompareTo(parentItem)<0)//a.compareto(b) a<b=-1 a=b=0 a>b=1
{
Swap(item, parentItem);//当前节点更小就交换
}
else
{
break;
}
parentIndex = (item.HeapIndex - 1) / 2;//继续向上比较
}
}
//交换数据和指针
void Swap(T itemA, T itemB)
{
items[itemA.HeapIndex] = itemB;
items[itemB.HeapIndex] = itemA;
int itemAIndex = itemA.HeapIndex;
itemA.HeapIndex = itemB.HeapIndex;
itemB.HeapIndex = itemAIndex;
}
开始修改Node,先继承接口
public class Node :IHeapItem<Node>
实现接口方法
int heapIndex;
//内部构造一个指针
public int HeapIndex
{
get {
return heapIndex;
}set
{
heapIndex = value;
}
}
实现接口继承的方法,也就是比较估价值
public int CompareTo(Node nodeToCompare)
{
int compare = fCost.CompareTo(nodeToCompare.fCost);
if (compare==0)
{
compare = hCost.CompareTo(nodeToCompare.hCost);
}
return compare;
}
Grid类怎加一个返回地图大小的属性
public int MaxSize
{
get
{
return gridCntX * gridCntY;
}
}
开始修改FindPath类中FindingPath中遍历搜索开启列表的部分
先把开启列表修改成为树
Heap<Node> openSet = new Heap<Node>(_gird.MaxSize);//根据地图大小实例化堆的容量
修改遍历方法
while (openSet.Count()>0)
//Node CurrentNode = openSet[0];
//for (int i = 0; i < openSet.Count; i++)
//{
// if (openSet[i].fCost<CurrentNode.fCost||
//
//(openSet[i].fCost==CurrentNode.fCost&&openSet[i].hCost<CurrentNode.hCost))
// {
// CurrentNode = openSet[i];
// }
//}
Node CurrentNode = openSet.RemoveFirst();
//openSet.Remove(CurrentNode);
closeSet.Add(CurrentNode);
.......
}
嗯,现在对A*的二叉堆优化就完成了
项目文件 if you want https://github.com/lvcoc/SimpleAstar