这道题是前天的每日一题,题目如下:
一个序列的 宽度 定义为该序列中最大元素和最小元素的差值。
给你一个整数数组 nums ,返回 nums 的所有非空 子序列 的 宽度之和 。由于答案可能非常大,请返回对 109 + 7 取余 后的结果。
子序列 定义为从一个数组里删除一些(或者不删除)元素,但不改变剩下元素的顺序得到的数组。例如,[3,6,2,7] 就是数组 [0,3,1,6,2,2,7] 的一个子序列。
解题思路
由题意可知,这道题是让求子序列最大值最小值的差值。由于子序列可以不连续,且本题只需要关注两个最值,因此给的数组的顺序也就并不重要了,就是说数组的顺序对题的答案并不会产生影响。所以我们可以对数组进行排序,如此一来就方便我们找最大值和最小值了。
nums.sort((a, b) => a - b);//对数组进行排序
排完序后,我们就可以开始计算宽度了,设两个数分别为最大值和最小值,那么这一组最大值最小值对应的所有子序列的宽度之和,等于这两个数的差值乘上排完序后两数之间数的个数的组合的数量。
所以,我们可以这样写
var sumSubseqWidths = function(nums) {
nums.sort((a, b) => a - b);
let sum = 0;
for(let i = 0;i<nums.length;i++){
for(let j=i+1;j<nums.length;j++){
let mul = Math.pow(2,j-i-1)% 1000000007;
sum = (sum + (nums[j]-nums[i])*mul% 1000000007) % 1000000007;
}
}
return sum;
};
这样写逻辑上没有毛病,在遇到一些不是那么长数据不是那么奇怪的案例的时候,都是能通过的。但是遇到那些特别长的案例,就需要用数学的方法优化一下了。
var sumSubseqWidths = function(nums) {
nums.sort((a, b) => a - b);
let sum = 0;
let x = nums[0], y = 2;
for (let j = 1; j < nums.length; j++) {
sum = (sum + nums[j] * (y - 1) - x) % 1000000007;
x = (x * 2 + nums[j]) % 1000000007;
y = y * 2 % 1000000007;
}
return sum;
};