数据结构与算法--子数组和为0

来源:互联网 发布:c语言中status 编辑:程序博客网 时间:2024/06/13 22:11

问题:子数组和为0

Given an integer array, find a subarray where the sum of numbers is zero.Your code should return the index of the first number and the index of the last number.ExampleGiven [-3, 1, 2, -3, 4], return [0, 2] or [1, 3].NoteThere is at least one subarray that it's sum equals to zero.

解题思路:

题目中的对象是分析子串和,那么我们先从常见的对数组求和出发,f(i)=∑​0​i​​nums[i] 表示从数组下标 0 开始至下标 i 的和。子串和为0,也就意味着存在不同的 i​1​​ 和 i​2​​ 使得 f(i​1​​)−f(i​2​​)=0, 等价于 f(i​1​​)=f(i​2​​). 思路很快就明晰了,使用一 vector 保存数组中从 0 开始到索引i的和,在将值 push 进 vector 之前先检查 vector 中是否已经存在,若存在则将相应索引加入最终结果并返回。

解法一:哈希表

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 subarraySum(vector nums){        vector result;        // curr_sum for the first item, index for the second item        map hash;        hash[0] = 0;        int curr_sum = 0;        for (int i = 0; i != nums.size(); ++i) {            curr_sum += nums[i];            if (hash.find(curr_sum) != hash.end()) {                result.push_back(hash[curr_sum]);                result.push_back(i);                return result;            } else {                hash[curr_sum] = i + 1;            }        }        return result;    }};

源码分析

为了将curr_sum == 0的情况也考虑在内,初始化哈希表后即赋予 <0, 0>. 给 hash赋值时使用i + 1, push_back时则不必再加1.

由于 C++ 中的map采用红黑树实现,故其并非真正的「哈希表」,C++ 11中引入的unordered_map用作哈希表效率更高,实测可由1300ms 降至1000ms.

复杂度分析

遍历求和时间复杂度为 O(n), 哈希表检查键值时间复杂度为 O(logL), 其中 L 为哈希表长度。如果采用unordered_map实现,最坏情况下查找的时间复杂度为线性,最好为常数级别。

解法二:排序

除了使用哈希表,我们还可使用排序的方法找到两个子串和相等的情况。这种方法的时间复杂度主要集中在排序方法的实现。由于除了记录子串和之外还需记录索引,故引入pair记录索引,最后排序时先按照sum值来排序,然后再按照索引值排序。如果需要自定义排序规则可参考sort_pair_second.

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 subarraySum(vector nums){        vector result;        if (nums.empty()) {            return result;        }        const int num_size = nums.size();        vector <pair<int,int>> sum_index(num_size + 1);        for (int i = 0; i != num_size; ++i) {            sum_index[i + 1].first = sum_index[i].first + nums[i];            sum_index[i + 1].second = i + 1;        }        sort(sum_index.begin(), sum_index.end());        for (int i = 1; i < num_size + 1; ++i) {            if (sum_index[i].first == sum_index[i - 1].first) {                result.push_back(sum_index[i - 1].second);                result.push_back(sum_index[i].second - 1);                return result;            }        }        return result;    }};

源码分析

没啥好分析的,注意好边界条件即可。这里采用了链表中常用的「dummy」节点方法,pair排序后即为我们需要的排序结果。这种排序的方法需要先求得所有子串和 然后再排序,最后还需要遍历排序后的数组,效率自然是比不上哈希表。但是在某些情况下这种方法有一定优势。

复杂度分析

遍历求子串和,时间复杂度为 O(n), 空间复杂度 O(n). 排序时间复杂度近似 O(nlogn), 遍历一次最坏情况下时间复杂度为 O(n). 总的时间复杂度可近似为 O(nlogn). 空间复杂度 O(n).

阅读全文
0 0
原创粉丝点击