【问题标题】:How is the time complexity O(n) and not O(n^2)?时间复杂度是 O(n) 而不是 O(n^2)?
【发布时间】:2018-03-11 01:26:20
【问题描述】:

我正在解决LeetCode.com上的一个问题:

给定一个正整数向量nums,计算并打印子数组中所有元素的乘积小于 k 的(连续)子数组的数量。

代码(我在大量在线帮助下编写的)如下:

class Solution {
public:
    int numSubarrayProductLessThanK(vector<int>& nums, int k) {
        if(nums.empty() || k<=1) return 0;

        int counter=0, left=0, currProd=1;
        for(int i=0; i<nums.size(); i++) {
            currProd*=nums[i];
            while(left<nums.size() && currProd>=k)
                currProd/=nums[left++];
            counter+=i-left+1;
        }

        return counter;
    }
};

虽然我了解发生了什么,但我不明白时间复杂度是如何被称为O(n) 而不是O(n^2)。恕我直言,ileft 都会增加,导致在最坏的情况下访问 nums 的每个元素两次 - 一次是通过归纳变量 i,然后是 left

那么,O(n) 的时间复杂度如何?

【问题讨论】:

  • 如果你在一个循环中有一个循环,你通常会遇到 O(n^2) 的情况。
  • @tadman,然而,据说时间复杂度是 O(n)
  • 理论上这可能是正确的,但我认为您的代码不符合该标准。您可能需要另一种方法。对于 O(n),您需要使用单循环执行一次通过方法。
  • 我认为要真正做到O(n),您需要摆脱内部while,所以这是一个简单的线性扫描。您可能需要使用一些巧妙的技巧来消除这种情况。
  • 我认为如果它符合 O(n) 条件,那是因为 left 在循环内永远不会重置为零,因此“内循环”永远不会迭代超过 mums.size() 次不管外循环迭代多少次。

标签: c++ algorithm time-complexity


【解决方案1】:

虽然代码可能看起来很像O(N^2),但需要注意的关键是:

在 for 循环中,left 永远不会重置为 0,并且始终在 while 循环中递增。

这意味着while循环在代码整个执行期间最多只能执行N次。因此代码只在数组中运行了两次,因此O(N)

【讨论】:

  • 是的,我明白了。但我的疑问是:i 访问所有元素一次; left(尽管它没有被重置)访问所有元素(对于给定值i)。那么,怎么还是O(n)
  • left 在整个运行过程中只访问每个元素一次,而不是在每次 for 循环执行期间。
【解决方案2】:

每次执行内部循环的主体时,left 都会增加 1。left 最初为 0,并且永远不会超过 nums.size()。因此内部循环的主体最多执行nums.size() 次。

外层循环的主体被精确地执行了nums.size() 次。

因此函数的执行时间最多为T0 + nums.size() * T1 + nums.size() * T2 其中T0是在外循环之外执行代码所花费的时间,T1是在不包括内循环的情况下执行一次外循环迭代所花费的时间循环,T2 是执行一次内循环迭代所需的时间。这个上限是A + nums.size() * B 的形式,其中 A 和 B 是一些常数。因此函数的执行时间为O(nums.size())

当你有两个嵌套循环时,整个程序的复杂度最多 M*N 其中M 是外层循环的迭代次数,N最大内部循环的迭代次数。因此程序的复杂度满足O(M*N)。有时可能会找到下限,因为内部循环的迭代次数会有所不同。这里有一个不变量保证内部循环最多执行一定次数total,它给出了比O(nums.size() * nums.size())更好的界限。

【讨论】:

  • 感谢您的回答。这很有帮助。
猜你喜欢
  • 1970-01-01
  • 2021-08-13
  • 1970-01-01
  • 2019-12-13
  • 1970-01-01
  • 2020-03-22
  • 1970-01-01
  • 2019-12-09
  • 1970-01-01
相关资源
最近更新 更多