• [LintCode] Subarray Sum Closest | 前缀和


    http://www.lintcode.com/en/problem/subarray-sum-closest/#

    思路1:Brute Force

    枚举子数组,其中用一个Hash Map保存子数组的abs(sum)以及区间信息,最后返回abs(sum)最小的子数组区间。

    Time complexity: O(n^2)
    Space complexity: O(n^2)

    此方法在LintCode上会超时。

    class Solution {
    public:
        /**
         * @param nums: A list of integers
         * @return: A list of integers includes the index of the first number 
         *          and the index of the last number
         */
        vector<int> subarraySumClosest(vector<int> nums){
            vector<int> ret;
            unordered_map<long long, pair<int, int> > mymap;
            long long min_dis = INT_MAX;
            for (int i = 0; i < nums.size(); ++i) {
                long long sum = 0;
                for (int j = i; j < nums.size(); ++j) {
                    sum += nums[j];
                    long long dis = abs(sum);
                    if (dis < min_dis) {
                        min_dis = dis;
                        mymap[min_dis] = make_pair(i, j);
                    }
                }
            }
            pair<int, int> &interval = mymap[min_dis];
            ret.push_back(interval.first);
            ret.push_back(interval.second);
            return ret;
        }
    };
    

    思路2:Prefix Sum + Sort

    计算所有的前缀和并记录位置信息,然后按前缀和排序,依次计算相邻两个前缀和的差,找到差最小的两个区间,输出目标区间即可。

    Time complexity: O(n log n)
    Space complexity: O(n)

    写的过程中我是自定义了前缀和数据结构并且重载了operator<,其实可以直接用C++的pair<int, int>容器。

    struct PrefixSum {
        long long sum;
        int pos;
        PrefixSum(long long _sum = 0, int _pos = 0) : sum(_sum), pos(_pos) {}
        bool operator< (const PrefixSum &other) const {
            return (this->sum < other.sum || 
                    (this->sum == other.sum && this->pos < other.pos ));
        }
    };
    
    class Solution {
    public:
        /**
         * @param nums: A list of integers
         * @return: A list of integers includes the index of the first number 
         *          and the index of the last number
         */
        vector<int> subarraySumClosest(vector<int> nums){
            vector<int> ret;
            
            if (nums.empty()) return ret;
            
            vector<PrefixSum> prefix_sum(nums.size());
            prefix_sum[0] = PrefixSum(nums[0], 0);
            for (int i = 1; i < nums.size(); ++i) {
                prefix_sum[i] = PrefixSum(prefix_sum[i-1].sum + nums[i], i);
            }
            sort(prefix_sum.begin(), prefix_sum.end());
            
            long long closest_val = INT_MAX;
            int start = 0, end = 0;
            for (int i = 1; i < prefix_sum.size(); ++i) {
                long long val = prefix_sum[i].sum - prefix_sum[i-1].sum;
                if (val < closest_val) {
                    start = min(prefix_sum[i].pos, prefix_sum[i-1].pos) + 1;
                    end = max(prefix_sum[i].pos, prefix_sum[i-1].pos);
                    closest_val = val;
                }
            }
            ret.push_back(start); ret.push_back(end);
            return ret;
        }
    };
    
    

    思路3:Prefix Sum + BST / Tree Map

    扫描一遍数组,过程中构造每一个位置的前缀和sum并维护一颗以前缀和为key的二叉搜索树(Tree Map)。对于每个sum,在BST中查找与其最接近的前缀和,这一步须要考察大于等于sum的最小值,以及小于sum的最大值,可以调用std::maplower_bound()函数实现查找。

    Time complexity: O(n log n)
    Space complexity: O(n)

    该方法不仅适用于查找最接近0的子数组,也可以推广到查找最接近k的子数组,查找的时候改为查找sum - k即可。

    该方法出自: http://stackoverflow.com/questions/16388930/how-to-find-the-subarray-that-has-sum-closest-to-zero-or-a-certain-value-t-in-o

    class Solution {
    public:
        /**
         * @param nums: A list of integers
         * @return: A list of integers includes the index of the first number 
         *          and the index of the last number
         */
        vector<int> subarraySumClosest(vector<int> nums){
            vector<int> ret;
            int n = nums.size();
            if (n == 0) return ret;
            
            int start = 0, end = 0;
            
            map<long long, int> tree_map;
            tree_map[0] = -1;
            tree_map[LLONG_MIN] = -2; tree_map[LLONG_MAX] = n;  // barriers
            long long sum = 0, min_val = LLONG_MAX;
            for (int i = 0; i < n; ++i) {
                sum += nums[i];
                map<long long, int>::iterator it = tree_map.lower_bound(sum);
                // it->first >= sum, and with the minimal value in tree map
                long long dis = it->first - sum;
                if (dis < min_val) {
                    min_val = dis;
                    start = it->second + 1;
                    end = i;
                }
                
                it--; // it->first < sum, and with the maximal value in tree map
                dis = sum - it->first;
                if (dis < min_val) {
                    min_val = dis;
                    start = it->second + 1;
                    end = i;
                }
                
                tree_map[sum] = i;
            }
            
            ret.push_back(start); ret.push_back(end);
            return ret;
        }
    };
    
    
  • 相关阅读:
    VS2015, .NET 4.6, C# 6.0, F# 4.0等重量级产品正式上线
    Visual Studio 2015正式发布
    持续集成并不能消除 Bug,而是让它们非常容易发现和改正(转)
    如何用Excel直接查询Oracle中的数据(转)
    HTTP必知必会(转)
    二叉树的友好实现(转)
    TortoiseGit push失败原因小结(转)
    Android
    对JAVA Bean使用PropertyDescriptor反射调用JAVA方法
    关于MSHTML
  • 原文地址:https://www.cnblogs.com/ilovezyg/p/6408076.html
Copyright © 2020-2023  润新知