Leetcode 第 395 场周赛题解

本文解析了LeetCode第395场周赛中的四个题目,涉及数组相加求整数、数组子数组操作、唯一性数组的中位数计算,以及位运算的应用,提供了详细的思路、代码及复杂度分析。

Leetcode 第 395 场周赛题解

题目1:3131. 找出与数组相加的整数 I

思路

对两个数组进行排序,相同下标的 nums2[i] - nums1[i] 即为答案。

代码

/*
 * @lc app=leetcode.cn id=3131 lang=cpp
 *
 * [3131] 找出与数组相加的整数 I
 */

// @lc code=start
class Solution
{
public:
    int addedInteger(vector<int> &nums1, vector<int> &nums2)
    {
        ranges::sort(nums1);
        ranges::sort(nums2);
        return nums2[0] - nums1[0];
    }
};
// @lc code=end

复杂度分析

时间复杂度:O(nlogn),其中 n 是数组 nums1 或 nums2 的长度。

空间复杂度:O(1)。

题目2:3132. 找出与数组相加的整数 II

思路

nums1 比 nums2 多两个元素。设 nums2 数组的长度为 n,则数组 nums1 的长度为 n+2。

将两个数组排序后,可以想到,只有 nums1[0…n]、nums1[1…n+1]、nums1[2…n+2] 这三个子数组去与数组 nums2 做比较。

为了使得答案最小,我们先从 nums1[2…n+2] 这个子数组开始比较,设 diff = nums2[0] - nums1[2],然后从下标 idx1 = 2 开始遍历数组 nums1,从下标 idx2 = 0 开始遍历数组 nums2,看是否满足 nums1[idx1] + diff == nums2[idx2]。如果遍历到 nums2 的末尾,发现都满足,则 diff 就是答案;否则,从 nums1[1…n+1] 比较,最后试试 nums1[0…n],题目保证存在答案。

代码

/*
 * @lc app=leetcode.cn id=3132 lang=cpp
 *
 * [3132] 找出与数组相加的整数 II
 */

// @lc code=start
class Solution
{
public:
    int minimumAddedInteger(vector<int> &nums1, vector<int> &nums2)
    {
        ranges::sort(nums1);
        ranges::sort(nums2);
        for (int i = 2; i > 0; i--)
        {
            int diff = nums2[0] - nums1[i];
            // 在 {nums1[i] + diff} 中找子序列 nums2
            int j = 0;
            for (int k = i; k < nums1.size(); k++)
            {
                if (j < nums2.size() && nums2[j] == nums1[k] + diff)
                    j++;
            }
            if (j == nums2.size())
                return diff;
        }
        return nums2[0] - nums1[0];
    }
};
// @lc code=end

复杂度分析

时间复杂度:O(nlogn),其中 n 为数组 nums1 的长度。瓶颈在排序上。

空间复杂度:O(1)。

题目3:3133. 数组最后一个元素的最小值

思路

位运算。

题解:位运算,两种简洁写法(Python/Java/C++/Go)

代码1

/*
 * @lc app=leetcode.cn id=3133 lang=cpp
 *
 * [3133] 数组最后一个元素的最小值
 */

// @lc code=start
class Solution
{
public:
    long long minEnd(int n, int x)
    {
        n--; // 先把 n 减一,这样下面讨论的 n 就是原来的 n-1
        long long ans = x;
        // n 的第 j 个比特(从右往左)填到 x 的第 i 个比特上
        int i = 0, j = 0;
        while (n >> j)
        {
            // x 的第 i 个比特值是 0,即「空位」
            if (((ans >> i) & 01) == 0)
            {
                // 空位填入 n 的第 j 个比特值
                int bit = (n >> j) & 01;
                ans |= (long long)bit << i;
                j++;
            }
            i++;
        }
        return ans;
    }
};
// @lc code=end

复杂度分析:

时间复杂度:O(logx+logn)。

空间复杂度:O(1)。

代码2

把 x 取反,用 lowbit 枚举其中的 1,就是要填的空位。

class Solution {
public:
    long long minEnd(int n, int x) {
        n--;
        long long ans = x;
        int j = 0;
        for (long long t = ~x, lb; n >> j; t ^= lb) {
            lb = t & -t;
            ans |= (long long) (n >> j++ & 1) * lb;
        }
        return ans;
    }
};

复杂度分析:

时间复杂度:O(logn)。

空间复杂度:O(1)。

题目4:3134. 找出唯一性数组的中位数

思路

二分答案 + 滑动窗口。

二分中位数 upper,问题变成:distinct 值 ≤upper 的子数组有多少个?

设子数组的个数为 cnt,如果 cnt<k 说明二分的 upper 小了,更新二分左边界 left,否则更新二分右边界 right。

怎么计算 distinct 值 ≤upper 的子数组个数 cnt?

由于子数组越长,不同元素个数(distinct 值)不会变小,这样的单调性可以让我们滑窗。

用一个哈希表 freq 统计窗口(子数组)内的元素及其出现次数。

枚举窗口右端点 r,把 nums[r] 加入 freq(出现次数加一)。如果发现 freq 的大小超过 upper,就不断移出窗口左端点元素 nums[l](出现次数减一,如果出现次数等于 0 就从 freq 中移除),直到 freq 的大小 ≤upper 为止。

此时右端点为 r,左端点为 l,l+1,l+2,⋯ ,r 的子数组都是满足要求的(distinct 值 ≤upper),一共有 r−l+1 个,加到子数组个数 cnt 中。

代码

/*
 * @lc app=leetcode.cn id=3134 lang=cpp
 *
 * [3134] 找出唯一性数组的中位数
 */

// @lc code=start
class Solution
{
public:
    int medianOfUniquenessArray(vector<int> &nums)
    {
        int n = nums.size();
        long long k = ((long long)n * (n + 1) / 2 + 1) / 2;

        auto check = [&](int upper)
        {
            long long cnt = 0;
            int l = 0;
            unordered_map<int, int> freq;
            for (int r = 0; r < n; r++)
            {
                freq[nums[r]]++;
                while (freq.size() > upper)
                {
                    int out = nums[l++];
                    freq[out]--;
                    if (freq[out] == 0)
                        freq.erase(out);
                }
                cnt += r - l + 1;
                if (cnt >= k)
                    return true;
            }
            return false;
        };

        int left = 0, right = n;
        while (left + 1 < right)
        {
            int mid = (left + right) / 2;
            (check(mid) ? right : left) = mid;
        }
        return right;
    }
};
// @lc code=end

复杂度分析

时间复杂度:O(nlogn),其中 n 为数组 nums 的长度。二分 O(log⁡n) 次,每次会跑一个 O(n) 的滑动窗口。

空间复杂度:O(n),其中 n 为数组 nums 的长度。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

UestcXiye

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值