问题 等值首尾和
假设有一个数组x[],它有n个元素,每一个都大于零;称x[0] + x[1] + … + x[i]为前置和(Prefix Sum),
而x[j] + x[j+1] + … + x[n-1]为后置和(Suffix Sum)。试编写一个程序,求出x[]中有多少组相同的前置和与后置和。
最容易想到的当然是把前置和与后置和分别算出来放到两个长度为n的数组里面,再检索两个数组相同的元素。
但我们可以注意到每个元素都大于0,所以前置和与后置和都是递增的。于是我们可以:
设置前后两个游标
前置和大于后置和,则后游标向前移位,同时后置和加上相应元素;
同理,前置和小于后置和,前游标向后移位,前置和加上相应元素;
两者相等,则统计数目加1,同时执行两步操作。
直到某一个或两个游标到达终点
下面是我的代码
1 // 最差情况,交替上升,执行代码 2n 次,n为数组长度 2 int GetHeadTail(int * a, int lenA) 3 { 4 int count = 0; // 统计数 5 int prefixSum = 0; // 前置和 6 int suffixSum = 0; // 后置和 7 int head = 0; // 前游标 8 int tail = lenA - 1; // 后游标 9 while (head < lenA && tail >= 0) 10 { 11 prefixSum += a[head++]; 12 suffixSum += a[tail--]; 13 while (prefixSum < suffixSum && head < lenA) 14 { // 向后遍历 15 prefixSum += a[head++]; 16 } 17 while (suffixSum < prefixSum && tail >= 0) 18 { // 向前遍历 19 suffixSum += a[tail--]; 20 } 21 if (prefixSum == suffixSum) 22 { 23 count++; 24 } 25 } 26 return count; 27 }
本来用的for循环,逻辑一样,却导致代码看上去很凌乱。后来改成了while循环,简洁多了。因此还沾沾自喜,再看看作者的答案
1 int head_tail(int x[], int n) 2 { 3 int prefix = 0, suffix = 0; 4 int prefix_idx = 0, suffix_idx = n - 1; 5 int count = 0; 6 7 while (prefix_idx < n && suffix_idx >= 0) 8 { 9 if (prefix < suffix) 10 prefix += x[prefix_idx++]; 11 else if (suffix < prefix) 12 suffix += x[suffix_idx--]; 13 else { 14 count++; 15 suffix += x[suffix_idx--]; 16 prefix += x[prefix_idx++]; 17 } 18 } 19 return count; 20 }
作者的逻辑清晰,代码整洁。 而我的还有一个bug————可能会漏掉前后全部加完的情况。
问题及答案来源————《C语言名题精选百则技巧篇》问题1.5