1818绝对差值和
思路
拿到题首先简单分析一下:
- 因为nums1和nums2的长度都是n,而 1 ≤ n ≤ 1 0 5 1\le n \le 10^5 1≤n≤105,所以最多只能使用 O ( n l o g n ) O(nlogn) O(nlogn)的算法;
- nums1中最多只能进行一次元素替换操作,可以不进行替换。
因为求的是——进行最多一次替换后的最小绝对差值和,所以
- 若原本的绝对差值和就是0,那就不用进行替换了;
- 如果需要替换,又因为只能替换一次,那就需要找到可以最大程度减少绝对差值和的一次替换,然后在原本的绝对值和的基础上减去这一次的减少量即可;
所以得到如下的算法步骤:
- 首先计算不进行替换的
绝对差值和ans
,如果此时的ans=0
,则直接返回0
; - 否则,利用
数组sub记录每一对的绝对差值
; - 因为只能对nums1中的元素使用替代操作,所以
将nums1中的元素存入TreeSet
中,方便后续进行处理(有很多人使用的是二分查找来寻找合适的值,因为前几天曾经使用过TreeMap的floorKey做过题,这次使用TreeSet的floor和ceiling方法来做
,但是这样的时间复杂度高了很多); - 无视题目要求,可以进行无限次替换操作,
使用TreeSet中的ceiling(i)和floor(i)找到nums2中当前元素对应nums1中最接近的两个元素
,求出nums2中每一个元素对应最佳的绝对差值
,并与sub中对应的值相减
,从而找到最大的减少量max
; - 使用
(ans-max+mod)%mod
,然后返回即可。
因为TreeSet增删改查的时间复杂度都是
O
(
l
o
g
n
)
O(logn)
O(logn),再加上嵌套的循环,所以总的时间复杂度为
O
(
n
l
o
g
n
)
O(nlogn)
O(nlogn)
空间复杂度为
O
(
2
n
)
O(2n)
O(2n),其中使用sub存储元素的绝对差值、使用Treeset存储nums1的元素分别使用了
O
(
n
)
O(n)
O(n)的空间
代码
class Solution {
public int minAbsoluteSumDiff(int[] nums1, int[] nums2) {
//因为n的长度为1e5所以最多只能使用nlogn的时间复杂度 n更好
//最多只能替换一个元素
int mod = (int) 1e9+7;
int len = nums1.length;
int ans = 0;
int[] sub = new int[len];
TreeSet<Integer> ts = new TreeSet<>();
for (int i=0;i<len;i++){
sub[i] = nums1[i]-nums2[i]>0?nums1[i]-nums2[i]:nums2[i]-nums1[i];
ans = (ans + sub[i])%mod;
ts.add(nums1[i]);
}
if (ans==0)
return 0;
int max = Integer.MIN_VALUE;
for (int i=0;i<len;i++)
{
int c = ts.ceiling(nums2[i])==null?Integer.MAX_VALUE:ts.ceiling(nums2[i]);
int f = ts.floor(nums2[i])==null?Integer.MAX_VALUE:ts.floor(nums2[i]);
int per = Math.abs(c-nums2[i])>Math.abs(f-nums2[i])?Math.abs(f-nums2[i]):Math.abs(c-nums2[i]);
max = Math.max(sub[i]-per,max);
}
return (ans-max + mod) % mod;
}
}