1. 解题思路
这一题和题目3507. Minimum Pair Removal to Sort Array I本质上是同一道题目,唯一的区别在于时间复杂度的要求上面。
对于题目3507,我们可以通过一个二重循环进行实现,时间复杂度为 O ( N 2 ) O(N^2) O(N2),但是放到这里就显然不行了,我们最多只能允许一个时间复杂度为 O ( N ⋅ l o g N ) O(N\cdot logN) O(N⋅logN)的算法实现。
而这里难度会有两个:
- 找到当前最小的相邻元素的和,来进行元素的替换;
- 如何判断当前序列是否已经变成了一个单调非减的序列。
正常来说,这俩都需要 O ( N ) O(N) O(N)的时间复杂度,但是,这里,我们需要对其进行优化。
首先,对于第一个问题,找到最小的相邻元素的问题,我们可以通过二分搜索的方式实现,找最小的元素,其时间复杂度就是 O ( l o g N ) O(logN) O(logN),但是问题在于我们在合并之后其前后的元素会同时被调整,因此我们需要维护当前的数组,然后对其前后的元素的和进行同步调整。
而对于第二部分的问题,对于单调非减序列的判断,正常确实需要 O ( N ) O(N) O(N)的时间复杂度,但是这里我们可以通过维护当前数组当中相邻元素的逆序元素的个数来进行判断,对于单调非减序列,其逆序元素的总数必然为0。
因此,这就变成了一个数组的维护问题了。
唯一比较坑的是,如果直接使用二分查找的方式,会出现超时的情况,需要使用堆排来对其进行进一步的优化。
2. 代码实现
给出最终的python代码实现如下:
class Solution:
def minimumPairRemoval(self, nums: List[int]) -> int:
n = len(nums)
index = [i for i in range(n)]
pairs = [(nums[i] + nums[i+1], i) for i in range(n-1)]
heapq.heapify(pairs)
remove = set()
reverse_cnt = 0
for i in range(n-1):
if nums[i] > nums[i+1]:
reverse_cnt += 1
m = n
while reverse_cnt > 0:
s, idx = heapq.heappop(pairs)
if (s, idx) in remove:
continue
remove.add((s, idx))
i = bisect.bisect_left(index, idx)
# replace element
x, y = nums[i], nums[i+1]
xi, yi = index[i], index[i+1]
nums.pop(i+1)
nums[i] = x+y
index.pop(i+1)
m -= 1
if x > y:
reverse_cnt -= 1
# maintain pre element
if i-1 >= 0:
t, ti = nums[i-1], index[i-1]
remove.add((t+x, ti))
heapq.heappush(pairs, (t+x+y, ti))
if (t+x+y, ti) in remove:
remove.remove((t+x+y, ti))
if t > x:
reverse_cnt -= 1
if t > x+y:
reverse_cnt += 1
# maintain post element
if i+1 < m:
t, ti = nums[i+1], index[i+1]
remove.add((t+y, yi))
heapq.heappush(pairs, (t+x+y, xi))
if (t+x+y, xi) in remove:
remove.remove((t+x+y, xi))
if y > t:
reverse_cnt -= 1
if x+y > t:
reverse_cnt += 1
return n - m
提交代码评测得到:耗时7765ms,占用内存95.4MB。