【问题标题】:Getting the lowest value of a List<Struct.int> [duplicate]获取 List<Struct.int> 的最小值 [重复]
【发布时间】:2013-02-04 17:30:10
【问题描述】:

我有一个struct,其中包含一些intbool 成员,我希望从列表中获得最低值(实际上是在制作基于A* 搜索的路径查找器)。

基本上,我的对象如下所示:

    public struct Tile
    {
        public int id;
        public int x;
        public int y;
        public int cost;
        public bool walkable;
        public int distanceLeft;
        public int parentid;
    }

我想得到距离Left最小的项目。列表声明如下:

        List<Structs.Tile> openList = new List<Structs.Tile>();

并以这种方式分配值:

        while (pathFound == null)
        {
            foreach (Structs.Tile tile in map)
            {
                foreach (Structs.Tile tile1 in getSurroundingTiles(Current))
                {
                    if (tile1.x == tile.x && tile1.y == tile.y)
                    {
                        Structs.Tile curTile = tile1;
                        curTile.parentid = Current.id;
                        curTile.distanceLeft = (Math.Abs(tile.x - goalx) + Math.Abs(tile.y - goaly));
                        if (curTile.distanceLeft == 0)
                        {
                            pathFound = true;
                        }
                        openList.Add(curTile);
                    }
                }
            }
            foreach (Structs.Tile tile in openList)
            {

            }
        }

如果我不得不猜测,我会说这要么非常困难,要么比我说的要复杂得多,或者非常简单,我只是感到困惑。

我确实考虑过滚动列表并将每个项目与其较低的对应项进行比较,但考虑到我们所处的时代,这似乎不合理,似乎会有更简单的方法。我不关心列表的顺序,因为我为每个项目分配了一个索引,我可以从中调用它。

提前致谢!

【问题讨论】:

  • 你应该看看 LINQ。
  • 为什么要使用结构体?它确实消耗了大约 28 个字节,这比 4/8 字节的指针大小要大得多。每个确实使用您的结构的方法都需要携带 28 个字节。如果是类,则比较会快得多。
  • 这可能是重复的 mbeckish,但我对 Linq 的了解还不够,无法知道这是一个 Linq 函数,所以对此我深表歉意。不过,我仍然想保留此线程一段时间,因为它确实包含我认为可能对某些人有用的响应(其他可能不知道 Linq 或此处引用的关联的人)。
  • Alois Kraus - 我并不完全了解使用类的优缺点,尽管我认为声明类的新实例比新实例更重要一个结构?如果没有,请告诉我,我会记住这一点以供将来参考。非常感谢您的建议,非常感谢!
  • 有一篇小文章解释了这个东西:msdn.microsoft.com/en-us/magazine/cc301569.aspx

标签: c# algorithm search a-star


【解决方案1】:

没有一种 LINQ 扩展方法可以返回具有最小值的对象,但您可以自己编写一个。以下类对任何非空枚举执行您想要的操作:

public static class MyExtensions
{
    public static TSource MinOf<TSource>(
        this IEnumerable<TSource> source,
        Func<TSource, int> selector)
    {
        // Get the enumerator.
        var enumerator = source.GetEnumerator();
        if (!enumerator.MoveNext())
            throw new InvalidOperationException("The source sequence is empty.");

        // Take the first value as a minimum.
        int minimum = selector(enumerator.Current);
        TSource current = enumerator.Current;

        // Go through all other values...
        while (enumerator.MoveNext())
        {
            int value = selector(enumerator.Current);
            if (value < minimum)
            {
                // A new minimum was found. Store it.
                minimum = value;
                current = enumerator.Current;
            }
        }

        // Return the minimum value.
        return current;
    }
}

把它放在你项目中的一个文件中,这样调用它:

openList.MinOf(tile => tile.distanceLeft);

这比排序整个序列(使用OrderBy)然后取第一个值(使用First)更有效。

【讨论】:

  • 谢谢,这个方法会返回对象本身,还是只返回存储在对象中的 int?我假设通过查看它,对象?
  • 不,它返回了 int。我用返回对象的东西更新了答案。
  • 谢谢,这看起来很复杂,但我需要一段时间才能理解。看来这是“正确”的继续方式,非常感谢老兄!
  • @JimMischel Min 返回一个整数。他想要那个对象。
【解决方案2】:

要获得Tile 和最低distanceLeft,试试这个:

Tile tile = openList.OrderByAscending(t => t.distanceLeft).First();

编辑:

这样做将返回一个IEnumerable&lt;Tile&gt;,它将按升序排序 - openList 本身不会被修改。

【讨论】:

  • 这会修改列表的顺序吗?如果是这样,我认为Linq选项可能是有利的,我知道我提到我不介意修改订单,但我宁愿不要为了资源而不要,这样做似乎有点浪费这。不过,我非常感谢您的回复。谢谢!
  • @XtrmJosh 查看我的编辑。
  • 不修改输入列表。相反,它会创建一些内存结构,对整​​个列表进行排序,然后除第一个元素外,所有列表都被丢弃。
  • 它似乎比这里的替代方法更占用内存,但正如我所说,我感谢您的输入,谢谢!
【解决方案3】:

或者,如果由于某种原因您不能使用 LINQ:

int lowest = 0;
for (int i = 1; i < openList.Count; ++i)
{
    if (openList[i].distanceLeft < openList[lowest].distanceLeft)
    {
        lowest = i;
    }
}
// lowest contains the index of the item with the lowest 'distanceLeft' value.
// You can return the item itself by indexing into the list.
var lowestItem = openList[lowest];

【讨论】:

  • 这里返回最小值,而不是最小值的对象
  • 只是为了更清楚地说明为什么在这种情况下不能使用 Min():Min() 返回对象,但比较对象本身(不能指定选择器)。 Min(lambda) 返回 lambda 选择的数据类型,在本例中为 int。
  • 其实是返回最小值的item的索引。现在它返回项目本身。
  • @gunr2171:是的,看到了。我的错。奇怪的是界面是这样写的。
【解决方案4】:

其他答案解释了如何使用 LINQ 执行此操作 - 但是,它们都是 O(n) 或更慢。使用其中一种方法会显着减慢您的寻路算法。

相反,您应该使用正确的数据结构。您应该将节点存储在 Priority Queue 中,而不是列表,以便在O(log n) 中获得(并删除)最小值。

有关 .Net 中的优先级队列列表,请参阅 this question

【讨论】:

  • 这取决于您通常如何使用该集合。如果你想对它进行索引,优先级队列将是一个相当糟糕的数据结构选择。
  • @Jim:他在问题中说他想将它用于A*
  • 啊……错过了。那么优先队列绝对是要走的路。
猜你喜欢
  • 1970-01-01
  • 2022-12-17
  • 1970-01-01
  • 2017-11-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多