Files
ParkingRobot/.task8-sweep/ParkrobTrajplanner/CoarsePath/Search/BinaryMinHeap.cs
T

147 lines
4.8 KiB
C#

using System;
using System.Collections.Generic;
using MultiWheelC.TrajectoryPlanning.Utils;
namespace MultiWheelC.TrajectoryPlanning.CoarsePath.Search;
/// <summary>
/// 为 Hybrid A* Open List 提供确定性优先级的二叉最小堆。
/// 排序严格依次比较 F、H、较大的 G 和插入序号;F、H、G 必须为有限且非负的等效米代价。
/// </summary>
/// <typeparam name="T">与一组搜索代价关联的节点或条目类型。</typeparam>
public sealed class BinaryMinHeap<T>
{
private readonly List<HeapEntry> _entries = new List<HeapEntry>();
private long _nextInsertionSequence;
/// <summary>创建空的确定性 Open List 堆。</summary>
public BinaryMinHeap()
{
}
/// <summary>当前堆内尚未出队的条目数量。</summary>
public int Count { get { return _entries.Count; } }
/// <summary>
/// 将一个条目和其搜索排序代价压入堆。
/// 参数:item 为关联条目;f、h、g 均为有限且非负的等效米代价。
/// 失败:任一代价无效、item 为 null(仅引用类型)或插入序号耗尽时抛出异常。
/// </summary>
public void Push(T item, double f, double h, double g)
{
if (ReferenceEquals(item, null)) throw new ArgumentNullException(nameof(item));
ValidateCost(f, nameof(f));
ValidateCost(h, nameof(h));
ValidateCost(g, nameof(g));
if (_nextInsertionSequence == long.MaxValue)
throw new InvalidOperationException("The binary heap insertion sequence has been exhausted.");
var entry = new HeapEntry(item, f, h, g, _nextInsertionSequence);
_nextInsertionSequence++;
_entries.Add(entry);
SiftUp(_entries.Count - 1);
}
/// <summary>
/// 弹出当前排序最优的条目。
/// 返回:按 F、H、较大 G 和插入序号排序后的最小条目;空堆时抛出 <see cref="InvalidOperationException"/>。
/// </summary>
public T Pop()
{
if (_entries.Count == 0) throw new InvalidOperationException("The binary heap is empty.");
HeapEntry result = _entries[0];
int lastIndex = _entries.Count - 1;
if (lastIndex == 0)
{
_entries.RemoveAt(0);
return result.Item;
}
_entries[0] = _entries[lastIndex];
_entries.RemoveAt(lastIndex);
SiftDown(0);
return result.Item;
}
/// <summary>清空尚未出队的条目;后续插入序号继续单调递增以保持整个实例内的确定性。</summary>
public void Clear()
{
_entries.Clear();
}
private void SiftUp(int index)
{
while (index > 0)
{
int parentIndex = (index - 1) / 2;
if (Compare(_entries[index], _entries[parentIndex]) >= 0) return;
Swap(index, parentIndex);
index = parentIndex;
}
}
private void SiftDown(int index)
{
while (true)
{
int leftChildIndex = index * 2 + 1;
if (leftChildIndex >= _entries.Count) return;
int bestChildIndex = leftChildIndex;
int rightChildIndex = leftChildIndex + 1;
if (rightChildIndex < _entries.Count && Compare(_entries[rightChildIndex], _entries[leftChildIndex]) < 0)
bestChildIndex = rightChildIndex;
if (Compare(_entries[bestChildIndex], _entries[index]) >= 0) return;
Swap(index, bestChildIndex);
index = bestChildIndex;
}
}
private void Swap(int firstIndex, int secondIndex)
{
HeapEntry temporary = _entries[firstIndex];
_entries[firstIndex] = _entries[secondIndex];
_entries[secondIndex] = temporary;
}
private static int Compare(HeapEntry left, HeapEntry right)
{
int comparison = left.F.CompareTo(right.F);
if (comparison != 0) return comparison;
comparison = left.H.CompareTo(right.H);
if (comparison != 0) return comparison;
comparison = right.G.CompareTo(left.G);
if (comparison != 0) return comparison;
return left.InsertionSequence.CompareTo(right.InsertionSequence);
}
private static void ValidateCost(double value, string parameterName)
{
if (!NumericGuard.IsFinite(value) || value < 0d)
throw new ArgumentOutOfRangeException(parameterName, "Search costs must be finite and non-negative.");
}
private sealed class HeapEntry
{
public HeapEntry(T item, double f, double h, double g, long insertionSequence)
{
Item = item;
F = f;
H = h;
G = g;
InsertionSequence = insertionSequence;
}
public T Item { get; }
public double F { get; }
public double H { get; }
public double G { get; }
public long InsertionSequence { get; }
}
}