记一道面试题
题目是这样的,已知一个整数数列(这里应该说的是正整数数列)和一个给定的sum值,从这个整数数列中,找出两个整数的和刚好等于sum值,将这个数列中所有这种可能的组合进行输出。
例如:有一个整数数列{3,4,2,7,5,2,4},sum=6,
那么这样的组合就有:
第二项和第三项的和,即<2,3>;
第二项和第六项的和,即<2,6>;
第三项和第七项的和,即<3,7>;
以此类推…
当时我的解答是这样,比较笨哈:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConsoleApplication16
{
class Program
{
static void Main(string[] args)
{
int[] testArray = { 3, 4, 2, 7, 5, 2, 4 };
Console.WriteLine(GetNumCombinations(testArray,6));
}
public static string GetNumCombinations(int[] arrayInt, int sum)
{
List<int> firstNums = new List<int>();
List<int> secondNums = new List<int>();
for (int i = 0; i < arrayInt.Length-1; i++)
{
for (int j = i+1; j < arrayInt.Length; j++)
{
if (arrayInt[i]+arrayInt[j]==sum)
{
firstNums.Add(i+1);
secondNums.Add(j + 1);
}
}
}
StringBuilder sb = new StringBuilder();
for (int i = 0; i < firstNums.Count; i++)
{
sb.Append(String.Format("<{0},{1}>",firstNums[i],secondNums[i]));
}
return sb.ToString();
}
}
}
这里有两个问题,第一个问题就是采用List来保存配对的元素。List的内部顺序是不稳定的,也就是说,你先Add进去的元素并不一定会在后Add进去的元素前面,所以,有可能会造成配对的信息错误。(这个问题其实我没考证过,是那个面试官这么解释的,大致意思我猜想就是这样)所以应该自定义一个数据结构进行保存。
还有一个问题就是算法的效率问题了,具体差在哪?一看就懂得哈!
面试官给了我一个方案,先对数列进行一个从小到大的排序,找sum,和sum/2在数列中的位置,然后再循环查找。
回来后总结了一下,我想他的大致意思应该是这样的:
继续拿上面得例子演示,先将数列进行排序。
2,2,3,4,4,5,7
当然,顺序乱了,之前的序列值肯定也不对了,所以需要多增加一个数组来维护它的序列值,就假如它排序后的序列值如下:
2,2,3,4,4,5,7
3,6,1,2,7,5,4(这一行是序列值)
然后找到sum和sum/2在该序列中的位置,sum=6,sum/2=3:
2,2,3,(3),4,4,5,(6),7
相信大部分人应该看出名堂了吧,想要两个数的和相加等于6,那么这两个数必定有一个小于3,另一个大于3,不可能有两个数都大于3的数相加小于等于6的,同理,也不可能有两个数都小于3的数相加大于等于6的。
在取数据进行相加的时候,设一个变量x从最小值到中间值,一个变量y从最大值到中间值:
→x ←y
2,2,3,(3),4,4,5,(6),7
如果两数相加比sum大,那么最大值y肯定更小,如果两数相加比sum小,那么最小值x需要更大,直到x与y到中间值为止。
代码重新整理如下(咱就不考虑排序的效率了哈,也没有很好的容错性,应该会有各种BUG哈,呵呵!)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConsoleApplication17
{
//保存配对信息
struct NumCombin
{
public int x;
public int y;
public override string ToString()
{
return String.Format("<{0},{1}>", x, y);
}
}
class Program
{
public static NumCombin[] GetNumCombinations(int[] arrayInt, int sum)
{
List<NumCombin> numCombinList = new List<NumCombin>();
//创建一个数组维护排序顺序
int[] arrayOrder = new int[arrayInt.Length];
for (int i = 0; i < arrayOrder.Length; i++)
{
arrayOrder[i] = i + 1;
}
//一个简单的冒泡,进行从小到大的排序
for (int i = 0; i < arrayInt.Length; i++)
{
bool isChange = false;
for (int j = 0; j < arrayInt.Length-1-i; j++)
{
if (arrayInt[j]>arrayInt[j+1])
{
int temp = arrayInt[j];
arrayInt[j] = arrayInt[j + 1];
arrayInt[j + 1] = temp;
temp = arrayOrder[j];
arrayOrder[j] = arrayOrder[j + 1];
arrayOrder[j + 1] = temp;
isChange = true;
}
}
if (!isChange)
{
break;
}
}
int maxCount=-1, halfCount=-1;
for (int i = 0; i < arrayInt.Length; i++)
{
if (arrayInt[i]>sum)
{
maxCount = i;
break;
}
}
if (maxCount==-1)
{
maxCount = arrayInt.Length;
}
for (int i = 0; i < arrayInt.Length; i++)
{
if (arrayInt[i]>sum/2)
{
halfCount = i;
break;
}
}
//如果这个数列最大的数都没有达到sum值的一半,
//那么肯定不会存在两个数相加为sum的值
if (halfCount==-1)
{
return null;
}
for (int i = 0; i < halfCount; i++)
{
for (int j = maxCount; j >= halfCount; j--)
{
if (arrayInt[i] + arrayInt[j] > sum)
{
maxCount--;
}
else if (arrayInt[i] + arrayInt[j] < sum)
{
break;
}
else
{
NumCombin numCombin = new NumCombin() { x = arrayOrder[i], y = arrayOrder[j] };
numCombinList.Add(numCombin);
}
}
}
return numCombinList.ToArray();
}
static void Main(string[] args)
{
NumCombin[] test = GetNumCombinations(new int[] { 3, 4, 2, 7, 5, 2, 4 },6);
foreach (var item in test)
{
Console.WriteLine(item);
}
}
}
}
代码写得有点拙劣,望广大博友批评指导!希望有人能提出更好的方案哈!