【问题标题】:What is the micro-optimal and most elegant way of returning List<string> of elements that occurs only once in other List<string>返回 List<string> 在其他 List<string> 中只出现一次的元素的微优化和最优雅的方式是什么
【发布时间】:2015-12-02 05:57:12
【问题描述】:

这是我今天的面试问题:

“给定一个字符串列表,只返回一个唯一字符串的列表”

我很好奇 Skeet 认证的答案是什么。

我自己的回答是

public static List<string> sans_repeats ( List<string> input ) 
{
    Dictoinary<string,int> counter = new Dictionary<string,int>();
    List<string> output = new List<string>();
    foreach ( string S in input ) 
    {
       if ( counter.HasKey(S) ) counter[S] = 1;
       else ++count[S];
    }     
    foreach ( KeyValuePair<string,int> entry in counter ) 
       if ( entry.Value == 1 ) output.Add(entry.Key);
    return output;
}

采访说

“嗯,这是一种方法……”

用一种听起来居高临下的声音,好像我做错了什么。

  • 这有什么逻辑错误吗?
  • 有没有办法实现内存和处理效率更高的解决方案?
  • 面试官是否可能正在寻找类似 LINQ 的解决方案?为什么他似乎不喜欢我的?
  • 有没有办法让它更紧凑?

【问题讨论】:

  • 如果添加到列表中该字符串的字符串索引,它将是唯一的,您也可以使用 new GUID()+ yourstring
  • 你能否详细说明“List中唯一的字符串”的含义,我自己很好奇。
  • @WaqarAhmed 示例:{ "dog", "cat", "elephant", "dog", "dog" } -> { "cat", "elephant" }
  • 哦,你的意思是在列表中只出现一次的元素,对:P
  • 只是我的看法: 作为一名面试官,我可能会期待 LINQ 解决方案对性能的评价。它可能效率较低(但不是那么多,它也是线性的),但性能的轻微改进并不是“重新发明轮子”和每次使用集合时编写自定义函数的原因。

标签: c# .net algorithm


【解决方案1】:

根据更新的问题,这是一种使用 LINQ 的方法:

var src = new List<string>() { "dog", "cat", "elephant", "dog", "dog" } ;
src.GroupBy(x => x).Where(y => y.Count() == 1).ToList();

Demo

【讨论】:

  • 我的问题可能措辞有误。我的意思是返回的列表只包含在原始列表中出现一次的字符串。
  • 啊,太棒了!但是,考虑到 LINQ 子句的未分类实现,这是否同样有效?
  • 时间上我想你可以确定它。
  • 如果底层结构不是ICollection(你不知道),调用Count() 可能会非常昂贵。所以我更喜欢!y.Skip(1).Any() 见demo。
  • 在ToList调用之前不应该有.Select(g =&gt; g.Key)之类的东西。
【解决方案2】:

最简单的方法,使用内置函数:

List<string> distinctList = input.Distinct();

https://msdn.microsoft.com/library/bb920306(v=vs.90).aspx

或者,检查当前项目是否已经存在于输出列表中,如果存在则继续:

List<string> output = new List<string>();
foreach (string S in input) 
{
    if (output.Contains(S)) continue;       
    output.Add(S);
}

编辑

根据您的评论,您只希望项目只出现一次:

Dictionary<string, int> stringCount = new Dictionary<string, int>();
foreach (string S in input) 
{
    if (stringCount.ContainsKey(S))
    {
        stringCount[S]++;
    }
    else
    {
        stringCount.Add(S, 1);
    }       
}

var output = stringCount.Where(x => x.Value == 1).ToList();

【讨论】:

  • input.Distinct() 是IEnumerable&lt;string&gt;
  • 我可能没有正确表达我的问题。我说的是一个过程,如果元素在第一个字符串中恰好出现一次,它只会将元素添加到第二个字符串中。
  • @Candide 所以之后使用.ToList()。
  • @user5572578,是的。不过,最后有点“Linq-y”。
【解决方案3】:

既然是面试题,你需要考虑面试官想从这个问题中得到什么。

  1. 如果他想知道你的 LINQ 技能,你应该给出 LINQ 代码。 (和其他答案一样)

  2. 如果他想测试你的算法,你需要编写纯C风格的程序,而不是使用内置库。在这种情况下,您的代码可以得到改进,因为它消耗的内存比预期的要多。可以优化程序中的变量output。输入参数已经包含所有字符串,无需再次复制。另外counter 是一个过度杀戮,你只需要一个int[] 来存储元素的数量。

  3. 有时面试官想测试被面试者解决问题的方式。当你被问到这个问题时,你可以问面试官:

    (1)返回的列表是否应该与输入列表相同的引用,或者创建一个新的列表是可以接受的?

    (2) 一个元素最多出现多少次?如有必要,请使用byte[] 而不是int[] 来存储计数。

【讨论】:

  • 如何使用byte[] 来存储计数?
  • @CallumLinington 如果有人告诉我所有元素最多出现 100 次。这只是一个例子。
  • 我知道,我只是想了解为什么byte[] 很重要。为什么是 100 次?
  • @CallumLinington 这并不重要。但是如果你在面试中提到这个小小的改进,面试官可能会认为你在开始编码之前总是会仔细和全面地考虑。
【解决方案4】:

Linq 是一个不错的选择,但如果您想要一个集合,为什么使用Dictionary 而HashSet 可能更合适。

var strings = new[] { "a", "a", "b", "c" };
var hash = new HashSet<string>(strings);
hash.Dump(); // a, b, c

【讨论】:

  • 查看 OP 对问题的评论:{ "dog", "cat", "elephant", "dog", "dog" } -&gt; { "cat", "elephant" }。
【解决方案5】:

这是另一种有效的方法,使用 HashSet.Add 方法实际添加项目并仅当项目不在集合中时才返回 true 的事实。代码是不言自明的。

var uniqueSet = new HashSet<string>();
var duplicateSet = new HashSet<string>(input.Where(item => !uniqueSet.Add(item)));
var output = input.Where(item => !duplicateSet.Contains(item)).ToList();

【讨论】:

    猜你喜欢
    • 2020-10-21
    • 2020-10-09
    • 1970-01-01
    • 2018-11-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-03-10
    相关资源
    最近更新 更多