二叉堆(BinaryHeap)的一种C#实现

二叉堆(BinaryHeap)的一种C#实现

最近项目里用到了寻路,可以用两种算法实现,迪杰斯特拉和AStar。其中AStar是迪杰斯特拉算法的一种改进,所以使用更广泛一点。这篇文章不讨论这两种算法,而聚焦于这两种算法中都用到的一种数据结构——二叉堆(BinaryHeap)。

在这两种算法中的性能瓶颈最可能发生在从”开表”中取出路径最短的节点,也就是从一个集合中取出最小元素。最简单的方法当然就是对集合进行排序,然后取出最小值,而排序操作往往需要耗费大量的性能,尤其是元素较多时。其实我们只需要得到最小的元素,而不太关心其他元素。二叉堆就是这样一种数据结构。具体的概念和原理在这里:

浅析基础数据结构——二叉堆

总结来说,这种数据结构使用一个数组实现,它总是可以保证第0个元素是集合中最小(最小堆)或者最大(最大堆)的,而不保证之后的元素顺序。在寻路中需要的是最小堆。在二叉堆中插入,删除操作复杂度都为O(log2n),要优于大部分的排序算法。下面给出一种二叉堆在C#中的实现:

using System;

/* 二叉堆用于寻路
 * 注意T为值类型时默认值对结果的影响
 * 最小堆
 */
public class BinaryHeap<T> where T : IComparable<T>
{
    private readonly T[] items;
    private readonly bool IsClass;
    public int Count { get; private set; }
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
下面是一个 TypeScript 实现的双向 A* 算法,其中使用二叉堆作为优先队列: ```typescript interface Node { x: number; y: number; f: number; g: number; h: number; parent: Node | null; } function aStar(start: Node, end: Node, map: number[][]): Node | null { const openList1: Node[] = [start]; const openList2: Node[] = [end]; const closeList1: Node[] = []; const closeList2: Node[] = []; const heap1 = new BinaryHeap<Node>((a, b) => a.f < b.f); const heap2 = new BinaryHeap<Node>((a, b) => a.f < b.f); heap1.push(start); heap2.push(end); while (heap1.size() > 0 && heap2.size() > 0) { const current1 = heap1.pop()!; const current2 = heap2.pop()!; closeList1.push(current1); closeList2.push(current2); if (closeList2.some(node => node.x === current1.x && node.y === current1.y)) { const node = closeList2.find(node => node.x === current1.x && node.y === current1.y); return node?.parent ? node.parent : node; } if (closeList1.some(node => node.x === current2.x && node.y === current2.y)) { const node = closeList1.find(node => node.x === current2.x && node.y === current2.y); return node?.parent ? node.parent : node; } const neighbors1 = getNeighbors(current1, map); neighbors1.forEach(neighbor => { if (closeList1.some(node => node.x === neighbor.x && node.y === neighbor.y)) return; if (!openList1.some(node => node.x === neighbor.x && node.y === neighbor.y)) { neighbor.g = current1.g + 1; neighbor.h = manhattanDistance(neighbor, end); neighbor.f = neighbor.g + neighbor.h; neighbor.parent = current1; openList1.push(neighbor); heap1.push(neighbor); } else { const existingNode = openList1.find(node => node.x === neighbor.x && node.y === neighbor.y); if (existingNode && current1.g + 1 < existingNode.g) { existingNode.g = current1.g + 1; existingNode.f = existingNode.g + existingNode.h; existingNode.parent = current1; heap1.update(existingNode); } } }); const neighbors2 = getNeighbors(current2, map); neighbors2.forEach(neighbor => { if (closeList2.some(node => node.x === neighbor.x && node.y === neighbor.y)) return; if (!openList2.some(node => node.x === neighbor.x && node.y === neighbor.y)) { neighbor.g = current2.g + 1; neighbor.h = manhattanDistance(neighbor, start); neighbor.f = neighbor.g + neighbor.h; neighbor.parent = current2; openList2.push(neighbor); heap2.push(neighbor); } else { const existingNode = openList2.find(node => node.x === neighbor.x && node.y === neighbor.y); if (existingNode && current2.g + 1 < existingNode.g) { existingNode.g = current2.g + 1; existingNode.f = existingNode.g + existingNode.h; existingNode.parent = current2; heap2.update(existingNode); } } }); } return null; } function getNeighbors(node: Node, map: number[][]): Node[] { const neighbors: Node[] = []; const { x, y } = node; if (y > 0 && map[y - 1][x] !== 1) neighbors.push({ x, y: y - 1, f: 0, g: 0, h: 0, parent: null }); if (y < map.length - 1 && map[y + 1][x] !== 1) neighbors.push({ x, y: y + 1, f: 0, g: 0, h: 0, parent: null }); if (x > 0 && map[y][x - 1] !== 1) neighbors.push({ x: x - 1, y, f: 0, g: 0, h: 0, parent: null }); if (x < map[0].length - 1 && map[y][x + 1] !== 1) neighbors.push({ x: x + 1, y, f: 0, g: 0, h: 0, parent: null }); return neighbors; } function manhattanDistance(node1: Node, node2: Node): number { return Math.abs(node1.x - node2.x) + Math.abs(node1.y - node2.y); } class BinaryHeap<T> { private heap: T[] = []; private compareFn: (a: T, b: T) => boolean; constructor(compareFn: (a: T, b: T) => boolean) { this.compareFn = compareFn; } size(): number { return this.heap.length; } push(item: T): void { this.heap.push(item); this.bubbleUp(this.heap.length - 1); } pop(): T | undefined { if (this.heap.length === 0) return undefined; const root = this.heap[0]; const last = this.heap.pop()!; if (this.heap.length > 0) { this.heap[0] = last; this.bubbleDown(0); } return root; } update(item: T): void { const index = this.heap.indexOf(item); if (index >= 0) { this.bubbleUp(index); this.bubbleDown(index); } } private bubbleUp(index: number): void { const item = this.heap[index]; while (index > 0) { const parentIndex = Math.floor((index - 1) / 2); const parent = this.heap[parentIndex]; if (this.compareFn(item, parent)) { this.heap[index] = parent; index = parentIndex; } else break; } this.heap[index] = item; } private bubbleDown(index: number): void { const item = this.heap[index]; const length = this.heap.length; while (index < length) { const leftIndex = index * 2 + 1; const rightIndex = index * 2 + 2; let leftChild: T, rightChild: T; let swapIndex = -1; if (leftIndex < length) { leftChild = this.heap[leftIndex]; if (this.compareFn(leftChild, item)) swapIndex = leftIndex; } if (rightIndex < length) { rightChild = this.heap[rightIndex]; if (this.compareFn(rightChild, item) && this.compareFn(rightChild, leftChild)) swapIndex = rightIndex; } if (swapIndex === -1) break; this.heap[index] = this.heap[swapIndex]; index = swapIndex; } this.heap[index] = item; } } ``` 这里的 `Node` 接口定义了一个节点,包含了 x、y、f、g、h 和 parent 属性,分别表示节点的横纵坐标、总代价、从起点到该节点的代价、从该节点到终点的估价和父节点。`aStar` 函数实现了双向 A* 算法,其中使用两个优先队列(二叉堆)来维护两个方向的搜索。`getNeighbors` 函数用于获取一个节点的邻居节点。`manhattanDistance` 函数用于计算两个节点之间的曼哈顿距离。`BinaryHeap` 类是一个二叉堆实现,用于实现优先队列。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值