使用映射查找具有给定和(带有负数)的子数组

Using a map to find subarray with given sum (with negative numbers)

本文关键字:数组 映射 查找      更新时间:2023-10-16

考虑以下问题陈述:

给定一个未排序的整数数组,找出一个与给定数字相加的子数组。如果有多个以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

在这个站点上,建议使用以下线性时间解决方案,其中包括使用映射来存储算法遍历数组时当前子集的和:

// 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;
}

对于post中提供的测试用例,检查current_sum - sum是否的第二个if语句永远不会输入。而是在第一个if语句中找到和并打印出来。这里有一些混淆的地方:

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

2:在什么情况下,甚至需要输入第二个if语句来使用映射来解决问题?

    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以子数组sum(索引0和索引i之间的arr之和)作为键存储索引值i。所以对于这个例子,它将是:

  • 地图[1]= 0
  • 地图[5]= 1
  • 地图[25]= 2
  • 地图[28]= 3
  • 地图[38]= 4
这个代码:

    map.find(curr_sum - sum)

搜索映射是否有一个键为(curr_sum - sum)的条目。

在我们的示例中,当i = 4时,curr_sum将为38,而给定的sum将为33。如果我们消去子数组arr[0,2],我们得到33。因此,地图。Find (curr_sum - sum) => map. Find(38-33)是命中的,因为我们有条目map[5] = 1。

希望你明白了

有两种情况:

  1. 从索引0到n的和就是我们要找的数字。cur_sum是所有指标的运行和,所以只需检查该变量即可。

  2. 指数mn的和就是我们要找的数字。如果是这样,那么存在一个m,使得(从0到n的和)减去(从0到m的和)就是我们要找的和。所有这些中间和都存储在映射中,所以这就是为什么我们要寻找cur_sum - sum——如果这个差对应于某个部分和,那么我们就完成了。

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

如果数组中没有负整数,则不需要映射。在这种情况下,你可以只维护一个start变量,从currSum中减去arr[start],并在currSum> sum时将start增加1。

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

试试这个例子来理解解决方案:

-5、2、10、20、3、8

sum = 33

答案:索引2到索引4的和