【问题标题】:Comparing two arrays in C# and printing the numbers that are missing比较 C# 中的两个数组并打印缺少的数字
【发布时间】:2016-10-31 17:07:01
【问题描述】:

我是 C# 的新手,无法检查两个字符串之间的差异,数字用空格分隔,并返回第二个字符串中缺少的数字。

   // Initial String with numbers
    string stringA = "503 504 505 506 507 508 503 504 505 506"
    string stringB = "503 504 504 505 506 507 505 508 503 506 505 506 504"

    // I them split them into arrays
    string[] inputArrayA = stringA.Split();
    string[] inputArrayB = stringB.Split();

    // I change them to integers
    int[] numbersA = Array.ConvertAll(inputArrayA, int.Parse);
    int[] numbersB = Array.ConvertAll(inputArrayB, int.Parse);

    // Change the int[] array's to Lists
    var listN = new List<int>(numbersA);
    var listM = new List<int>(numbersB);

    // Compare the lists and put in an array the numbers that are missing from listN
    var missinNumbers = listM.Except(listN).ToList();

    // Print List contents
    missinNumbers.ForEach(Console.WriteLine);

但这现在不起作用。 我尝试使用 HashSet 实现一个单独的方法。但由于某种原因,missinNumbers 始终为空。

public static IEnumerable<int> FindMissing(IEnumerable<int> mainList, IEnumerable<int> listToCompare)
{
    // Compare lists and return values that aren't in mainList but are in listToCompare
    HashSet<int> convertedToHash = new HashSet<int>(mainList);
    convertedToHash.ExceptWith(listToCompare);
    return convertedToHash;
}

我不确定我做错了什么。我浏览了在 C# 中比较两个数组时建议的所有可能解决方案,并尝试了使用 LINQ 和两个 for 循环的不同方法,但我都无法弄清楚。 感谢您的帮助。

编辑: 我的目标是打印与 stringB 相比 stringA 中缺少的数字。所以,如果我们对这两个数组进行排序,我们可以看到缺失的数字是: 504 505 506。

【问题讨论】:

  • 您的初始代码到底有什么问题?
  • Except 的工作方式与应有的完全一样——第二个列表中没有第一个列表中不存在的数字。有些只是出现更多次。你想要的输出是什么?
  • 您希望丢失的数字是什么?
  • Getting the “diff” between two arrays in C#?。此外,“不工作”是相当模糊的。提供错误、您预期会发生但没有发生的事情,和/或发生的事情您没有预料到。
  • 提出问题时,您拥有的代码是正确的。没有丢失的数字,两组都包含数字 503、504、505、506、507 和 508。它们仅在数量和顺序上有所不同,但这不是您提出的问题,也不是您的代码检查的内容。如果您提供了您想要的输出,将会很有帮助。

标签: c# linq


【解决方案1】:

尝试使用字典,其中键是字符串(“503”等),值是该元素的出现次数(因此,如果 503 重复 3 次,则键值对将是 )。

您可以为 2 个列表创建 2 个字典,然后遍历其中一个并在第二个中查找元素,减去出现次数以找出该键剩余的元素数。

在你的情况下,字典看起来像

A__________B__________结果

_______

_______504, 列表 B 中又出现了 2 次​​p>

...等

这是你在 C# 中的实现方式

string stringA = "503 504 505 506 507 508 503 504 505 506";
string stringB = "503 504 504 505 506 507 505 508 503 506 505 506 504";

// linq to make dictionary from A
var mapA = stringA.Split().GroupBy(a => a)
                          .Select(a => new {a.Key,Count = a.Count()})
                          .ToDictionary(a => a.Key, a => a.Count);

// linq to make dictionary from B
var mapB =  stringB.Split().GroupBy(b => b)
                           .Select(b => new { b.Key, Count = b.Count() })
                           .ToDictionary(b => b.Key, b => b.Count);

// Elements that are in B but not in A 
var BminusA = mapB.Select(b => { int aCount; 
                                 return new {b.Key, Value = b.Value - (mapA.TryGetValue(b.Key, out aCount) ? aCount: 0)};})
                  .Where(difference => difference.Value > 0);

上面还将为您提供缺失数字的计数({504、505、506} 各为 1)。

