从TwoSum浅析时间复杂度的优化策略

版权声明:本文为博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/Yakov_Sverdlov/article/details/78598900

TwoSum
以上是leetcode链接
题目其实很简单,只是使用题中给出的一个int数组,以及一个int型整数,在数组中找两个元素,使其相加等于目标整数。

题目

class Solution {
public:
    vector<int> twoSum(vector<int>& nums, int target) {
        for (int i = 0; i < nums.size(); i++)//用二重循环进行查找,时间复杂度为O(n^2)
    {
        for (int j = i + 1; j < nums.size(); j++)
        {
            if (nums[i] + nums[j] == target)
            {
                vector<int> vec(2);
                vec[0] = i;
                vec[1] = j;
                return vec;
            }
        }
    }
    }
};

我的这个算法有比较大的问题:
(1)使用二重循环来遍历整个数组是没有必要的,凭空增加了不必要的时间复杂度。
(2)过于简单和不动脑筋,查找还有很多其他的方法可以使用,而非只有遍历。

在此贴上leetcode上只需要3ms的优秀代码:

static int x = [](){ 
    std::ios::sync_with_stdio(false); 
    cin.tie(NULL);  
    return 0; 
}();

class Solution {
public:
    vector<int> twoSum(vector<int>& nums, int target) {
        vector<int> result;
        map<int,int> numNeeded;
        std::map<int,int>::iterator it;

        int i,targNum, len = nums.size();

        for(i = 0; i < len; i++)
        {
            it = numNeeded.find(nums[i]);
            if(it == numNeeded.end())
            {
                targNum = target - nums[i];
                numNeeded.insert(numNeeded.begin(), std::pair<int,int>(targNum,i));
            }
            else
            {
                return vector<int> {it->second, i};
            }
        }
        return result;
    }
};

关于std::ios::sync_with_stdio()以及cin.tie()这两个函数的使用,参见以下这篇博客的说明。
cin.tie与sync_with_stdio加速输入输出

文中提到,

在ACM里,经常出现数据集超大造成 cin TLE的情况。这时候大部分人(包括原来我也是)认为这是cin的效率不及scanf的错,甚至还上升到C语言和C++语言的执行效率层面的无聊争论。其实像上文所说,这只是C++为了兼容而采取的保守措施。我们可以在IO之前将stdio解除绑定,这样做了之后要注意不要同时混用cout和printf之类。在默认的情况下cin绑定的是cout,每次执行 << 操作符的时候都要调用flush,这样会增加IO负担。可以通过tie(0)(0表示NULL)来解除cin与cout的绑定,进一步加快执行效率。

sync_with_stdio这个函数是一个“是否兼容stdio”的开关,C++为了兼容C,保证程序在使用了std::printf和std::cout的时候不发生混乱,将输出流绑到了一起。

由此可见,sync_with_stdio()与cin.tie()的作用在于对stdio进行解除绑定的操作,增加IO负担的原因在于每次执行<<操作符都被调用的flush。在解绑之后,可以加快执行效率。

3ms的代码中,加快查找速度主要是依靠c++中的关联式容器map。
在map中,根据key值快速查找记录,查找的复杂度是log(n),因此可以较大地加快查找速度。
但是map的的数据结构是采取了键值对的形式,即key-value这种模式,所以在此题当中通过建立map来加速查找,其重点在于如何建立key-value这种对应关系。
源码作者先是建立了一个空的map,for循环中的第一次查找自然是查找不到任何结果的,所以迭代器it会被指向容器尾元素的下一个元素(其实应该是超尾)
然后在接下来if语句的判断条件为it是否指向了numNeeded.end()所返回的值。此时由于map为空,所以numNeeded.end()理应也是超尾,意为没有找到目前所要插入的键值对,key是target与key的差(即targetNum),value为targetNum对应的num[i]的序号。因为对于每一个target,都会有一组特定解在所给出的int数组中。
比如target=3,num[]={1,5,2},那么所建立的map为{<2,0>,<-2,1>},因为在第三次调用find()函数时,返回结果it=begin(),指向了第一个键值对,因而return的vector为<0,2>。

展开阅读全文

没有更多推荐了,返回首页