LeetCode 327.インターバル合計の数(マルチセットバイナリ検索/マージソート)

1.タイトル

整数配列numsを指定して、[lower、upper]の間隔と数を返します(下限と上限を含む)。

区間とS(i、j)は、iとjを含む、num内のiからjまでの位置の要素の合計を表します(i≤j)。

注:
最も直感的なアルゴリズムの複雑さはO(n2)です。これに基づいてアルゴリズムを最適化してください。

示例:
输入: nums = [-2,5,-1], lower = -2, upper = 2,
输出: 3 
解释: 3个区间分别是: [0,0], [2,2], [0,2],它们表示的和分别为: -2, -1, 2

出典:LeetCodeリンク:https://leetcode-cn.com/problems/count-of-range-sum
著作権は控除ネットワークに属しています。商用転載の正式な許可書に連絡し、非商用転載の出典を明記してください。

2.問題解決

2.1動的プログラミングのタイムアウト

  • インターバル動的プログラミング、タイムアウトの例、複雑さが高すぎる O ( n 2 ) O(n ^ 2)
  • 整数オーバーフローの例[2147483647,-2147483648,-1,0] -1 0
    ここに画像の説明を挿入
class Solution {
public:
    int countRangeSum(vector<int>& nums, int lower, int upper) {
    	if(nums.size() == 0)
    		return 0;
    	int i, j, len, n = nums.size(), count=0;
    	vector<vector<long>> dp(n,vector<long>(n,0));
    	//区间[i,j]的和
    	for(i = 0; i < n; ++i)
    	{
    		dp[i][i] = nums[i];
    		if(lower<=dp[i][i] && dp[i][i]<=upper)
    			count++;
    	}
    	for(len = 1; len < n; ++len)
    	{
    		for(i = 0; i < n-len; ++i)
    		{
    			dp[i][i+len] = dp[i][i+len-1] + dp[i+len][i+len];
    			if(lower<=dp[i][i+len] && dp[i][i+len]<=upper)
    				count++;
    		}
    	}
    	return count;
    }
};

2.2バイナリサーチ

  • ソリューションを参照しください
  • プレフィックスと合計、 L s u m [ j ] s u m [ i ] U s u m [ j ] U s u m [ i ] s u m [ j ] L sum [j] -sum [i] \ le U Rightarrow sum [j] -U \ le sum [i] \ le sum [j] -L
  • j = 0の場合、上記の式sum [i] = 0、sum [i]は以前の合計、sum [j]は現在の合計と見なすことができます。
  • 各jポイントは終了間隔として、iからjの合計は範囲内にあります
  • 前の接頭辞を挿入してマルチセットに挿入し、順序付けして、バイナリで検索できます
  • 現在のプレフィックスでセット内のプレフィックス値を検索し、 s u m [ j ] sum [j] 上限と下限の範囲内 [ s u m [ j ] U , s u m [ j ] L ] [sum [j] -U、sum [j] -L]
class Solution {
public:
    int countRangeSum(vector<int>& nums, int lower, int upper) {
    	if(nums.size() == 0)
    		return 0;
    	multiset<long> s;
        s.insert(0);
    	int count = 0;
    	long sum = 0;
    	for(int i = 0; i < nums.size(); ++i)
    	{
    		sum += nums[i];
    		count += distance(s.lower_bound(sum-upper), s.upper_bound(sum-lower));
            s.insert(sum);
    	}
    	return count;
    }
};

ただし、上記のソリューションでは、セット内の距離(非ランダムアクセス)はO(n)時間の複雑さであるため、配列を使用して2つのエンドポイントをバイナリで見つけ、その差を計算する方が速くなります。
80ミリ秒14.4 MB

2.3マージソート

  • 実際、逆の次数を見つけるためのマージソートは、この問題の特別なケースです
  • 接頭辞の合計をマージしてソートします(最初の数値の頭に0が追加されることに注意してください)
  • マージするときは、左側の端点を修正します。右側に2つのポインタがあり、これを横断して検索します

コアコードセグメント:

		int i = l, jlo = mid+1, jup = mid+1;//右侧两个指针
        while(i <= mid)//遍历左侧的端点
        {	
            while(jlo <= r && sum[jlo]-sum[i] < lower)//[i,jlo]不在范围内
                jlo++;
            while(jup <= r && sum[jup]-sum[i] <= upper)//[i,jup]在范围内
                jup++;
            //最后 [jlo,jup) 为在范围内的右端点
            count += jup-jlo;//计数
            i++;//遍历下一个左端点
        }
class Solution {
    vector<long> temp;
public:
    int countRangeSum(vector<int>& nums, int lower, int upper) {
        if(nums.size() == 0)
            return 0;
        vector<long> sum(nums.size()+1, 0);
        temp.resize(nums.size()+1);
        for(int i = 1; i < sum.size(); ++i)
            sum[i] = sum[i-1] + nums[i-1];
        return mergeSort(sum,0,sum.size()-1,lower,upper);
    }

    int mergeSort(vector<long>& sum, int l, int r, int lower, int upper)
    {
        if(l >= r)
            return 0;
        int mid = l+((r-l)>>1), count = 0;
        count += mergeSort(sum, l, mid, lower, upper);
        count += mergeSort(sum, mid+1, r, lower, upper);
        int i = l, jlo = mid+1, jup = mid+1;
        while(i <= mid)
        {
            while(jlo <= r && sum[jlo]-sum[i] < lower)
                jlo++;
            while(jup <= r && sum[jup]-sum[i] <= upper)
                jup++;
            count += jup-jlo;
            i++;
        }
        //合并,跟归并排序一致
        i = l; int j = mid+1, k = 0;
        while(i <= mid && j <= r)
        {
            if(sum[i] <= sum[j])
                temp[k++] = sum[i++];
            else
                temp[k++] = sum[j++];
        }
        if(i <= mid)
            while(i <= mid)
                temp[k++] = sum[i++];
        else
            while(j <= r)
                temp[k++] = sum[j++];
        for(i = 0, j = l; i < k; ++i)
            sum[l++] = temp[i];
        return count;
    }
};

28 ms 12.2 MB

公開された889件の元の記事 2657 件の賞賛 470,000回

おすすめ

転載: blog.csdn.net/qq_21201267/article/details/105636619