我有一个包含1000万个元素的整数数组,如何在C#中编写一个函数,如果数组有一个总和最多为75的对,则返回True。
我的代码是:
int sum = 75, max = 10000000;
int[] array = new int[max];
bool checkFlag = false;
Random rnd = new Random();
Stopwatch sw = Stopwatch.StartNew();
for (int i = 0; i < max; i++)
{
array[i] = rnd.Next(0, max * 20);
}
Array.Sort(array);
if (array[0] + array[1] <= sum)
{
Console.WriteLine("{0} + {1} = {2}", array[0], array[1], array[0] + array[1]);
checkFlag = true;
}
Console.WriteLine("Sum upto 75 is: " + checkFlag);
答案 0 :(得分:8)
这是一个问题。你想要0s的桶与75s配对,1s与74s配对,等等。 Bucketing是一个字典作业。 Dictionary<int, List<int>>
给出O(n)摊销的结果。如果您只关心bool结果,那么HashSet<int>
就足够了。你不能比O(n)好。
static bool PairExists(int[] arr, int sum) {
var set = new HashSet<int>();
foreach (int elem in arr) set.Add(elem);
foreach (int elem in set)
if (set.Contains(sum - elem)) return true;
return false;
}
如果数组可能包含该对,那么您可以考虑在Add()调用之后进行测试,仍然是O(n)。
答案 1 :(得分:1)
如果数组已排序,这将有效。
public bool ContainsPair(int[] array)
{
int i = 0;
int j = array.Length - 1;
while(i < j)
{
if (array[i] + array[j] == 75)
return true;
else if (array[i] + array[j] < 75)
i++;
else if (array[i] + array[j] > 75)
j--;
}
return false;
}
你使用两个指针走向数组的中间。指针i
从数组的开头开始,而j
从结尾开始。如果您找到两个总和为75的数字,则返回true。如果总和小于75,则将指针i
向中间移动一步并再次检查。如果总和超过75,则将指针j
向中间移动一步并再次检查。
如果两个指针相遇,则返回false,因为找不到对。
这是O(n),不包括对数组进行排序。
答案 2 :(得分:0)
如果您认为数字是正数,则可以这样做:
示例C#:
var bits = new bool[75];
foreach (var n in intArray)
{
if (n <= 75)
{
var diff = 75 - n;
if (bits[diff - 1])
{
MessageBox.Show(string.Format("Found pair: {0} and {1}", n, diff));
break;
}
bits[n - 1] = true;
}
}
但是如果数组中的数字可以是任何有效整数,包括负数,你可以这样做:
var set = new HashSet<int>();
foreach (var n in intArray)
{
if (n <= 75)
{
var diff = 75 - n;
if (set.Contains(diff))
{
MessageBox.Show(string.Format("Found pair: {0} and {1}", n, diff));
break;
}
set.Add(n);
}
}
答案 3 :(得分:0)
你可以进行蛮力搜索。
public bool hasPairOf(int[] a, int sum)
{
for(int i=0; i < a.length-1; i++)
{
if(a[i]+a[i+1] == sum)
return true;
}
return false;
}
或者,您可以创建一个枚举器并使用LINQ。
public static IEnumerate<int> toSums(this int[] a)
{
for(int i=0; i < a.length-1; i++)
{
yield return a[i]+a[i+1];
}
}
现在您可以执行以下操作。
a.toSums().Any(pair=>pair == 75);
两者都应具有相同的性能。如果你问为什么?这是因为C#只会在Any
条件为真之前执行枚举器。 toSums
函数使用yield
关键字创建仅在评估时执行的枚举器。
修改强>
要在数组中找到总和为75且不仅仅是相邻的任何值对。除了LINQ之外,我只会使用它来更容易阅读。
function bool hasPairOf(int[] a, int sum)
{
var nums = a.Where(val=>val <= sum)
.Select((v,i)=>new{value=v,index=i})
.ToArray();
return (from a1 in nums
from a2 in nums
where a1.index != a2.index
&& a1.value+a2.value == sum
select true).FirstOrDefault();
}
答案 4 :(得分:0)
using System;
using System.Diagnostics;
using System.Linq;
using System.Collections.Generic;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
const int max = 10000000;
const int sum = 75;
var data = new int[max];
var rnd = new Random();
bool found = false;
int c = 1;
Stopwatch sw;
while (!found)
{
sw = Stopwatch.StartNew();
for (int i = 0; i < max; ++i)
{
data[i] = rnd.Next(0, max*25);
}
sw.Stop();
Console.WriteLine("Took {0}ms to create the array", sw.ElapsedMilliseconds);
sw = Stopwatch.StartNew();
var check75 = new HashSet<int>(data.Where(x => x <= 75));
sw.Stop();
Console.WriteLine("Took {0}ms to create the hashset", sw.ElapsedMilliseconds);
sw = Stopwatch.StartNew();
for (int i = 0; i < max; ++i)
{
if (check75.Contains(sum - data[i]))
{
Console.WriteLine("{0}, {1} ", i, data[i]);
found = true;
}
}
sw.Stop();
Console.WriteLine("Took {0}ms to check75", sw.ElapsedMilliseconds);
Console.WriteLine("Loop #" + c++);
}
Console.WriteLine("Finish");
Console.ReadKey();
}
}
}