五月训练 Day3

本文详细解析了LeetCode中的四道算法题目,包括有序数组的平方、丢失的数字、数组中最大数对和的最小值以及按递增顺序显示卡牌。通过排序、位运算、贪心算法等技巧,逐一解答了这些问题,并提供了高效的解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

0. Leetcode 977. 有序数组的平方

给你一个按 非递减顺序 排序的整数数组 nums,返回 每个数字的平方 组成的新数组,要求也按 非递减顺序 排序。
提示:
1 ≤ n u m s . l e n g t h ≤ 104 1 \le nums.length \le104 1nums.length104
− 104 ≤ n u m s [ i ] ≤ 104 -104 \le nums[i] \le 104 104nums[i]104
nums 已按 非递减顺序 排序

分析与解答

对于本题可以先计算出 nums 的平方数,再排序:

class Solution {
public:
    void qSort(vector<int>& nums, int low, int high) { // 排序
        if (low >= high) {
            return;
        }
        int pivot = nums[low];
        int l(low), r(high);

        while (l < r) {
            while (nums[r] > pivot) {
                r--;
            }
            if (l < r) {
                nums[l] = nums[r];
            }

            while (nums[l] <= pivot && l < r) {
                l++;
            }
            if (l < r) {
                nums[r] = nums[l];
            }
        }
        nums[l] = pivot;

        qSort(nums, low, l - 1);
        qSort(nums, l + 1, high);
    }

    vector<int> sortedSquares(vector<int>& nums) {
        vector<int> result;
        result.reserve(nums.size());
        for (int i = 0; i < nums.size(); ++i) { // 计算平方数后放入数组
            result.push_back(nums[i] * nums[i]);
        }
        
        qSort(result, 0, result.size() - 1); // 数组排序
    
        return result;
    }
};

算法时间复杂度为 O ( N l o g N ) O(NlogN) O(NlogN)
由于数组为有序数组,因此可以使用双指针方法,找到分界点后依序计算平方数并放入结果数组中。

1. Leetcode 268. 丢失的数字

给定一个包含 [0, n] 中 n 个数的数组 nums ,找出 [0, n] 这个范围内没有出现在数组中的那个数。

分析与解答

本题可以使用位运算的特性:a ^ a ^ b = 0,找缺失数字时,可以将数组与 0-n 的所有数字进行异或运算,相当于使用出现的数字与自己做了异或运算,得到 0,此时最后结果即缺失的数字只出现一次:

class Solution {
public:
    int missingNumber(vector<int>& nums) {
        int k = 0;
        int result(0);
        for (int i = 0; i < nums.size(); ++i) {
            result = result ^ nums[i] ^ k++;
        }
        result = result ^ k;
        return result;
    }
};

由于该方法遍历一次数组,因此算法时间复杂度为 O ( N ) O(N) O(N)

3. Leetcode 1877. 数组中最大数对和的最小值

一个数对 (a,b) 的 数对和 等于 a + b 。最大数对和 是一个数对数组中最大的 数对和 。
比方说,如果我们有数对 (1,5) ,(2,3) 和 (4,4),最大数对和 为 max(1+5, 2+3, 4+4) = max(6, 5, 8) = 8 。
给你一个长度为 偶数 n 的数组 nums ,请你将 nums 中的元素分成 n / 2 个数对,使得:
nums 中每个元素 恰好 在 一个 数对中,且
最大数对和 的值 最小 。
请你在最优数对划分的方案下,返回最小的 最大数对和 。
提示:
n = = n u m s . l e n g t h n == nums.length n==nums.length
2 ≤ n ≤ 105 2 \le n \le 105 2n105
n 是 偶数 。
1 ≤ n u m s [ i ] ≤ 105 1 \le nums[i] \le 105 1nums[i]105

分析与解答

本题难点在于贪心算法的理解。对任意四个数字, m i n ≤ x ≤ y ≤ m a x min \le x \le y \le max minxymax,取 ( m i n , m a x ) ; ( x , y ) (min, max);(x, y) (min,max)(x,y) 组合能使最大数对和最小。若取 ( x , m a x ) ; ( m i n , y ) (x, max); (min, y) (x,max);(min,y),此时 x + m a x > m i n + m a x x + max > min + max x+max>min+max 故该组合最大数对和更大;若取 ( y , m a x ) ; ( x , m i n ) (y, max); (x, min) (y,max);(x,min),此时 y + m a x > m i n + m a x y + max > min + max y+max>min+max 故该组合最大数对和更大。因此对有序数组,最大最小两个数匹配为最优数对划分。因此对数组排序后遍历组合即可求出最小的最大数对和:

class Solution {
public:
    void qSort(vector<int>& nums, int low, int high) {
        if (low >= high) {
            return;
        }
        int pivot = nums[low];
        int l(low), r(high);

        while (l < r) {
            while (nums[r] > pivot) {
                r--;
            }
            if (l < r) {
                nums[l] = nums[r];
            }

            while (nums[l] <= pivot && l < r) {
                l++;
            }
            if (l < r) {
                nums[r] = nums[l];
            }
        }
        nums[l] = pivot;

        qSort(nums, low, l - 1);
        qSort(nums, l + 1, high);
    }

    int minPairSum(vector<int>& nums) {
        // 排序后依次组合最大最小数即可
        qSort(nums, 0, nums.size()-1);
        int result(-1);
        for (int i = 0, j = nums.size() - 1;
            i < j; i++, j--) {
                if (nums[i] + nums[j] > result) {
                    result = nums[i] + nums[j];
                }
            }
        return result;
    }
};

由于该方法需要排序,因此算法时间复杂度为 O ( N l o g N ) O(NlogN) O(NlogN)

4. Leetcode 950. 按递增顺序显示卡牌

牌组中的每张卡牌都对应有一个唯一的整数。你可以按你想要的顺序对这套卡片进行排序。
最初,这些卡牌在牌组里是正面朝下的(即,未显示状态)。
现在,重复执行以下步骤,直到显示所有卡牌为止:
从牌组顶部抽一张牌,显示它,然后将其从牌组中移出。
如果牌组中仍有牌,则将下一张处于牌组顶部的牌放在牌组的底部。
如果仍有未显示的牌,那么返回步骤 1。否则,停止行动。
返回能以递增顺序显示卡牌的牌组顺序。
答案中的第一张牌被认为处于牌堆顶部。
提示:
1 <= A.length <= 1000
1 <= A[i] <= 10^6
对于所有的 i != j,A[i] != A[j]

分析与解答

!!看懂啦
对长度为 ndeck,假设已有正确排序后的答案数组,此时下标为 0-n,那么可以按照题意,模拟取出操作时的下标变化。由于取出后数组为升序数组,因此在模拟时,将升序排列后的 deck 依序放入下标数组中第一个元素指示的位置,放入后依题意操作下标数组,直至放完所有数,就构成了答案数组。

class Solution {
public:
    vector<int> deckRevealedIncreasing(vector<int>& deck) {
        vector<int> result;
        result.resize(deck.size());
        // 构造下标数组模拟取出顺序
        // 排序 deck 后依下标数组顺序放入
        deque<int> index(deck.size());
        for (int i = 0; i < deck.size(); ++i) { // 这里为下标数组
            index[i] = i;
        }
        
        sort(deck.begin(), deck.end()); // 排序

        for (int i = 0; i < deck.size(); ++i) {
            result[index[0]] = deck[i]; // 顺序放入
            // 模拟取出顺序
            index.pop_front(); // 第一个已经放入
            int tmp = index.front(); // 将第一个弹出,放到最后
            index.pop_front();
            index.push_back(tmp);
        }

        return result;
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值