【问题标题】:Finding 'Next Available' Key in a dictionary or related collection在字典或相关集合中查找“下一个可用”键
【发布时间】:2012-06-04 17:15:03
【问题描述】:

我想编写一个 linq 扩展(或自定义字典、排序列表列表或任何最佳解决方案),它允许我向集合添加一个值,其键为“下一个可用”。

例如:

int CreatedKey = IncrementDictionary.AddNext(myCustomer);

如果当前存在的key如下:

1
2
8
4
3

然后它将 myCustomer 添加到具有 5 键的字典中,并返回该键。

你怎么看?

【问题讨论】:

  • 我认为目前还不清楚问题是什么。我也不认为这会成为一个特别有用的数据结构,因为对象引用或哈希码/相等方法的组合足以识别对象。
  • 嗨,威廉,您的问题似乎属于 codereview.stackexchange.com。您会在该网站上找到更好的响应率,因为它完全适用于此类问题。
  • @dtb 但 OP 要求人们评估(或审查)他的想法,这里没有问题,他只是要求审查
  • @DoctaJonez:哪个想法?这些问题在我看来就像 OP 要求的代码满足他的要求。
  • @dtb 它读作“这就是我正在做的事情,你怎么看?”。对我来说听起来像是一个评论问题

标签: c# .net linq c#-4.0 collections


【解决方案1】:

这是我的解决方案,比第一个更短,但您可以从中获得一些想法。

        Dictionary<int, string> x = new Dictionary<int, string>();
        x.Add(1, "a");
        x.Add(2, "a");
        x.Add(3, "a");
        x.Add(4, "a");
        x.Add(8, "a");
        Console.WriteLine((x.Keys.Where(k => !x.Keys.Contains(k + 1)).Min() + 1).ToString());

【讨论】:

  • 你熟悉Schlemiel the Painter吗?
  • @dtb 是的 ;) 感谢您的帮助 :) 虽然我的回答真的是 uhmm :)
【解决方案2】:
public static int AddNext(this Dictionary<int, string> dict)
{
    int min = dict.Keys.Min();
    int max = dict.Keys.Max();

    return Enumerable.Range(min, max-min).Except(dict.Keys).First();   
}

把它当做

int result = new Dictionary<int, string>(){ {1, "a"}, {2, "a"}, 
                                            {8, "a"}, {4, "a"}, 
                                                      {3, "a"}}.AddNext();

这里result 是 5。

【讨论】:

  • Wow +1 for you NikHil :) 你用 1 行回答了 :)
  • Schlemiel the Painter's algorithm 的另一个很好的例子
  • @dtb:我知道,但这在这里如何应用?你能解释一下吗
  • @dtb 你为什么老是在这里谈论那个 schlemiel 的人 :).. 他是你的大学教授吗?
  • 对于您插入字典的每个键,在查找下一个键时您必须做更多的工作。这是非常低效的。应该使用适当的数据结构来解决问题,而不是蛮力。
【解决方案3】:

您是否正在寻找这样的东西(它显然只能包含 1000 个元素)?可能有许多其他解决方案,但很难说出您到底想做什么。无论如何,这可能是一个起点。

public class IncrementDictionary : Dictionary<int, object>
{
    private bool[] usedKeys = new bool[1000];

    public new void Add(int key, object value)
    {
        base.Add(key, value);
        usedKeys[key] = true;
    }

    public new void Clear()
    {
        base.Clear();
        usedKeys = new bool[1000];
    }

    public new object this[int key] 
    {
        get
        {
            return base[key];
        }
        set
        {
            base[key] = value;
            usedKeys[key] = true;
        }
    }

    public new bool Remove(int key)
    {
        usedKeys[key] = false;
        return base.Remove(key);
    }

    public int AddNext(object anObj)
    {
        int newKey = -1;
        for (int i = 1; i < 1000; i++)
            if (!usedKeys[i])
            {
                newKey = i;
                break;
            }
        if (newKey > 0)
            this.Add(newKey, anObj);
        return newKey;
    }
}

【讨论】:

    【解决方案4】:

    您可以使用SortedList 和扩展方法添加到下一个自动检索的密钥。

    假设数据结构是任何对象,带有数字键,

    以下是 SortedList 的 ExtensionMethod

    public static class SortedListExtensions
    {
        ///Add item to sortedList (numeric key) to next available key item, and return key
        public static int AddNext<T>(this SortedList<int, T> sortedList, T item)
        {
            int key = 1; // Make it 0 to start from Zero based index
            int count = sortedList.Count;
    
            int counter=0;
            do
            {
                if (count == 0) break;
                int nextKeyInList = sortedList.Keys[counter++];
    
                if (key != nextKeyInList) break;
    
                key = nextKeyInList +1;
    
                if (count == 1 || counter == count  ) break;
    
    
                if (key != sortedList.Keys[counter])
                    break;
    
            } while (true);
    
            sortedList.Add(key, item);
            return key;
        }
    
    }
    

    可以像下面这样使用

      SortedList<int, string> x = new SortedList<int, string>();
    
            x.Add(4, "BCD");
            x.Add(6, "BCD");
    
            x.AddNext("a");
            x.AddNext("b");
            x.AddNext("c");
            x.AddNext("d");
            x.AddNext("e");
    
            foreach (var item in x)
                Console.WriteLine(item.Key + " " + item.Value);
    

    输出是

            1 a
            2 b
            3 c
            4 BCD
            5 d
            6 BCD
            7 e
    

    您可以使用字典或任何其他数据结构。在这种情况下,将需要双循环。在 SortedList 的情况下,搜索键时会保存一个循环。 SortedList.Add 函数使用 BinarySearch 算法在内部使用此循环。

    二分查找比循环所有元素更快(对于更大尺寸的数据)。

    【讨论】:

      【解决方案5】:

      嗯...这是一个老问题,但我想回答一下。

      Nikhil Agrawal 的代码可能会有所改进,考虑到如果您有一个空集合,则必须指定最小值是多少。于是代码变成了:

       public static int FirstFree(Dictionary<int, Guid> dict, int minumum)
          {
              int min = dict.Count == 0
                  ? minumum                       //use passed minimum if needed
                  : dict.Keys.Min();              //use real minimum
              int max = dict.Count > 1
                  ? dict.Keys.Max() + 2           //two steps away from maximum, avoids exceptions
                  : min + 2;                      //emulate data presence
              return Enumerable.Range(min, max).Except(dict.Keys).First();
          }
      

      但是,如果您在定义的范围内工作,您还应该知道是否有空间来存储值。可以这样:

      public static int? FirstFree(Dictionary<int, Guid> dict, int min, int max)
          {
              if (max <= min)
                  throw new Exception($"Specified range is invalid (must be max > min)");
              if (max - min + 1 == dict.Count)
                  //no space left
                  return null;
              return Enumerable.Range(min, max).Except(dict.Keys).First();
          }
      

      如果你不喜欢 nullable-int,你可以检查 result 是否是允许的最小值。 显然,如果你指定 min=int.MinValue 是行不通的,我知道,但在这种情况下,问题是这个方法创建的巨大集合!

      public static int FirstFree(Dictionary<int, Guid> dict, int min, int max)
          {
              if (max <= min)
                  throw new Exception($"Specified range is invalid (must be max > min)");
              if (max - min + 1 == dict.Count)
                  //no space left
                  return int.MinValue;
              return Enumerable.Range(min, max).Except(dict.Keys).First();
          }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-08-26
        • 2011-03-11
        • 1970-01-01
        • 2016-02-24
        相关资源
        最近更新 更多