使用地图查找具有给定总和的子阵列(带负数)

时间:2016-09-04 22:25:29

标签: c++ arrays algorithm

考虑以下问题陈述:

  

给定一个未排序的整数数组,找到一个添加到给定数字的子数组。如果有多个子数组,其中sum为给定数字,则打印其中任何一个。

Examples:

Input: arr[] = {1, 4, 20, 3, 10, 5}, sum = 33
Ouptut: Sum found between indexes 2 and 4

Input: arr[] = {10, 2, -2, -20, 10}, sum = -10
Ouptut: Sum found between indexes 0 to 3

Input: arr[] = {-10, 0, 2, -2, -20, 10}, sum = 20
Ouptut: No subarray with given sum exists

在这个site上,建议使用以下线性时间解决方案,当算法迭代数组时,使用map来存储当前子集的总和:

// Function to print subarray with sum as given sum
void subArraySum(int arr[], int n, int sum)
{
    // create an empty map
    unordered_map<int, int> map;

    // Maintains sum of elements so far
    int curr_sum = 0;

    for (int i = 0; i < n; i++)
    {
        // add current element to curr_sum
        curr_sum = curr_sum + arr[i];

        // if curr_sum is equal to target sum
        // we found a subarray starting from index 0
        // and ending at index i
        if (curr_sum == sum)
        {
            cout << "Sum found between indexes "
                 << 0 << " to " << i << endl;
            return;
        }

        // If curr_sum - sum already exists in map
        // we have found a subarray with target sum
        if (map.find(curr_sum - sum) != map.end())
        {
            cout << "Sum found between indexes "
                 << map[curr_sum - sum] + 1
                 << " to " << i << endl;
            return;
        }

        map[curr_sum] = i;
    }

    // If we reach here, then no subarray exists
    cout << "No subarray with given sum exists";
}
// Driver program to test above function
int main()
{
    int arr[] = {10, 2, -2, -20, 10};
    int n = sizeof(arr)/sizeof(arr[0]);
    int sum = -10;

    subArraySum(arr, n, sum);

    return 0;
}

使用帖子中提供的测试用例,第二个if语句检查是否从未输入current_sum-sum。相反,找到总和并在第一个if语句中打印。 这里有一些混乱点

1:在地图中查找current_sum-sum的目的是什么?

2:在哪种情况下,甚至会输入第二个if语句来将地图用于解决问题?

3 个答案:

答案 0 :(得分:16)

    curr_sum = curr_sum + arr[i];

下面:

  

curr_sum保存从arr [0]

开始的所有条目的总和(最多为arr [i])
if (curr_sum == sum)
{
    cout << "Sum found between indexes "
         << 0 << " to " << i << endl;
    return;
}

下面:

  

if条件检查curr_sum是否等于给定的总和。请记住,curr_sum保存从arr [0]开始的总和。

因此,如果结果子阵列的索引是2和4,则上述条件不够。

Input: arr[] = {1, 4, 20, 3, 10, 5}, sum = 33
Ouptut: Sum found between indexes 2 and 4

在那个例子中,

  

当i = 4时,curr_sum将为38.但如果你取1和4(arr [0]和arr [1]这是一个子阵列),你得到33。

    if (map.find(curr_sum - sum) != map.end())
    {
        cout << "Sum found between indexes "
             << map[curr_sum - sum] + 1
             << " to " << i << endl;
        return;
    }

    map[curr_sum] = i;

上面的代码就是这么做的。

 map[curr_sum] = i;

map将索引值(i)与子数组sum(索引0和i之间的arr之和)存储为关键字。因此,对于该示例,它将是:

  • 地图[1] = 0
  • 地图[5] = 1
  • map [25] = 2
  • map [28] = 3
  • 地图[38] = 4

此代码:

    map.find(curr_sum - sum)

在地图中搜索是否有带密钥的条目(curr_sum - sum)。

  

在我们的例子中,当i = 4时,curr_sum将是38,并且给定的总和将是33.如果我们消除子数组arr [0,2],我们得到33.因此,map.find(curr_sum - sum) =&GT; map.find(38-33)是一个命中,因为我们有入口图[5] = 1。

希望这能让你清楚

答案 1 :(得分:9)

有两种情况:

  1. 从索引0到n的总和是我们正在寻找的数字。 cur_sum是所有索引的运行总和,因此只需检查该变量即可。
  2. 从索引mn的总和是我们正在寻找的数字。如果是这种情况,则存在m,使得(从0到n的总和)减去(从0到m的总和)是我们正在寻找的总和对于。所有这些中间总和都存储在地图中,这就是为什么我们要查找cur_sum - sum - 如果这个差异对应于某个部分总和,那么我们就完成了。

    第二种情况的一个例子是你问题中的第一个测试用例 - 在{1, 4, 20, 3, 10, 5}中寻找33。在n==4,我们cur_sum 38。但5在地图中是前两个指数的总和。

答案 2 :(得分:0)

如果数组中没有负整数,则不需要映射。在这种情况下你可以保持一个起始变量,从currSum中减去arr [start]并在currSum&gt;中增加1开始。总和。

如果数组中可能存在负整数,则可以使用带有映射的此方法。

尝试此示例以了解解决方案:

-5,2,10,20,3,8

sum = 33

答案:索引2到4之间的总和