【问题标题】:Is there a more efficient way of checking whether two numbers in a list add up to an int, k?有没有更有效的方法来检查列表中的两个数字加起来是否为 int, k?
【发布时间】:2019-11-01 23:45:16
【问题描述】:

我订阅了一份编码挑战邮件列表。这是今天的:

给定一个数字列表和一个数字 k,返回列表中任意两个数字加起来是否为 k。

例如,给定 [10, 15, 3, 7] 和 k of 17,返回 true,因为 10 + 7 是 17。

奖励:你能一次性完成吗?

我想出了以下方法,但我想知道这是否是最有效的解决方案。

bool found = false;
int k = 17;
list<int> given({10, 15, 3, 7});

int main() {
    for (int num : given) {
        found = find(given.begin(), given.end(), k - num) != given.end();

        if (found) break;
    }

    return found;
}

代码完美无缺。我只是想知道它是否可以更有效或 如果我在我的代码中做任何在工作场所不受欢迎的事情。非常感谢。

【问题讨论】:

  • 它不能完美运行。如果k = 6 将计数3 两次。
  • 即使它确实可以完美运行,假设您有 1000 个号码,而不是 4 个号码。您的循环将迭代 1000000 次。这听起来有效率吗?跳出框框思考,即以某种方式记住您已经看到的数字。
  • 奖金意味着它可以在一次通过中完成,并且这样做 n 次。
  • 如果您使用大小为 k+1 的标志数组,并且每个数字恰好执行 0 或 2 次数组写入,则可以在 1 次通过。
  • 另外,如果任何问题的解决方案涉及嵌套的for 循环,它几乎总是低效的。如果您看到自己在写 for (int i = 0; i &lt; n; ++i) { for (j = 0; j &lt; n; ++j;) 或类似的东西,请停下来采取不同的方法。

标签: c++ performance


【解决方案1】:

您可以使用集合对数组进行一次迭代。

int k = 17;
list<int> given({10, 15, 3, 7});
unordered_set<int> seen();

// O(n) time-complexity
int main() {
    // Iterate over O(n) items
    for (int num : given) {
        // O(1) operations
        if (seen.contains(k - num)) {
            // seen contains a value that is the difference between k and num. If you add num to that value, k - n + n = k, you have found two numbers that sum to k
            return true;
        } else {
            // Better luck next time, keep looking
            seen.add(num);
        }
    }

    return false;
}

【讨论】:

  • 想知道它是否可以算作数组的一次迭代,在另一个容器中复制...
  • 我认为重点是时间复杂性,它不会受到第二次通过的影响。但你可能是对的,你必须向挑战者澄清。
  • 当然,这很重要。这是一个具有 O(n) 内存复杂度的迭代。添加和包含操作都是 O(1) 分摊时间复杂度。
【解决方案2】:

有一个更快的解决方案,其复杂度为 O(nlogn),不确定 O(n)。

提示:使用双指针方法。

首先对数组进行排序。

有两个指针 - 一个从索引 0 开始并递增,另一个从最后一个索引开始并递减。

令从0开始的为a,从最后一个索引开始的为b

如果a+b大于目标值,那么我们必须减小b的索引。请记住,索引 b 之前的数字小于索引 b 之前的数字。

如果a+b小于目标值,我们必须增加a的索引,因为索引a之后的数字em> 形成一个递增序列。

sort(v.begin(), v.end());
    while(b>a){
        if (v[a]+v[b]==target) {
            //There exists such values
            return 0;
        }
        else if (v[a]+v[b]>target) {
            b--;
        }
        else {
            a++;
        }
    }

【讨论】:

  • 请注意,这不是一次性方法(因为排序本身构成排序的“超级通过”),但至少只有O(n log n)O(1) 或 @ 987654324@ 空间开销,取决于您的排序是如何实现的),其中 OP 的方法是O(n²)。对于 1000 个元素的输入,这就是做约 10,000 个“单位”工作和做约 1,000,000 个“单位”工作之间的区别。
【解决方案3】:

保留“需要”值的地图/字典。

  • 扫描给定,
  • 是否需要给定值?

    • 如果是,则完成

    • 否则将所需的值添加到所需的地图

可以说,这是 O(n) 或 O(n log n)(取决于地图性能,您可以使用集合)

伪代码:

bool searchy( int wanted ) {
    bool found = false;
    needed = map<int,bool>{};
    for( each x in given ) {
        if( needed[x] ) then { return found = true; }
        else { needed[wanted - x] = false; }
    }

    return found;
}

以及C++中的算法

#include <map>
#include <list>
int wanted = 17;
std::list<int> given({10, 15, 3, 7});

bool searchy(int wanted) {
    bool found = false;
    std::map<int,bool> needed = {}; //could keep position of item matched...
    for (int x : given) {
        if( needed.count(x) > 0 ) { found = true; break; }
        else { needed[wanted - x] = false; }
    }
    if( found ) std::cout "found: (" << wanted - x << "+" << x << std::endl;
    return found;
}

int main() {
    bool found = searchy(wanted);
    return found;
}

【讨论】:

  • a std::map&lt;Key,T&gt;(尤其是T = bool),其中只检查Key 应该是std::set&lt;Key&gt;
  • 然而,我可能想指出哪个位置......因为那是下一个问题......
【解决方案4】:

我使用 JS 对象的 Javascript 解决方案(我认为 JS Obj 与其他语言中的哈希映射相同)。当我们遍历数组时,该解决方案会记住元素,这可能会占用大量内存。但复杂度将保持为 O(n)。

const checkTwoSum = (arr, sum) => {
  const obj = {};
  const found = arr?.find(item => {
    const target = sum - item;
    if (obj[target]) return true;
    else {
        obj[item] = 1;
    }
  });

  return !!(found || found === 0);
}

【讨论】:

    猜你喜欢
    • 2020-09-18
    • 1970-01-01
    • 2022-09-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-10-28
    • 2011-08-08
    • 2019-09-30
    相关资源
    最近更新 更多