背景
将为您提供一个包含正数和负数的数组。 您将在数组中找到一个等于某个值X的子数组。 输入是数组和X值。 输出是子数组的开始和结束索引。
示例
Array = [2,6,0,9,7,3,1,4,1,10]
X = 15
Output = [1,3]
以下是我在geeks4geeks上找到的代码
public static void subArraySum(int[] arr, int n, int sum) {
//cur_sum to keep track of cummulative sum till that point
int cur_sum = 0;
int start = 0;
int end = -1;
HashMap<Integer, Integer> hashMap = new HashMap<>();
for (int i = 0; i < n; i++) {
cur_sum = cur_sum + arr[i];
//check whether cur_sum - sum = 0, if 0 it means
//the sub array is starting from index 0- so stop
if (cur_sum - sum == 0) {
start = 0;
end = i;
break;
}
//if hashMap already has the value, means we already
// have subarray with the sum - so stop
if (hashMap.containsKey(cur_sum - sum)) {
start = hashMap.get(cur_sum - sum) + 1;
end = i;
break;
}
//if value is not present then add to hashmap
hashMap.put(cur_sum, i);
}
// if end is -1 : means we have reached end without the sum
if (end == -1) {
System.out.println("No subarray with given sum exists");
} else {
System.out.println("Sum found between indexes "
+ start + " to " + end);
问题
总的来说,我能够理解为什么该解决方案有效。条件为cur_sum - sum == 0
的第一个块对我来说完全有意义。
但是,我对hashMap.containsKey(cur_sum - sum)
支票为什么起作用感到非常困惑。我知道它每次都有效,但是我想了解它背后的数学意义。我在一个采访博客中读到,它使用了通用的diff技术,但是我不确定这如何应用于此。
在准备面试时,我并不是要记住这种解决方案,而是要对它为什么起作用有一个坚实的了解。
我花了数小时来提出示例,并通过手工对其进行跟踪,并且解决方案有效,但是我无法理解该技术为何起作用,因此我拒绝盲目地记住它。
例如,如果我们只谈论带有正数的数组,那么我知道可以使用“滑动窗口”技术,并且我能够完全理解。扩展窗口时,总和变大;关闭窗口时,总和变小。上面的问题不是这种情况,因此将对您有所帮助。
答案 0 :(得分:0)
sum
-我们要达到的数字
cur_sum
-是到目前为止数组中所有元素的总和
说cur_sum - sum = x
每一步我们都从头开始更新cur_sum
,如果使您感到困惑的条件评估为false
,我们将更新哈希图并继续。
到目前为止很好。
现在,为什么要查看x
是否已经在哈希图中?
对此的答案是,如果存在先前的索引i
,直到该索引(包括该索引)之前的所有元素的总和为x
,这意味着从索引i+1
开始到当前索引为止,我们有一个子数组,其总和为:cur_sum - x
由于我们已经知道cur_sum - sum = x
,这意味着从i+1
开始到当前索引结束的子数组的总和就是sum
。
让我们以您发布的示例为例:
Array = [2,6,0,9,7,3,1,4,1,10]
X = 15
Output = [1,3]
让我们迭代数组:
索引0:总和2 =>使用(0:2)更新地图
索引1:sum 2 + 6 = 8 =>使用(1:8)更新地图
索引2:sum 2 + 6 + 0 = 8 =>使用(2:8)更新地图
索引3:总和2 + 6 + 0 + 9 = 17 =>
但是现在我们可以看到地图已经包含17-15 = 2(0:2) 因此,我们知道子数组的总和从索引1开始(索引1从(0:2)开始在零之后),并在当前索引处结束:3-此子数组的总和为15。