面试必看:优势洗牌

贪心+双指针求解优势洗牌问题(C++ 实现)

题目描述

给定两个长度相等的数组 nums1nums2,定义 nums1 相对于 nums2 的优势为满足 nums1[i] > nums2[i] 的索引 i 的数量。要求返回 nums1 的任意一个排列,使得该排列相对于 nums2 的优势最大化。

问题分析

结合题目要求和数据特征,我们可以先梳理核心约束与解题思路:

  1. 两个数组长度相同,排列后元素需按索引一一对应匹配;
  2. 目标是最大化满足 nums1[i] > nums2[i] 的索引数量,本质是用最优的匹配策略实现收益最大化;
  3. 直接暴力枚举所有排列会产生极高的时间复杂度,无法高效处理较大数据量,因此需要选择更优的算法方案。

结合问题特征,贪心算法+双指针 是适配该场景的最优解:贪心策略用于保证每一步选择都能获取当前最优收益,双指针用于高效遍历匹配元素,整体算法可以在线性遍历结合排序的基础上完成计算。

算法思路

核心策略

采用贪心匹配 原则:用 nums1最小的可用大数 去匹配 nums2 中对应元素,无法满足大小关系时,用 nums1 中最小的数去填充,最大化有效匹配数量的同时,减少优质元素的浪费。

具体步骤

  1. 数组预处理
    • nums1 执行升序排序,方便通过双指针快速获取当前最大/最小可用元素;
    • nums2 构建(值, 原索引)的键值对数组,再对该数组执行升序排序。保留原索引是为了保证最终结果能对应到题目要求的原始位置。
  2. 双指针初始化
    定义左指针 left 指向排序后 nums1 的起始位置(最小值),右指针 right 指向排序后 nums1 的末尾位置(最大值)。
  3. 逆序遍历匹配
    从大到小遍历处理后的 nums2 数组,依次判断当前元素:
    • nums1 的当前最大值大于 nums2 的当前元素,将该最大值填入结果数组的对应原始索引,右指针左移;
    • 若不满足大小关系,说明当前最大元素也无法形成优势,改用 nums1 的当前最小值填充,左指针右移。
  4. 输出结果
    遍历完成后,结果数组即为满足条件的 nums1 最优排列。

C++ 实现代码

cpp 复制代码
#include <vector>
#include <algorithm>
using namespace std;

class Solution {
public:
    vector<int> advantageCount(vector<int>& nums1, vector<int>& nums2) {
        // 获取数组长度
        int n = nums1.size();
        // 初始化结果数组,长度与输入数组一致
        vector<int> res(n, 0);
        // 存储nums2的(值, 原索引)对,保留原始位置信息
        vector<pair<int, int>> nums2_temp;
        for (int i = 0; i < n; ++i) {
            nums2_temp.emplace_back(nums2[i], i);
        }

        // 对nums1升序排序
        sort(nums1.begin(), nums1.end());
        // 对nums2的键值对数组按值升序排序
        sort(nums2_temp.begin(), nums2_temp.end());

        // 双指针初始化:left指向nums1最小值,right指向nums1最大值
        int left = 0;
        int right = n - 1;

        // 逆序遍历排序后的nums2_temp,从大元素开始匹配
        for (int i = n - 1; i >= 0; --i) {
            int val = nums2_temp[i].first;   // nums2当前元素值
            int index = nums2_temp[i].second; // 该元素在原数组中的索引
            // 贪心选择:能用大值匹配就用大值,否则用小值填充
            if (val < nums1[right]) {
                res[index] = nums1[right];
                --right;
            } else {
                res[index] = nums1[left];
                ++left;
            }
        }
        return res;
    }
};

复杂度分析

时间复杂度

算法主要耗时操作为排序操作:

  • nums1 排序的时间复杂度为 O(nlog⁡n)O(n\log n)O(nlogn);
  • nums2 键值对数组排序的时间复杂度为 O(nlog⁡n)O(n\log n)O(nlogn);
  • 后续双指针遍历为线性操作,时间复杂度为 O(n)O(n)O(n)。

整体时间复杂度为 O(nlog⁡n)O(n\log n)O(nlogn),在数据量较大时仍能保持高效运行。

空间复杂度

  • 额外开辟了存储 nums2 键值对的数组 nums2_temp 和结果数组 res,两者空间开销均为 O(n)O(n)O(n);
  • 排序过程中递归调用栈的空间开销为 O(log⁡n)O(\log n)O(logn),可忽略不计。

整体空间复杂度为 O(n)O(n)O(n),属于线性空间开销。

算法验证与适用场景

该算法通过贪心策略保证了每一步匹配的最优性,双指针遍历避免了重复判断,能够稳定得到优势最大化的排列。适用于题目给定的所有常规场景,无特殊数据边界限制,在力扣对应题目中可通过全部测试用例。


总结

  1. 本题核心解法为贪心算法+双指针,通过排序预处理和逆序匹配实现最优解;
  2. 算法时间复杂度为 O(nlog⁡n)O(n\log n)O(nlogn),空间复杂度为 O(n)O(n)O(n),兼顾了时间效率与实现简洁性;
  3. 保留原数组索引是解题关键,确保排列结果能对应到题目要求的原始位置。
相关推荐
李日灐3 小时前
C++进阶必备:红黑树从 0 到 1: 手撕底层,带你搞懂平衡二叉树的平衡逻辑与黑高检验
开发语言·数据结构·c++·后端·面试·红黑树·自平衡二叉搜索树
YuTaoShao3 小时前
【LeetCode 每日一题】3634. 使数组平衡的最少移除数目——(解法二)排序 + 二分查找
数据结构·算法·leetcode
wangluoqi3 小时前
26.2.6练习总结
数据结构·算法
Q741_1473 小时前
C++ 优先级队列 大小堆 模拟 力扣 703. 数据流中的第 K 大元素 每日一题
c++·算法·leetcode·优先级队列·
网安墨雨3 小时前
Python自动化一------pytes与allure结合生成测试报告
开发语言·自动化测试·软件测试·python·职场和发展·自动化
一战成名9963 小时前
深度解析 CANN 模型转换工具链:从 ONNX 到 OM
人工智能·学习·安全·开源
木井巳3 小时前
【递归算法】二叉搜索树中第K小的元素
java·算法·leetcode·深度优先·剪枝
铉铉这波能秀3 小时前
LeetCode Hot100 中 enumerate 函数的妙用(2026.2月版)
数据结构·python·算法·leetcode·职场和发展·开发
墨有6663 小时前
哈希表从入门到实现,一篇吃透!
数据结构·算法·哈希算法