【每日LeetCode】 最小操作次数使数组元素相等 + 逆序对【归并】

97 阅读4分钟

LCP 24. 数字游戏 O(nlogn)O(n)\lgroup O(n\log n)、O(n) \rgroup

image.png

class Solution:
    def numsGame(self, nums: List[int]) -> List[int]:
        # 题目的要求 等价于 将 nums[i] - i 操作成相同的数 符合要求时, i 加 1, nums[i] 加 1
        res = [0] * len(nums)
        MOD = 10**9 + 7
        left = [] # 维护 较小的一半  大根堆
        right = [] # 维护 较大的 一半   小根堆   中位数 必定为 right[0]
        left_sum = right_sum = 0
        for i, num in enumerate(nums):
            x = num - i 
            if i % 2 == 0: # 左右 元素一样多。 要把 -left[0] 和 x 中大的 添加 到 right 
                left_sum -= max(-left[0] - x, 0) if left else 0  # 要是 x 大于 -left[0]  left 不变
                temp = -heappushpop(left, -x)
                right_sum += temp 
                heappush(right, temp)
                res[i] = (right_sum - right[0] - left_sum) % MOD   # 右边多。 根据计算公式 有一个中位数 无法抵消。
            else:  # right 多 要把 right[0] 和 x 中 小的  加到 left 
                right_sum += max(x - right[0], 0)  # 要是 x 大于 right[0], x 加到 left。 right_sum 不变
                temp = heappushpop(right, x)
                left_sum += temp 
                heappush(left, -temp)
                res[i] = (right_sum - left_sum) % MOD 

        return res 

image.png

class Solution {
public:
    vector<int> numsGame(vector<int>& nums) {
        const int mod = 1e9 + 7;
        vector<int> res(nums.size());
        priority_queue<int> left; // 大顶堆
        priority_queue<int, vector<int>, greater<int>> right; // 较长,right.top()必为中位数。 小根堆
        long long left_sum = 0, right_sum = 0;
        for (int i = 0; i < nums.size(); ++i){
            int x = nums[i] - i;
            if (i % 2 == 0){// 总长为 奇数, 中位数为 right.top()   left.top()和 x 中更大的 加到 right
                if (!left.empty() && x < left.top()){
                    left_sum -= left.top() - x;   // 换了 个 更小的数
                    left.push(x);
                    x = left.top();
                    left.pop();   
                }
                right_sum += x;
                right.push(x);
                res[i] = (right_sum - right.top() - left_sum) % mod;
            }else{// 总长 为 偶数, 中位数 为 (left.top() + right.top()) / 2  right.top() 和 x  中 更小的加到 left
                if (x > right.top()){ // x 替换 right.top()。 原来right.top() 加入 left
                    right_sum += x - right.top();
                    right.push(x);
                    x = right.top();
                    right.pop();
                }
                left_sum += x;
                left.push(x);
                res[i] = (right_sum - left_sum) % mod;
            }
        } 
        return res;
    }
};

462. 最小操作次数使数组元素相等 II

将 所有数 变成 中位数 所需的 移动数 最少。

方法一:排序 O(nlogn)O(logn)\lgroup O(n\log n)、O(\log n) \rgroup

