如何检查5个整数中是否有3个相同?

3

假设我有5个整数。

int a = 1;
int b = 2;
int c = 5;
int d = 1;
int f = 1;

我想检查这5个整数中是否有3个相同。

我尝试了一些方法,但代码很长(500+行),我认为这不是一个好方法。


你需要知道哪些是相同的,还是只想知道有哪些是相同的? - sakura-bloom
我只需要知道其中是否有一些。 - Efekan
5个回答

8

首先将它们全部放在一个集合中,而不是有单独的变量:

var numbers = new[]{a,b,c,d,f};

然后将它们分组,找到每个组的数量,并查看是否符合您的标准。

var isLargeGroup = numbers.GroupBy(n => n, (key, group) => group.Count() )
    .Any(count => count >= 3);

无法编译。方法...的类型参数无法从使用中推断出来。请明确指定类型参数。 - Selman Genç
1
@Selman22 对的。我总是假设第二个参数的委托只应该有一个IGrouping参数。这会让世界变得更加容易。可惜它不是这样的。不过这很容易修复。 - Servy
@キャタ 我在帖子本身中已经这样做了。它的阅读方式也几乎与其相同。基于每个数字将集合中的所有项分组,然后将每个组映射到其计数(这是GroupBy的第二个参数所做的)。最后,如果其中任何一个计数大于或等于三,则为true,否则为false。 - Servy
1
@DavidHaney 鉴于“group”是一个“ICollection”,因此计算组大小将成为O(1)操作,因此在“Count”中的优化将会生效,但即使这不是情况,两种解决方案仍然都是O(n)。毕竟,O(2n)等于O(n/2)。至于垃圾回收,实际上它将是微不足道的。分配一个连续单个GC都无法存活的对象几乎没有任何成本。通过集合保持对象存活才有成本。并不是在这里创建了那么多*对象。 - Servy
1
@DavidHaney 从技术上讲,你不能保证它,但是虽然有相当数量的对象被创建,但是没有很多对象同时存在。一些对象可能会在GC期间存活,但是大量对象不会(由于LINQ的流式特性),因此GC发生在运行时的性能影响(这本身就不太可能)不会非常大。 - Servy
显示剩余4条评论

1

其他两种解决方案对于大数据集(例如10000个数字)可能会很昂贵,需要完整枚举并创建许多将被垃圾回收的对象。尝试使用以下方法,它可以在完整枚举之前停止执行:

private bool AreNumbersTheSame(int[] numbers, int duplicateCount)
{
    var numberCounts = new Dictionary<int, int>();
    for (int i = 0; i < numbers.Length; i++)
    {
        var current = numbers[i];
        if (!numberCounts.ContainsKey(current))
        {
            numberCounts[current] = 1;
            continue;
        }

        if (numberCounts[current] == duplicateCount - 1)
        {
            return true;
        }

        numberCounts[current]++;
    }
    return false;
}

像这样调用:

var result = AreNumbersTheSame(new[] { a, b, c, d, f }, 3);

1
有一个大小为5的集合,我认为完全迭代它而不进行短路是可行的。 - Servy
4
但问题特别地指出它的大小为5。 - Servy
1
过早的优化是万恶之源。重要的是要认识到什么值得优化,什么不值得优化。你需要花时间去认识到哪些优化对开发者的时间和代码复杂度有足够大的影响,而性能差异在这里几乎为零,因为数据集很小。代码清晰度和明显的正确性比这些解决方案之间微秒级别的差异更有价值。 - Servy
1
我不知怎么错过了你的方法,它和我的非常相似。所以我给你点赞。 - Tim Schmelter
@DavidHaney你说过,你认为这个方法值得使用的原因是因为它的性能,“效率现在已经成为一门死艺术,被LINQ的昂贵误用所杀死”。声称这里的性能提升是相关的,这显然是不正确的。两种解决方案都可以在极短的时间内轻松完成,使优化变得无意义。如果您发现这个解决方案更易读,则这是一个完全不同的观点,更加主观。您似乎也夸大了与LINQ相关的成本形象。 - Servy
显示剩余5条评论

0

值得一提的是,这里有另一个选项,如果数据序列很大,则比GroupBy更有效,虽然不太优雅。我认为,在扩展方法中隐藏了可读性并不是那么重要。

public static bool HasDuplicateCount<T>(this IEnumerable<T> seq, int maxCount)
{
    Dictionary<T, int> counts = new Dictionary<T, int>();
    foreach (T t in seq)
    {
        int count;
        if (counts.TryGetValue(t, out count))
            ++count;
        else
            count = 1;
        if (count == maxCount)
            return true;
        counts[t] = count;
    }
    return false;
}

您可以这样使用它:
bool threeDups = new[] { a, b, c, d, f }.HasDuplicateCount(3);

0

如果你想查看超过一半的元素是否相同(例如5个元素中有3个相同),那么这也可以使用多数投票算法来解决:

public static bool HasMajorityElement(int[] a)
{
    int candidate = 0;
    int count = 0;
    foreach (int i in a)
    {
        if (count == 0)
        { 
            candidate = i;
            count = 1;
        }
        else 
        {
            count += candidate == i ? 1 : -1;
        }
    }
    count = 0;
    foreach (int i in a)
    {
        if (i == candidate) ++count;
    }
    return count > a.Length / 2;
}

在ideone.com上测试它

或者针对您的特定情况:

public static bool ThreeOutOfFive(int a, int b, int c, int d, int e)
{
    return ((a==b?1:0)+(a==c?1:0)+(a==d?1:0)+(a==e?1:0) > 1)
        || ((b==c?1:0)+(b==d?1:0)+(b==e?1:0) > 1)
        || ((c==d?1:0)+(c==e?1:0) > 1);
}

有趣。但解决的是不同的问题。OP只想知道数组中是否存在一个重复出现至少三次的副本。因此,如果数组包含6个数字而不是三个副本,则您的方法将返回错误的结果(false而不是true)。 - Tim Schmelter
真的,但是OP特别要求5个元素中的3个。我已经澄清了你必须寻找超过一半的元素相同。 - mattnewport

0
我会这样做:-
  1. 将所有数字添加到列表中。

  2. 创建一个字典,以数字为KEY,以其出现次数为VALUE。

  3. 在迭代列表时,需要检查数字是否不在字典中,然后使用数字作为KEY并将1作为其VALUE添加。

  4. 如果数字重复出现,则只需将字典的VALUE增加1。

  5. 一旦它等于3,就跳出循环。

            int a = 1;
            int b = 2;
            int c = 5;
            int d = 1;
            int f = 1;
            var listOfNumbers = new List<int> {a, b, c, d, f};
    
            var dict = new Dictionary<int, int>();
            foreach (var number in listOfNumbers)
            {
                if (dict.ContainsKey(number))
                {
                    dict[number] = dict[number] + 1; //如果键重复 => 将值增加1
                    if (dict[number] == 3)
                        break; //找到数字
                }
                else
                    dict.Add(number, 1); //对于键的第一次出现 => 将值初始化为1
            }
    

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接