【LeetCode学习计划】《数据结构入门-C++》第2天 数组

LeetCode【学习计划】:【数据结构】



1. 两数之和

LeetCode: 1. 两数之和

简 单 \color{#00AF9B}{简单}

给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个 整数,并返回它们的数组下标。
你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。
你可以按任意顺序返回答案。

示例 1:

输入:nums = [2,7,11,15], target = 9
输出:[0,1]
解释:因为 nums[0] + nums[1] == 9 ,返回 [0, 1]

示例 2:

输入:nums = [3,2,4], target = 6
输出:[1,2]

示例 3:

输入:nums = [3,3], target = 6
输出:[0,1]

提示:

  • 2 <= nums.length <= 104
  • -109 <= nums[i] <= 109
  • -109 <= target <= 109
  • 只会存在一个有效答案

进阶:你可以想出一个时间复杂度小于 O ( n 2 ) O(n^2) O(n2) 的算法吗


方法1:枚举(双指针)

设组成和的其中一个数为x,那么另一个数就是target-x。于是我们可以使用两重循环,外层确定一个x,内层寻找target-x

#include <vector>
using std::vector;
class Solution
{
public:
    vector<int> twoSum(vector<int> &nums, int target)
    {
        const int n = nums.size();
        for (int left = 0; left < n; left++)
        {
            for (int right = left + 1; right < n; right++)
            {
                if (nums[left] + nums[right] == target)
                    return {left, right};
            }
        }
        return {};
    }
};

复杂度分析

  • 时间复杂度: O ( n 2 ) O(n^2) O(n2)

  • 空间复杂度: O ( 1 ) O(1) O(1)。我们只需要常量空间来存储若干变量。

参考结果

Accepted
57/57 cases passed (300 ms)
Your runtime beats 30.24 % of cpp submissions
Your memory usage beats 60.55 % of cpp submissions (10 MB)

方法2:哈希表

方法1中时间复杂度较高的原因是内层寻找target-x的复杂度较高,而使用哈希表,可以使查找target-x的时间复杂度从 O ( n ) O(n) O(n) O ( 1 ) O(1) O(1)

对于每一个x,先查找target-x是否在哈希表中,若在则返回答案。然后把x插入到哈希表中。

#include <vector>
#include <unordered_map>
using std::unordered_map;
using std::vector;
class Solution
{
public:
    vector<int> twoSum(vector<int> &nums, int target)
    {
        unordered_map<int, int> hashtable;
        for (int i = 0; i < nums.size(); i++)
        {
            auto it = hashtable.find(target - nums[i]);
            if (it != hashtable.end())
                return {it->second, i};
            hashtable[nums[i]] = i;
        }
        return {};
    }
};

复杂度分析

  • 时间复杂度: O ( n ) O(n) O(n)

  • 空间复杂度: O ( n ) O(n) O(n)。主要为哈希表的开销。

参考结果

Accepted
57/57 cases passed (4 ms)
Your runtime beats 99.27 % of cpp submissions
Your memory usage beats 21.24 % of cpp submissions (10.7 MB)


88. 合并两个有序数组

LeetCode: 88. 合并两个有序数组

简 单 \color{#00AF9B}{简单}

给你两个按 非递减顺序 排列的整数数组 nums1nums2,另有两个整数 mn ,分别表示 nums1nums2 中的元素数目。
请你 合并 nums2nums1 中,使合并后的数组同样按 非递减顺序 排列。
注意:最终,合并后数组不应由函数返回,而是存储在数组 nums1 中。为了应对这种情况,nums1 的初始长度为 m + n,其中前 m 个元素表示应合并的元素,后 n 个元素为 0 ,应忽略。nums2 的长度为 n

示例 1:

输入:nums1 = [1,2,3,0,0,0], m = 3, nums2 = [2,5,6], n = 3
输出:[1,2,2,3,5,6]
解释:需要合并 [1,2,3] 和 [2,5,6] 。
合并结果是 [1,2,2,3,5,6] ,其中斜体加粗标注的为 nums1 中的元素。

示例 2:

输入:nums1 = [1], m = 1, nums2 = [], n = 0
输出:[1]
解释:需要合并 [1][] 。
合并结果是 [1]

示例 3:

输入:nums1 = [0], m = 0, nums2 = [1], n = 1
输出:[1]
解释:需要合并的数组是 [][1] 。
合并结果是 [1] 。
注意,因为 m = 0 ,所以 nums1 中没有元素。nums1 中仅存的 0 仅仅是为了确保合并结果可以顺利存放到 nums1 中。

提示:

  • nums1.length == m + n
  • nums2.length == n
  • 0 <= m, n <= 200
  • 1 <= m + n <= 200
  • -109 <= nums1[i], nums2[j] <= 109

进阶:你可以设计实现一个时间复杂度为 O(m + n) 的算法解决此问题吗?


方法1:合并后排序

最暴力的方法就是把nums2的所有元素先插入到nums1的后面,然后对nums1进行排序。

#include <vector>
#include <algorithm>
using namespace std;
class Solution
{
public:
    void merge(vector<int> &nums1, int m, vector<int> &nums2, int n)
    {
        for (int i = 0; i < n; i++)
        {
            nums1[m + i] = nums2[i];
        }
        sort(nums1.begin(), nums1.end());
    }
};

复杂度分析

  • 时间复杂度: O ( ( m + n ) × log ⁡ ( m + n ) ) O((m+n) \times \log (m+n)) O((m+n)×log(m+n))。数组长度为m+n,因此快速排序std::sort的时间复杂度为 O ( ( m + n ) × log ⁡ ( m + n ) ) O((m+n) \times \log (m+n)) O((m+n)×log(m+n))

  • 空间复杂度: O ( log ⁡ ( m + n ) ) O(\log (m+n)) O(log(m+n))。快速排序std::sort的空间复杂度为 O ( log ⁡ ( m + n ) ) O(\log (m+n)) O(log(m+n))

参考结果

Accepted
59/59 cases passed (4 ms)
Your runtime beats 63.91 % of cpp submissions
Your memory usage beats 90.69 % of cpp submissions (8.7 MB)

方法2:双指针

题目中提到两个数组都是已排序好的,于是我们在各数组的起始项设置一个指针,这样我们就能从小至大遍历2个数组了。每次遍历,将两个指针指向的更小的那个值存入一个新数组ans。最后将整个ans复制到nums1中即可。

#include <vector>
using namespace std;
class Solution
{
public:
    void merge(vector<int> &nums1, int m, vector<int> &nums2, int n)
    {
        int p1 = 0, p2 = 0, p = 0;
        int *ans = new int[m + n];
        while (p1 < m && p2 < n)
        {
            if (nums1[p1] < nums2[p2])
            {
                ans[p] = nums1[p1];
                p1++;
                p++;
            }
            else
            {
                ans[p] = nums2[p2];
                p2++;
                p++;
            }
        }
        while (p1 < m)
        {
            ans[p] = nums1[p1];
            p1++;
            p++;
        }
        while (p2 < n)
        {
            ans[p] = nums2[p2];
            p2++;
            p++;
        }
        for (int i = 0; i < m + n; i++)
        {
            nums1[i] = ans[i];
        }
    }
};

复杂度分析

  • 时间复杂度: O ( m + n ) O(m+n) O(m+n)。一共有m+n个元素。遍历一共为 O ( m + n ) O(m+n) O(m+n),最后复制到nums1 O ( m + n ) O(m+n) O(m+n),因此时间复杂度为 O ( 2 ( m + n ) ) = O ( m + n ) O(2(m+n))=O(m+n) O(2(m+n))=O(m+n)

  • 空间复杂度: O ( m + n ) O(m+n) O(m+n)。主要为中间数组ans的开销。

参考结果

Accepted
59/59 cases passed (4 ms)
Your runtime beats 63.91 % of cpp submissions
Your memory usage beats 38.29 % of cpp submissions (8.9 MB)

方法3:逆向双指针

方法2中需要用到中间数组的根本原因是:我们在确认nums1每个位置最终的元素时,又要保持原数组nums1不变。

而方法3的入手点在于:nums1数组的后半部分是无效值,也就是说这一部分的值是不用维护的。这样一来,我们可以在nums1nums2数组的末项处各设置一个指针,每次将更大的元素存入nums1,这样就不会覆盖掉nums1中任何未被遍历的值,而且也省去了用于维护的中间数组。

#include <vector>
using namespace std;
class Solution
{
public:
    void merge(vector<int> &nums1, int m, vector<int> &nums2, int n)
    {
        int p1 = m - 1, p2 = n - 1, p = m + n - 1;
        while (p1 >= 0 && p2 >= 0)
        {
            if (nums1[p1] < nums2[p2])
            {
                nums1[p] = nums2[p2];
                p2--;
                p--;
            }
            else
            {
                nums1[p] = nums1[p1];
                p1--;
                p--;
            }
        }
        while (p1 >= 0)
        {
            nums1[p] = nums1[p1];
            p1--;
            p--;
        }
        while (p2 >= 0)
        {
            nums1[p] = nums2[p2];
            p2--;
            p--;
        }
    }
};

复杂度分析

  • 时间复杂度: O ( m + n ) O(m+n) O(m+n)

  • 空间复杂度: O ( 1 ) O(1) O(1)。我们只需要常量空间来存储若干变量。

参考结果

Accepted
59/59 cases passed (0 ms)
Your runtime beats 100 % of cpp submissions
Your memory usage beats 94.93 % of cpp submissions (8.6 MB)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

亡心灵

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

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

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

打赏作者

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

抵扣说明:

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

余额充值