手撸一个A*+二叉堆优化(四)

二叉堆是堆的一种,使用完全二叉树来实现。所谓完全二叉树,即高度为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

Dijkstra算法是一种用于解决单源最短路径问题的经典算法,而二叉堆是一种常用的数据结构,用于实现Dijkstra算法中的优先队列。下面是Dijkstra算法结合二叉堆的Python代码实现: ```python import heapq def dijkstra(graph, start): # 初始化距离字典,用于记录起点到各个节点的最短距离 distances = {node: float('inf') for node in graph} distances[start] = 0 # 初始化优先队列,用于存储待处理的节点 queue = [(0, start)] while queue: # 弹出当前最小距离的节点 current_distance, current_node = heapq.heappop(queue) # 如果当前节点已经被处理过,则跳过 if current_distance > distances[current_node]: continue # 遍历当前节点的邻居节点 for neighbor, weight in graph[current_node].items(): distance = current_distance + weight # 如果通过当前节点到达邻居节点的距离更短,则更新最短距离并加入优先队列 if distance < distances[neighbor]: distances[neighbor] = distance heapq.heappush(queue, (distance, neighbor)) return distances ``` 上述代码中,`graph`表示图的邻接表表示,`start`表示起点。`distances`字典用于记录起点到各个节点的最短距离,初始时将所有节点的距离设为无穷大,起点的距离设为0。`queue`优先队列用于存储待处理的节点,初始时将起点加入队列。 在算法的主循环中,每次从优先队列中弹出当前最小距离的节点,然后遍历其邻居节点。如果通过当前节点到达邻居节点的距离更短,则更新最短距离并将邻居节点加入优先队列。 最后,返回`distances`字典,即起点到各个节点的最短距离。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值