查找奇数个元素中出现的值

时间:2018-01-09 02:35:48

标签: c# c#-4.0

给出了由N个整数组成的非空零索引数组A.该数组包含奇数个元素,并且该数组的每个元素都可以与另一个具有相同值的元素配对,除了一个未配对的元素。

例如,在数组A中:

A[0] = 9  A[1] = 3  A[2] = 9
A[3] = 3  A[4] = 9  A[5] = 7
A[6] = 9

索引0和2处的元素值为9, 索引1和3处的元素值为3, 索引4和6处的元素值为9, 索引5处的元素值为7且未配对。 写一个函数:

class Solution { public int solution(int[] A); }

给定一个由满足上述条件的N个整数组成的数组A,返回未配对元素的值。

这是我的尝试。它通过了测试用例,但任何改进它的反馈都会很棒:

using System;
using System.Collections.Generic;

class Solution {
public int solution(int[] A) {
    // write your code in C# 6.0 with .NET 4.5 (Mono)
    if (A == null || A.Length <= 0) return 0;

    Dictionary<int, int> dictionary =
        new Dictionary<int, int>();

    foreach(int number in A) {
    // using the index of count same way you'd use a key in a dictionary
    if (dictionary.ContainsKey(number))
        dictionary[number] ++;

    else dictionary.Add(number, 1);
    }

    foreach(var item in dictionary)
    {
        if(item.Value == 0) return item.Key;

        else if(item.Value %2 != 0) return item.Key;
    }   

    return -1;
}
}

2 个答案:

答案 0 :(得分:1)

有很多方法可以做到这一点。为了好玩,我尝试了一些。它可能会帮助您看到一些不同的方法。

使用LINQ

这种事情恰恰是为LINQ而构建的。除非存在性能问题,否则这可能是大多数专业开发人员所做的事情,因为它很直观(如果您了解LINQ)。以下是一行解决方案:

//using System.Linq;

public static int UseLinq(int[] input)
{
    return input
        .GroupBy( n => n )
        .Where( g => g.Count() %2 == 1)
        .Select( g => g.Key )
        .Single();
}

您采用的方式

使用字典跟踪计数,类似于您的方法。

public static int WithDictionary(int[] input)
{
    var counts = new Dictionary<int, int>();

    foreach (int n in input)
    {
        if (!counts.ContainsKey(n))
        {
            counts[n]=1;
        }
        else
        {
            counts[n]++;
        }
    }
    foreach (var d in counts)
    {
        if (d.Value % 2 == 1) return d.Key;
    }
    return -1;
}

您的方式,但使用LINQ生成字典

public static int WithDictionaryAndLinq(int[] input)
{
    var counts = input.GroupBy( n => n )
                      .ToDictionary( g => g.Key, g=> g.Count() );

    foreach (var d in counts)
    {
        if (d.Value % 2 == 1) return d.Key;
    }
    return -1;
}

使用对跟踪

有点像你做的那样,但不是记住计数,只需跟踪尚未配对的项目。这似乎是最短的解决方案。

public static int WithPairTracking(int[] input)
{
    var odd = new List<int>();
    foreach (var n in input)
    {
        if (odd.Contains(n)) odd.Remove(n); 
        else odd.Add(n);
    }
    return odd[0];
}

先排序数组

如果运行时提供快速Sort()算法(如.NET那样),这可能是性能最佳的解决方案,因为您只需要一次遍历数组。

public static int UseSort(int[] input)
{
    Array.Sort(input);
    if (input[0] != input[1]) return input[0];
    for ( int i = 1; i < input.GetUpperBound(0); i++ )
    {
        if (input[i] == input[i+1]) continue;
        if (input[i] == input[i-1]) continue;
        return input[i];
    }
    return -1;
}

<强>并行

这是一个多线程解决方案。由于false sharing的问题,并未真正推荐;可能没有比单线程更好的表现。

public static int WithParallelLoop(int[] input)
{
    int result = -1;

    Parallel.ForEach
    ( 
        input, 
        (element,state) =>
        {
            bool found = false;
            foreach (var n in input)
            {
                if (n == element) found = !found;
            }
            if (found) 
            {
                Interlocked.Exchange(ref result, element);
                state.Stop();
            }
        }
    );
    return result;
}

列表删除

此解决方案将数组放入列表中并成对移除它们,直到只剩下一个元素。

public static int ByRemovingFromList(int[] input)
{
    var list = input.ToList();
    while (list.Count != 0)
    {
        var n = list[0];
        list.RemoveAt(0);
        var i = list.IndexOf(n);
        if (i == -1) return n;
        list.RemoveAt(i);
    }
    return -1;
}

从数组中移除(我最喜欢的所有这些)

喜欢&#34;列表删除&#34;上面的解决方案,但通过坚持使用数组并减少搜索空间而不是删除和重新分配列表来尝试提高效率。此解决方案可能总体上具有最佳性能,因为它不需要内存分配,并且已经找到的项目通过缩小窗口(通过增加i)从进一步搜索中排除。

public static int ByRemovingFromArray(int[] input)
{
    var i = 0;
    while (i <= input.GetUpperBound(0))
    {
        var n = input[i];
        var j = Array.IndexOf(input, n, ++i);
        if (j == -1) return n;
        input[j] = input[i++];
    }
    return -1;
}

蛮力

为了完整性&#39;缘故。

public static int BruteForce(int[] input)
{
    for (int i=0; i<=input.GetUpperBound(0); i++)
    {
        int matchCount = 0;
        for (int j=0; j<=input.GetUpperBound(0); j++)
        {
            if (i == j) continue;
            if (input[i] == input[j])
            {
                matchCount++;
            }
        }
        if (matchCount % 2 == 0) return input[i];
    }
    return -1;
}

Here's my code on DotNetFiddle.

答案 1 :(得分:0)

另一种方法是使用XOR按位运算。每个相同的数字将相互抵消,而仅保留唯一的值。

代码如下:

#include <Windows.h>
#include <array>

std::string getGUID()
{
    std::string result{};
    GUID guid;

    if (S_OK == CoCreateGuid(&guid))
    {
        std::array<char, 36> buffer{};   //32 characters of guid + 4 '-' in-between

        snprintf(buffer.data(), buffer.size(), "{%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x}",
                guid.Data1, guid.Data2, guid.Data3, guid.Data4[0], guid.Data4[1], guid.Data4[2], guid.Data4[3], guid.Data4[4], guid.Data4[5], guid.Data4[6], guid.Data4[7]);
        result = std::string(buffer.data());
    }

    return result;
}