给定一个大整数列表(超过1 000 000个值),找出选择其中两个加起来为0的方法有多少....是问题
我所做的是创建一个正的随机整数列表:
Random pos = new Random();
int POSNO = pos.Next(1, 1000000);
lstPOS.Items.Add(POSNO);
lblPLus.Text = lstPOS.Items.Count.ToString();
POSCount++;
并创建了一个否定列表:
Random neg = new Random();
int NEGNO = neg.Next(100000, 1000000);
lstNEG.Items.Add("-" + NEGNO);
lblNegative.Text = lstNEG.Items.Count.ToString();
NegCount++;
要进行我正在使用的总和检查:
foreach (var item in lstPOS.Items)
{
int POSItem = Convert.ToInt32(item.ToString());
foreach (var negItem in lstNEG.Items)
{
int NEGItem = Convert.ToInt32(negItem.ToString());
int Total = POSItem - NEGItem;
if (Total == 0)
{
lstADD.Items.Add(POSItem + "-" + NEGItem + "=" + Total);
lblAddition.Text = lstADD.Items.Count.ToString();
}
}
}
我知道这不是最快的路线。我考虑过使用数组。你有什么建议吗?
答案 0 :(得分:5)
让我们看看;你的数组是这样的:
int[] data = new int[] {
6, -2, 3, 2, 0, 0, 5, 7, 0, -2
};
您可以通过两种不同的方式添加到零:
在上面的示例中,有五个对:
-2 + 2 (two pairs): [1] + [3] and [3] + [9]
0 + 0 (three pairs): [4] + [5], [4] + [8] and [5] + [8]
所以你必须跟踪正/负对和零。实施
Dictionary<int, int> positives = new Dictionary<int, int>();
Dictionary<int, int> negatives = new Dictionary<int, int>();
int zeros = 0;
foreach(var item in data) {
int v;
if (item < 0)
if (negatives.TryGetValue(item, out v))
negatives[item] = negatives[item] + 1;
else
negatives[item] = 1;
else if (item > 0)
if (positives.TryGetValue(item, out v))
positives[item] = positives[item] + 1;
else
positives[item] = 1;
else
zeros += 1;
}
// zeros: binomal coefficent: (2, zeros)
int result = zeros * (zeros - 1) / 2;
// positive/negative pairs
foreach (var p in positives) {
int n;
if (negatives.TryGetValue(-p.Key, out n))
result += n * p.Value;
}
// Test (5)
Console.Write(result);
注意,没有排序,字典(即哈希表)用于正面和负面,因此执行时间将是线性,O(n)
;实现的黑暗面是需要两个额外的结构(即附加存储器)。在你的情况下(只有数百万个整数 - 兆字节),你有那种记忆。
编辑:terser,但可读性较差的Linq解决方案:
var dict = data
.GroupBy(item => item)
.ToDictionary(chunk => chunk.Key, chunk => chunk.Count());
int result = dict.ContainsKey(0) ? dict[0] * (dict[0] - 1) / 2 : 0;
result += dict
.Sum(pair => pair.Key > 0 && dict.ContainsKey(-pair.Key) ? pair.Value * dict[-pair.Key] : 0);
答案 1 :(得分:3)
最快的方式没有排序!。
首先你要知道,当两个整数的等于绝对值时,它们之间只有0,但是一个是负数而另一个是正数。所以你不需要排序。你需要的是将正面列表与负面列表相交(通过比较绝对值)。结果是数字最终为0总和。
Intersect的时间复杂度为O(n+m)
,其中n
是第一个列表的大小,m
是第二个列表的大小。
private static void Main(string[] args)
{
Random random = new Random();
int[] positive = Enumerable.Range(0, 1000000).Select(n => random.Next(1, 1000000)).ToArray();
int[] negative = Enumerable.Range(0, 1000000).Select(n => random.Next(-1000000, -1)).ToArray();
var zeroSum = positive.Intersect(negative, new AbsoluteEqual());
foreach (var i in zeroSum)
{
Console.WriteLine("{0} - {1} = 0", i, i);
}
}
您还需要使用此IEqualityComparer。
public class AbsoluteEqual : IEqualityComparer<int>
{
public bool Equals(int x, int y)
{
return (x < 0 ? -x : x) == (y < 0 ? -y : y);
}
public int GetHashCode(int obj)
{
return obj < 0 ? (-obj).GetHashCode() : obj.GetHashCode();
}
}
答案 2 :(得分:1)
您试图避免检查两个接近的数字(1,2接近,3,4接近),但您没有避免检查(-100000,1),(-1,100000)。时间复杂度为O(n ^ 2)。 为避免这种情况,您需要先对它们进行排序,然后从两个方向进行搜索。
var random = new Random();
var input = Enumerable.Range(1, 100).Select(_ => random.Next(200) - 100).ToArray();
Array.Sort(input); // This causes most computation. Time Complexity is O(n*log(n));
var expectedSum = 0;
var i = 0;
var j = input.Length - 1;
while (i < j) // This has liner time complexity O(n);
{
var result = input[i] + input[j];
if(expectedSum == result)
{
var anchori = i;
while (i < input.Length && input[i] == input[anchori] )
{
i++;
}
var anchorj = j;
while (j >= 0 && input[j] == input[anchorj])
{
j--;
}
// Exclude (self, self) combination
Func<int, int, int> combination = (n, k) =>
{
var mink = k * 2 < n ? k : n - k;
return mink == 0 ? 1
: Enumerable.Range(0, mink).Aggregate(1, (x, y) => x * (n - y))
/ Enumerable.Range(1, mink).Aggregate((x, y) => x * y);
};
var c = i < j ? (i - anchori) * (anchorj - j) : combination(i - anchori, 2);
for (int _ = 0; _ < c; _++)
{
// C# 6.0 String.Format
Console.WriteLine($"{input[anchori]}, {input[anchorj]}");
}
}
else if(result < expectedSum) {
i++;
}
else if(result > expectedSum) {
j--;
}
}
答案 3 :(得分:1)
这是使用(huh)LINQ的另一种解决方案。希望代码是自我解释的
首先是一些数据
var random = new Random();
var data = new int[1000000];
for (int i = 0; i < data.Length; i++) data[i] = random.Next(-100000, 100000);
现在的解决方案
var result = data
.Where(value => value != int.MinValue)
.GroupBy(value => Math.Abs(value), (key, values) =>
{
if (key == 0)
{
var zeroCount = values.Count();
return zeroCount * (zeroCount - 1) / 2;
}
else
{
int positiveCount = 0, negativeCount = 0;
foreach (var value in values)
if (value > 0) positiveCount++; else negativeCount++;
return positiveCount * negativeCount;
}
})
.Sum();
理论上,上面应该有O(N)时间和O(M)空间复杂度,其中M是列表中唯一绝对值的计数。