class Solution:
    def minMoves2(self, nums: List[int]) -> int:
        nums.sort()
        return sum(abs(num - nums[len(nums)//2]) for num in nums)

image.png

⭐ 方法二: 快速选择 O(n)O(logn)\lgroup O(n)、O(\log n) \rgroup

image.png

class Solution {
public:
    int minMoves2(vector<int>& nums) {
        nth_element(nums.begin(), nums.begin() + nums.size() / 2, nums.end()); // 将数组中第k小的整数放在区间第k个位置。 中间规定找中位数
        int median = nums[nums.size()/2];
        int res = 0;
        for (int x : nums){
            res += abs(x - median);  // 将所有 数 都变成中位数 
        }
        return res;
    }
};

1103. 分糖果 II

方法一:遍历, 有糖就分

class Solution:
    def distributeCandies(self, candies: int, num_people: int) -> List[int]:
        res = [0] * num_people
        i = 0
        while candies != 0:
            res[i % num_people] += min(i + 1, candies)
            candies -= min(i + 1, candies)
            i += 1
        return res 

LCR 170. 交易逆序对的总数 【归并】O(nlogn)O(n)\lgroup O(n \log n)、O(n) \rgroup

image.png

image.png

class Solution:
    def reversePairs(self, record: List[int]) -> int:
        def merge_sort(left, right):
            # 终止条件
            if left >= right:
                return 0
            
            # 递归 划分
            mid = (left + right) // 2
            res = merge_sort(left, mid) + merge_sort(mid + 1, right)

            # 合并
            i, j = left, mid + 1  # 两两 比较
            temp[left : right + 1] = record[left : right + 1] # temp暂存之前的。 record 不断变得有序
            for k in range(left, right + 1):
                if i == mid + 1: # 左 子数组 遍历完成
                    record[k] = temp[j]
                    j += 1
                elif j == right + 1 or temp[i] <= temp[j]: # 右 子数组 遍历完成 或  temp[i] <= temp[j] 
                    record[k] = temp[i]
                    i += 1
                else: # temp[i] > temp[j]  # 逆序对
                    record[k] = temp[j]
                    j += 1
                    res += mid - i + 1  # temp[i-mid] 有序,则temp[i] - temp[mid] 均大于 temp[j] 逆序对 个数 增加 mid - i + 1
            return res 

        temp = [0] * len(record)
        return merge_sort(0, len(record) - 1)

class Solution {
public:
    int reversePairs(vector<int>& record) {
        vector<int> temp(record.size());
        return merge_sort(0, record.size() - 1, record, temp);
    }
private:
    int merge_sort(int left, int right, vector<int>& record, vector<int>& temp){
        // 终止条件
        if (left >= right) return 0;
        // 递归划分
        int mid = (left + right) / 2;
        int res = merge_sort(left, mid, record, temp) + merge_sort(mid + 1, right, record, temp);
        // 合并
        int i = left, j = mid + 1;
        for (int k = left; k <= right; ++k){
            temp[k] = record[k];
        }
        for (int k = left; k <= right; ++k){
            if (i == mid + 1){
                record[k] = temp[j];
                ++j;
            }else if (j == right + 1 || temp[i] <= temp[j]){
                record[k] = temp[i];
                ++i;
            }else{
                record[k] = temp[j];
                ++j;
                res += mid - i + 1; // temp[i].. temp[mid] 有序, 由于 temp[i] > temp[j]。temp[i].. temp[mid]这些数 都比 temp[j] 大
            }
        }
        return res;
    }
};
class Solution {
public:
    int reversePairs(vector<int>& record) {

        vector<int> temp(record.size());

        function<int(int, int)> merge_sort = [&](int left, int right){
            // 终止条件
            if (left >= right) return 0;
            // 递归划分
            int mid = (left + right) / 2;
            int res = merge_sort(left, mid) + merge_sort(mid + 1, right);
            // 合并
            int i = left, j = mid + 1;
            for (int k = left; k <= right; ++k){
                temp[k] = record[k];
            }
            for (int k = left; k <= right; ++k){
                if (i == mid + 1){
                    record[k] = temp[j];
                    ++j;
                }else if (j == right + 1 || temp[i] <= temp[j]){
                    record[k] = temp[i];
                    ++i;
                }else{
                    record[k] = temp[j];
                    ++j;
                    res += mid - i + 1; // temp[i].. temp[mid] 有序, 由于 temp[i] > temp[j]。temp[i].. temp[mid]这些数 都比 temp[j] 大
                }
            }
            return res;
        };

        return merge_sort(0, record.size() - 1);
    }
};