【发布时间】:2012-08-09 07:34:32
【问题描述】:
给定一个整数 x 和一个由 N 个不同整数组成的排序数组 a,设计一个线性时间算法来确定是否存在两个不同的索引 i 和 j 使得 a[i] + a[j] == x
【问题讨论】:
-
如果它是不同的但没有排序怎么办?如果不区分且未排序怎么办??
标签: algorithm
给定一个整数 x 和一个由 N 个不同整数组成的排序数组 a,设计一个线性时间算法来确定是否存在两个不同的索引 i 和 j 使得 a[i] + a[j] == x
【问题讨论】:
标签: algorithm
这是Subset sum problem的类型
这是我的解决方案。不知道是不是早知道。想象一下两个变量 i 和 j 的函数的 3D 图:
sum(i,j) = a[i]+a[j]
对于每个i,都有这样一个j,a[i]+a[j] 最接近x。所有这些 (i,j) 对形成 closest-to-x 行。我们只需要沿着这条线走,寻找a[i]+a[j] == x:
int i = 0;
int j = lower_bound(a.begin(), a.end(), x) - a.begin();
while (j >= 0 && j < a.size() && i < a.size()) {
int sum = a[i]+a[j];
if (sum == x) {
cout << "found: " << i << " " << j << endl;
return;
}
if (sum > x) j--;
else i++;
if (i > j) break;
}
cout << " not found\n";
复杂度:O(n)
【讨论】:
lower_bound 调用并设置int j = a.size() - 1 可能是一个解决方案?
lower_bound(a.begin(), a.end(), x-*a)。为什么不控制循环只是while (i < j)?另一种方法是使用lower_bound(a.begin(), a.end(), x/2) 并由内而外工作 - 更复杂的循环控制。 (只需向下滚动到其他答案 - 这是Guru Devanla's。)
从补语的角度思考。
遍历列表,为每个项目计算出该数字到达 X 所需的数字是多少。将数字和补码放入哈希中。在迭代检查以查看数字或其补码是否在散列中。如果有,找到了。
编辑:因为我有一些时间,一些伪代码。
boolean find(int[] array, int x) {
HashSet<Integer> s = new HashSet<Integer>();
for(int i = 0; i < array.length; i++) {
if (s.contains(array[i]) || s.contains(x-array[i])) {
return true;
}
s.add(array[i]);
s.add(x-array[i]);
}
return false;
}
【讨论】:
是 2*n ~ O(n)
这我们可以扩展到二分搜索。
使用二分搜索搜索一个元素,这样我们就可以找到 L,使得 L 为 min(elements in a > ceil(x/2))。
对 R 执行相同操作,但现在将 L 作为数组中可搜索元素的最大大小。
这种方法是 2*log(n)。
【讨论】:
这是一个使用 Dictionary 数据结构和数字补码的 python 版本。这具有线性运行时间(N 阶:O(N)):
def twoSum(N, x):
dict = {}
for i in range(len(N)):
complement = x - N[i]
if complement in dict:
return True
dict[N[i]] = i
return False
# Test
print twoSum([2, 7, 11, 15], 9) # True
print twoSum([2, 7, 11, 15], 3) # False
【讨论】:
dict 应该在 for 循环之前填充
鉴于数组已排序(WLOG 降序排列),我们可以执行以下操作: 算法 A_1: 我们得到 (a_1,...,a_n,m), a_1<... m unsat sat>
很明显,这是 O(n),因为计算的和的最大数量正好是 n。正确性的证明留作练习。
这只是用于 SUBSET-SUM 的 Horowitz 和 Sahni (1974) 算法的一个子程序。 (但请注意,几乎所有通用 SS 算法都包含这样的例程,Schroeppel, Shamir (1981), Howgrave-Graham_Joux (2010), Becker-Joux (2011)。)
如果给定一个无序列表,实现这个算法将是 O(nlogn),因为我们可以使用 Mergesort 对列表进行排序,然后应用 A_1。
【讨论】:
遍历数组并将合格的数字及其索引保存到地图中。该算法的时间复杂度为O(n)。
vector<int> twoSum(vector<int> &numbers, int target) {
map<int, int> summap;
vector<int> result;
for (int i = 0; i < numbers.size(); i++) {
summap[numbers[i]] = i;
}
for (int i = 0; i < numbers.size(); i++) {
int searched = target - numbers[i];
if (summap.find(searched) != summap.end()) {
result.push_back(i + 1);
result.push_back(summap[searched] + 1);
break;
}
}
return result;
}
【讨论】:
summap.find的复杂度是O(log n),所以这个算法的复杂度是O(n log n),而不是O(n)。
我会像这样将差异添加到HashSet<T>:
public static bool Find(int[] array, int toReach)
{
HashSet<int> hashSet = new HashSet<int>();
foreach (int current in array)
{
if (hashSet.Contains(current))
{
return true;
}
hashSet.Add(toReach - current);
}
return false;
}
【讨论】:
注意:代码是我的,但测试文件不是。另外,哈希函数的这个想法来自网上的各种阅读。
Scala 中的实现。它对值使用 hashMap 和自定义(但简单)映射。我同意它没有利用初始数组的排序特性。
哈希函数
我通过将每个值除以 10000 来固定存储桶大小。该数字可能会有所不同,具体取决于您想要的存储桶大小,可以根据输入范围进行优化。
例如,键 1 负责从 1 到 9 的所有整数。
对搜索范围的影响
这意味着,对于当前值 n,您正在为其寻找补码 c,例如 n + c = x (x 是您试图找到 2-SUM 的元素),只有 3 个可能的桶可以补码:
假设您的号码在以下格式的文件中:
0
1
10
10
-10
10000
-10000
10001
9999
-10001
-9999
10000
5000
5000
-5000
-1
1000
2000
-1000
-2000
这是 Scala 中的实现
import scala.collection.mutable
import scala.io.Source
object TwoSumRed {
val usage = """
Usage: scala TwoSumRed.scala [filename]
"""
def main(args: Array[String]) {
val carte = createMap(args) match {
case None => return
case Some(m) => m
}
var t: Int = 1
carte.foreach {
case (bucket, values) => {
var toCheck: Array[Long] = Array[Long]()
if (carte.contains(-bucket)) {
toCheck = toCheck ++: carte(-bucket)
}
if (carte.contains(-bucket - 1)) {
toCheck = toCheck ++: carte(-bucket - 1)
}
if (carte.contains(-bucket + 1)) {
toCheck = toCheck ++: carte(-bucket + 1)
}
values.foreach { v =>
toCheck.foreach { c =>
if ((c + v) == t) {
println(s"$c and $v forms a 2-sum for $t")
return
}
}
}
}
}
}
def createMap(args: Array[String]): Option[mutable.HashMap[Int, Array[Long]]] = {
var carte: mutable.HashMap[Int,Array[Long]] = mutable.HashMap[Int,Array[Long]]()
if (args.length == 1) {
val filename = args.toList(0)
val lines: List[Long] = Source.fromFile(filename).getLines().map(_.toLong).toList
lines.foreach { l =>
val idx: Int = math.floor(l / 10000).toInt
if (carte.contains(idx)) {
carte(idx) = carte(idx) :+ l
} else {
carte += (idx -> Array[Long](l))
}
}
Some(carte)
} else {
println(usage)
None
}
}
}
【讨论】:
int[] b = new int[N];
for (int i = 0; i < N; i++)
{
b[i] = x - a[N -1 - i];
}
for (int i = 0, j = 0; i < N && j < N;)
if(a[i] == b[j])
{
cout << "found";
return;
} else if(a[i] < b[j])
i++;
else
j++;
cout << "not found";
【讨论】:
这里是一个线性时间复杂度解 O(n) 时间 O(1) 空间
public void twoSum(int[] arr){
if(arr.length < 2) return;
int max = arr[0] + arr[1];
int bigger = Math.max(arr[0], arr[1]);
int smaller = Math.min(arr[0], arr[1]);
int biggerIndex = 0;
int smallerIndex = 0;
for(int i = 2 ; i < arr.length ; i++){
if(arr[i] + bigger <= max){ continue;}
else{
if(arr[i] > bigger){
smaller = bigger;
bigger = arr[i];
biggerIndex = i;
}else if(arr[i] > smaller)
{
smaller = arr[i];
smallerIndex = i;
}
max = bigger + smaller;
}
}
System.out.println("Biggest sum is: " + max + "with indices ["+biggerIndex+","+smallerIndex+"]");
}
【讨论】:
解决方案
更多信息:[http://www.prathapkudupublog.com/2017/05/two-sum-ii-input-array-is-sorted.html
【讨论】:
感谢狮子座
他的java解决方案,如果你想试一试
我删除了返回,所以如果数组已排序,但允许重复,它仍然给出对
static boolean cpp(int[] a, int x) {
int i = 0;
int j = a.length - 1;
while (j >= 0 && j < a.length && i < a.length) {
int sum = a[i] + a[j];
if (sum == x) {
System.out.printf("found %s, %s \n", i, j);
// return true;
}
if (sum > x) j--;
else i++;
if (i > j) break;
}
System.out.println("not found");
return false;
}
【讨论】: