Python 线段树算法解决 LeetCode 2243. 形成目标数组的最小操作数
Python 线段树算法解决 LeetCode 2243. 形成目标数组的最小操作数
题目链接: https://leetcode-cn.com/problems/minimum-operations-to-make-array-sequential/
题目描述:
给你一个包含若干互不相同整数的数组 nums ,你需要执行以下操作直到数组为空 :
- 如果数组中第一个元素是当前数组中的最小值,则删除它。
- 否则,将第一个元素移动到数组的末尾。
请你返回需要多少个操作使 nums 为空。
示例 1:
输入:nums = [5,1,3]
输出:3
解释:需要执行 3 次操作才能使 nums 为空:
1. 删除 1 ,得到 [5,3] 。
2. 删除 3 ,得到 [5] 。
3. 删除 5 ,得到 [] 。
示例 2:
输入:nums = [1,2,3,5,4]
输出:5
解释:需要执行 5 次操作才能使 nums 为空:
1. 移动下标 1 处的 2 到末尾,得到 [1,3,5,4,2] 。
2. 删除 1 ,得到 [3,5,4,2] 。
3. 删除 3 ,得到 [5,4,2] 。
4. 删除 5 ,得到 [4,2] 。
5. 删除 4 ,得到 [] 。
提示:
- 1 <= nums.length <= 10^5
- 1 <= nums[i] <= 10^9
- nums 中的所有元素互不相同
解题思路:
模拟题。题目描述给出了两个操作:
- 删除数组中的最小值。
- 将数组中的第一个元素移动到末尾。
可以发现,删除最小值操作和数组中的最小值有关,为了快速找到数组中的最小值,可以使用线段树(也可以用堆,但是线段树更为常用)。
对于数组元素的移动,可以使用双指针。用一个指针记录数组中的最小值,然后另一个指针向后遍历,如果找到了比最小值更小的元素,就将这个元素移到最后,同时更新最小值的位置。
代码实现:
首先实现线段树。对于每个节点,维护它的区间最小值和最小值的位置。对于每个节点的左右儿子,它的区间分别为 [left, mid] 和 [mid+1, right]。
class SegmentTree:
def __init__(self, n):
self.tree = [[0, 0] for _ in range(n * 4)]
def build(self, node, left, right, nums):
if left == right:
self.tree[node][0] = nums[left]
self.tree[node][1] = left
return
mid = (left + right) // 2
self.build(node * 2, left, mid, nums)
self.build(node * 2 + 1, mid + 1, right, nums)
if self.tree[node * 2][0] < self.tree[node * 2 + 1][0]:
self.tree[node][0] = self.tree[node * 2][0]
self.tree[node][1] = self.tree[node * 2][1]
else:
self.tree[node][0] = self.tree[node * 2 + 1][0]
self.tree[node][1] = self.tree[node * 2 + 1][1]
def query(self, node, left, right, qleft, qright):
if qleft <= left and right <= qright:
return self.tree[node]
mid = (left + right) // 2
res = [float('inf'), -1]
if qleft <= mid:
left_res = self.query(node * 2, left, mid, qleft, qright)
if left_res[0] < res[0]:
res[0] = left_res[0]
res[1] = left_res[1]
if qright > mid:
right_res = self.query(node * 2 + 1, mid + 1, right, qleft, qright)
if right_res[0] < res[0]:
res[0] = right_res[0]
res[1] = right_res[1]
return res
然后是主函数的实现。首先建立线段树,然后用双指针模拟操作。用 left 指针记录数组中的最小值的位置,用 right 指针遍历数组。如果 right 指针找到了比最小值更小的元素,就将这个元素移到最后,同时更新最小值的位置。如果 right 指针没有找到比最小值更小的元素,就将最小值删除,并更新最小值的位置。
def minOperations(nums):
n = len(nums)
tree = SegmentTree(n)
tree.build(1, 0, n - 1, nums)
left = tree.query(1, 0, n - 1, 0, n - 1)[1]
right = (left + 1) % n
ans = 0
while left != -1:
if nums[right] < nums[left]:
nums.append(nums.pop(right))
ans += 1
left = (left - 1) % n
else:
if left == right:
left_res = tree.query(1, 0, n - 1, 0, left - 1)
right_res = tree.query(1, 0, n - 1, right + 1, n - 1)
if left_res[0] < right_res[0]:
nums.pop(left)
ans += 1
left = left_res[1]
else:
nums.pop(right)
ans += 1
left = right_res[1]
else:
nums.pop(left)
ans += 1
left = tree.query(1, 0, n - 1, 0, left - 1)[1]
right = (left + 1) % n
return ans
完整代码:
class SegmentTree:
def __init__(self, n):
self.tree = [[0, 0] for _ in range(n * 4)]
def build(self, node, left, right, nums):
if left == right:
self.tree[node][0] = nums[left]
self.tree[node][1] = left
return
mid = (left + right) // 2
self.build(node * 2, left, mid, nums)
self.build(node * 2 + 1, mid + 1, right, nums)
if self.tree[node * 2][0] < self.tree[node * 2 + 1][0]:
self.tree[node][0] = self.tree[node * 2][0]
self.tree[node][1] = self.tree[node * 2][1]
else:
self.tree[node][0] = self.tree[node * 2 + 1][0]
self.tree[node][1] = self.tree[node * 2 + 1][1]
def query(self, node, left, right, qleft, qright):
if qleft <= left and right <= qright:
return self.tree[node]
mid = (left + right) // 2
res = [float('inf'), -1]
if qleft <= mid:
left_res = self.query(node * 2, left, mid, qleft, qright)
if left_res[0] < res[0]:
res[0] = left_res[0]
res[1] = left_res[1]
if qright > mid:
right_res = self.query(node * 2 + 1, mid + 1, right, qleft, qright)
if right_res[0] < res[0]:
res[0] = right_res[0]
res[1] = right_res[1]
return res
def minOperations(nums):
n = len(nums)
tree = SegmentTree(n)
tree.build(1, 0, n - 1, nums)
left = tree.query(1, 0, n - 1, 0, n - 1)[1]
right = (left + 1) % n
ans = 0
while left != -1:
if nums[right] < nums[left]:
nums.append(nums.pop(right))
ans += 1
left = (left - 1) % n
else:
if left == right:
left_res = tree.query(1, 0, n - 1, 0, left - 1)
right_res = tree.query(1, 0, n - 1, right + 1, n - 1)
if left_res[0] < right_res[0]:
nums.pop(left)
ans += 1
left = left_res[1]
else:
nums.pop(right)
ans += 1
left = right_res[1]
else:
nums.pop(left)
ans += 1
left = tree.query(1, 0, n - 1, 0, left - 1)[1]
right = (left + 1) % n
return ans
代码分析:
- 线段树的构建和查询操作比较基础,这里就不再赘述。
- 主函数中,我们用两个指针
left和right模拟操作。 left指针指向数组中的最小值,right指针指向left指针的下一个位置。- 循环中,我们判断
right指针指向的元素是否比left指针指向的元素小。 - 如果更小,则将
right指针指向的元素移动到数组末尾,并将left指针移动到数组的下一个位置。 - 否则,如果
left指针指向的元素是当前数组中的最小值,则将其删除,并将left指针移动到数组的下一个位置。 - 如果
left指针指向的元素不是当前数组中的最小值,则将其移动到数组末尾,并将left指针移动到数组的下一个位置。 - 当
left指针指向 -1 时,表示数组为空,循环结束。 - 最后,返回
ans,即操作次数。
原文地址: https://www.cveoy.top/t/topic/nJ9m 著作权归作者所有。请勿转载和采集!