【讨论】:

    【解决方案2】:

    抱歉,我之前的回答假设代码存在“错误”,我没有更彻底地查看它。如前所述,Except 在执行检查时不考虑数量,仅当元素存在于另一个列表中时根本。有一种 LINQ 方法可以做您想做的事,但手动操作更容易阅读(更不用说可能更快)。

    for (int i = 0; i < listN.Count; i++)
    {
        if (listM.Remove(listN[i]))
        {
            listN.RemoveAt(i--);
        }
    }
    
    for (int i = 0; i < listM.Count; i++)
    {
        if (listN.Remove(listM[i]))
        {
            listM.RemoveAt(i--);
        }
    }
    

    在此之后,listN 将由 listM 中不存在的所有特定元素填充,反之亦然。

    如果您想将两个差异列表加入到一个集合中,您只需执行以下操作:

    var differences = new List<int>(listM);
    differences.AddRange(listN);
    

    【讨论】:

    • 我喜欢你如何删除一个项目并同时退回索引。我花了一秒钟才意识到你在做什么,但是一旦点击它就完全有意义了。我将不得不借用这种模式。我一直保留一个单独的删除列表,并向后工作以防止索引发生变化。
    • 我看到它有效,但我不确定我理解的具体程度。我正在添加一堆印刷品来弄清楚,但也许你们中的一个人可以更好地解释它。最初我尝试了两个 for 循环,但第二个在第一个里面......
    • .Remove 取出其参数的第一个实例,如果该参数存在则返回 true。当您在 listN(stringA) 中遇到它时,通过从 listM(stringB) 中删除每个字符串,您只剩下丢失的数字。你真的只需要第一个循环就可以得到你想要的答案。第二个循环为您提供相反的结果(在 listN 中找到但在 listM 中缺失)
    • 好的,我明白了。这个怎么样 - listN.RemoveAt(i--);是否将 for 循环重置为始终从索引 0 循环?我认为 -1 索引在可迭代对象上倒退。至少在 Python 中是这样的,所以我对此有点困惑。
    • @burizz 减量是为了抵消调用RemoveAt 带来的索引偏移。没有它,您最终会在删除元素之后跳过该元素。
    【解决方案3】:

    这个怎么样?我们得到列表 b 中每个不同字符串的计数,如果该计数与列表 a 中的计数之间的差异为正,我们将字符串的多个副本添加到缺失列表中。

            string[] stringA = "503 504 505 506 507 508 503 504 505 506".Split();
            string[] stringB = "503 504 504 505 506 507 505 508 503 506 505 506 504".Split();
    
            List<int> missingNumbers = new List<int>();
    
            foreach (string num in stringB.Distinct())
            {
                int difference = stringB.Where(x => x == num).Count() - stringA.Where(x => x == num).Count();
                for (int i = difference; i > 0; i--)
                {
                    missingNumbers.Add(int.Parse(num));
                }
            }
    

    【讨论】:

    • 感谢您的帮助。我将在 LINQ 上准备更多内容,因为我认为我不太了解这部分:int difference = stringB.Where(x => x == num).Count() - stringA.Where(x => x = = num).Count();
    • stringB.Where 将 stringB 中的每个项目放入 x,检查 x == num,如果是,则将 x 添加到返回集合中。因此,如果 stringB 包含 1 2 2 3 4 4 和 num = 4,则 stringB.Where(x => x == num) 将返回一个包含 4 的 2 个副本的集合。
    【解决方案4】:

    提供一些反馈,以防它对某人有用。这是我的最终解决方案。它解决了我练习中的所有测试用例,除了一个。每个数组中有 1 000 000 个整数。如果我们有两个字符串,每个字符串由一个空格分隔,每个字符串都有超过一百万个数字,那么这样做的有效方法是什么?

    static void Main(String[] args)
    {
        // Build Array N from input numbers
        string[] inputLineN = Console.ReadLine().Split();
    
        // Build Array M from second input numbers
        string[] inputLineM = Console.ReadLine().Split();
    
        // Convert to int[] array
        int[] numbersN = Array.ConvertAll(inputLineN, int.Parse);
        int[] numbersM = Array.ConvertAll(inputLineM, int.Parse);
    
        // Convert them to Lists
        var listN = new List<int>(numbersN);
        var listM = new List<int>(numbersM);
    
        for (int index = 0; index < listN.Count; index++)
        {
            // Remove first occurance of item from listM if it exists in listN
            if (listM.Remove(listN[index]))
            {
                // Remove listN[index] and decrement the index to -1 so that the next iteration
                // starts from 0 again otherwise we will start at 1 and skip an item
                listN.RemoveAt(index--);
            }
        }
    
        // Sort missing items and join them back in a single string
        listM.Sort();
        int[] removeDuplicates = listM.Distinct().ToArray();
        string missingNumbers = string.Join(" ", removeDuplicates);
    
        Console.WriteLine(missingNumbers);
    }
    

    【讨论】:

    • 您当前的解决方案有多个问题,随着数据量的增加,时间复杂度和空间复杂度会变差,我可以看到几个问题:1.将字符串转换为整数(不需要),您没有进行任何整数特定操作,只是比较元素,因为字符串也可以使用 2. 从原始列表中删除将更改任何其他操作的原始数据,您必须保留单独的副本。跨度>
    • 查看我发布的解决方案,这将是实现您的用例的最简单方法,而且速度也很快,虽然时间复杂度为 O(N^2),但我们可以做得更好通过将 List 转换为 Hashset,在 O(logN) 中进行搜索,整体复杂度将变为 O(NLogN)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-08-12
    • 1970-01-01
    • 2019-08-03
    • 1970-01-01
    相关资源
    最近更新 更多