LeetCode Summary Ranges

228. Summary Ranges2

Given a sorted integer array without duplicates, return the summary of its ranges.

Example 1:

Input:  [0,1,2,4,5,7]
Output: ["0->2","4->5","7"]
Explanation: 0,1,2 form a continuous range; 4,5 form a continuous range.

Example 2:

Input:  [0,2,3,4,6,8,9] Output: ["0","2->4","6","8->9"] Explanation: 2,3,4 form a continuous range; 8,9 form a continuous range.28. Summary Ranges

给定一个数组,把数组汇总成若干个连续的区间,以字符串的形式给出。
方法是判断两个相邻的数之差是否为1,不是1则前面的构成一个区间,转换为字符串输出。判断前一个区间只有一个数还是有多个数的方法是区间的起止位置i,j是否相差1,如果是,则只有一个数,否则有多个数。
注意最后一个区间需要在while循环外部判断。代码如下:

class Solution {
public:
    vector<string> summaryRanges(vector<int>& nums)
    {
        vector<string> ans;
        int n = nums.size(), i = 0, j = 1;
        if (n == 0)
            return ans;
        while (j < n) {
            if (nums[j] – nums[j – 1] == 1)
                ++j;
            else {
                if (j – i == 1)
                    ans.push_back(to_string(nums[i]));
                else
                    ans.push_back(to_string(nums[i]) + "->" + to_string(nums[j – 1]));
                i = j++;
            }
        }
        if (j – i == 1)
            ans.push_back(to_string(nums[i]));
        else
            ans.push_back(to_string(nums[i]) + "->" + to_string(nums[j – 1]));
        return ans;
    }
};

本代码提交AC,用时0MS。
还有一种解法利用了二分查找的思路,很有意思,参考讨论区
假如给定的数组是[1,2,3,4,5,…,10000,20000],如果还是用上述方法,时间复杂度是O(n),至少需要遍历一遍数组。但是数组的前10000个数是严格有序且连续递增的,如果能把这一部分识别出来,直接转换为”1->10000″,则速度会大大提高。
二分查找的思路是,对于给定区间[a,…,b],假设其在数组中的下标起点分别为[i,…,j],则如果b-a==j-i,说明[a,b]之间是类似上面的[1,2,…,10000]的情况的,因为数组中没有重复元素,所以区间有j-i个元素,且元素最大值和最小值的差b-a也是j-i,说明他们是一个连续的有序区间,我们直接把这个区间转换为”a->b”。
否则如果j-i!=b-a,则取中点mid=(i+j)/2,递归在[i,mid]和[mid+1,j]进行。
有一种情况需要注意的是,分割出来的区间可能需要合并,比如[1,2,3,4,5,6,8],初始i[1,..,8]不满足b-a==j-i,所以递归在[1,2,3,4]和[5,6,8]进行。左边转换为了”1->4″,右边还需要递归,假设转换为了”5->6″和”8″,那么”1->4″和”5->6″是需要合并的。所以我们在插入”5->6″时,看看之前得到的区间”1->4″的end是否和当前区间”5->6″的start只差1,如果是,则需要合并,更新之前区间的end为现在要插入区间的end,变成了”1->6″。
完整代码如下:

class Solution {
private:
    struct Range {
        int start, end;
        Range(int s, int e)
            : start(s)
            , end(e){};
    };
    void add2Ans(int a, int b, vector<Range>& ranges)
    {
        if (ranges.empty() || ranges[ranges.size() – 1].end != a – 1) {
            ranges.push_back(Range(a, b));
        }
        else {
            ranges[ranges.size() – 1].end = b;
        }
    }
    void helper(vector<int>& nums, int start, int end, vector<Range>& ranges)
    {
        if (start == end || end – start == nums[end] – nums[start]) {
            add2Ans(nums[start], nums[end], ranges);
            return;
        }
        int mid = start + (end – start) / 2;
        helper(nums, start, mid, ranges); // 先左区间
        helper(nums, mid + 1, end, ranges); // 后右区间
    }

public:
    vector<string> summaryRanges(vector<int>& nums)
    {
        vector<string> ans;
        if (nums.empty())
            return ans;
        vector<Range> ranges;
        helper(nums, 0, nums.size() – 1, ranges);
        for (const auto& r : ranges) {
            if (r.start == r.end)
                ans.push_back(to_string(r.start));
            else
                ans.push_back(to_string(r.start) + "->" + to_string(r.end));
        }
        return ans;
    }
};

本代码提交AC,用时0MS。
这个代码在数据有很多连续区间时,接近O(lgn)的复杂度。但是当数据全是不连续的时,比如[1,3,5,7,9],则只有到递归到最深层start==end(即针对单个数字)时,才得到一个区间,所以退化为O(n)的算法。
如果再follow up可能有重复元素时,上述二分查找的方法就不管用了,因为属于一个区间的不一定满足b-a==j-i,比如[1,2,2,2,3],b-a=2,而j-i=4。此时只能用第一种O(n)的方法:

class Solution {
public:
    vector<string> summaryRanges(vector<int>& nums)
    {
        vector<string> ans;
        if (nums.empty())
            return ans;
        int i = 0, j = 1, n = nums.size();
        while (j < n) {
            while (j < n && (nums[j] == nums[j – 1] || nums[j] – nums[j – 1] == 1))
                ++j; // 考虑重复元素
            if (j >= n)
                break;
            if (j – 1 == i)
                ans.push_back(to_string(nums[i]));
            else
                ans.push_back(to_string(nums[i]) + "->" + to_string(nums[j – 1]));
            i = j++;
        }
        if (j – 1 == i)
            ans.push_back(to_string(nums[i]));
        else
            ans.push_back(to_string(nums[i]) + "->" + to_string(nums[j – 1]));
        return ans;
    }
};

本代码提交AC,用时0MS。

二刷。更加鲁邦容易理解的代码:

class Solution {
public:
	vector<string> summaryRanges(vector<int>& nums) {
		vector<vector<int>> ranges;
		int i = 0, n = nums.size();
		while (i < n) {
			int j = i + 1;
			while (j < n&&nums[j] == nums[j - 1] + 1)++j;
			ranges.push_back({ nums[i],nums[j - 1] });
			i = j;
		}
		vector<string> ans;
		for (int i = 0; i < ranges.size(); ++i) {
			if (ranges[i][0] == ranges[i][1]) {
				ans.push_back(to_string(ranges[i][0]));
			}
			else {
				ans.push_back(to_string(ranges[i][0]) + "->" + to_string(ranges[i][1]));
			}
		}
		return ans;
	}
};

本代码提交AC,用时0MS。

Leave a Reply

Your email address will not be published. Required fields are marked *