leetcode练习——数组篇(1)(std::ios::sync_with_stdio(false);std::cin.tie(nullptr);)

20 篇文章 0 订阅
14 篇文章 0 订阅

题号1. 两数之和:

给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标。

你可以假设每种输入只会对应一个答案。但是,你不能重复利用这个数组中同样的元素。

示例:

给定 nums = [2, 7, 11, 15], target = 9

因为 nums[0] + nums[1] = 2 + 7 = 9

所以返回 [0, 1]

才拿到这道题时,第一个反应是遍历每个元素 x,并查找是否存在一个值与 target−x 相等的目标元素的暴力解法,时间复杂度为O(n²),C++实现如下:

class Solution {
public:
    vector<int> twoSum(vector<int>& nums, int target) 
    {
        vector<int> ret;
        for (int i = 0; i < nums.size(); i++)
        {
            for(int j = i + 1; j < nums.size(); j++)
            {
                if(nums[i] == target - nums[j])
                {
                    ret.push_back(i);
                    ret.push_back(j);
                    return ret;
                }                
            }
        }
        ret.push_back(-1);
        ret.push_back(-1);
        return ret;
    }
};

 

系统耗时显示是164ms, 对于每个元素,我们试图通过遍历数组的其余部分来寻找它所对应的目标元素,这将耗费 O(n) 的时间。因此时间复杂度为 O(n^2),空间复杂度为O(1)。

 

为了对运行时间复杂度进行优化,我们需要一种更有效的方法来检查数组中是否存在目标元素。如果存在,我们需要找出它的索引。保持数组中的每个元素与其索引相互对应的最好方法是什么?哈希表。

class Solution {
public:
    vector<int> twoSum(vector<int>& nums, int target)
    {
        unordered_map<int, int> hm1;

        for (int i = 0; i < nums.size(); ++i) 
        {
            int complement = target - nums[i];
            if(hm1.find(complement) != hm1.end())
            {
                return vector<int>{hm1.find(complement)->second, i};
            }
            hm1[nums[i]] = i;
        }
        return vector<int>{-1, -1};
    }
};

时间立马缩减到了8ms,性能的提升是立竿见影的,但是看了标准答案,看到有位大神居然将耗时缩减到了4ms,这里附上他的解法:

static const auto io_sync_off = []()
{
    // turn off sync
    std::ios::sync_with_stdio(false);
    // untie in/out streams
    std::cin.tie(nullptr);
    return nullptr;
}();

class Solution {
public:
    vector<int> twoSum(vector<int>& nums, int target)
    {
        map<int, int> hm1;

        for (int i = 0; i < nums.size(); ++i) 
        {
            int complement = target - nums[i];
            if(hm1.find(complement) != hm1.end())
            {
                return vector<int>{hm1.find(complement)->second, i};
            }
            hm1[nums[i]] = i;
        }
        return vector<int>{-1, -1};
    }
};

看完特地查了下开头的函数,详细的解释在下面:https://blog.csdn.net/qq_32320399/article/details/81518476

https://blog.csdn.net/YinJianxiang/article/details/76436089

在ACM里,经常出现数据集超大造成 cin TLE的情况。这时候大部分人(包括原来我也是)认为这是cin的效率不及scanf的错,甚至还上升到C语言和C++语言的执行效率层面的无聊争论。其实像上文所说,这只是C++为了兼容而采取的保守措施。我们可以在IO之前将stdio解除绑定,这样做了之后要注意不要同时混用cout和printf之类

在默认的情况下cin绑定的是cout,每次执行 << 操作符的时候都要调用flush,这样会增加IO负担。可以通过tie(0)(0表示NULL)来解除cin与cout的绑定,进一步加快执行效率。


题号4. 寻找两个有序数组的中位数:

给定两个大小为 m 和 n 的有序数组 nums1 和 nums2

请你找出这两个有序数组的中位数,并且要求算法的时间复杂度为 O(log(m + n))。

你可以假设 nums1 和 nums2 不会同时为空。

示例 1:

nums1 = [1, 3]
nums2 = [2]

则中位数是 2.0

示例 2:

nums1 = [1, 2]
nums2 = [3, 4]

则中位数是 (2 + 3)/2 = 2.5

我的思路是类似二路归并排序,但是不需要新的数组来存放两个数组的数据,也不需要将所有数据全部排序,只需要取到中间两个数。首先计算出总个数,然后求出中间两个数的下标 m 和 n ,定义两个指针,分别从两个数组的左边开始向后推进,知道遍历到第 m 个和第 n 个,求出两数的平均数并返回。当然这里同样解除了 IO 的同步用于提升性能。

double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2)
{
    int size = nums1.size() + nums2.size();

    int m = 0, n = 0;
    // 判断中位数是两数平均值还是单个值
    if (size % 2 == 1) // 总个数为基数则为中间值
    {
        n = size / 2;
        m = n;
    }
    else                // 偶数则为中间俩个数的平均值
    {
        n = size / 2;
        m = n - 1;
    }

    double midData = 0;
    int leftPoint  = 0;
    int rightPoint = 0;

    // 获取中位数
    for (int i = 0; i <= n; ++i)
    {
        // 对数组进行判断是否越界
        if ( leftPoint >= nums1.size() ) // 即将进行的操作下标将越界
        {
            if (midData == 0)
                midData = ( (double)( nums2[m - leftPoint] + nums2[n - leftPoint] ) ) / 2;
            else
                midData = ( midData + nums2[n - leftPoint] ) / 2;
            return midData;
        }
        if ( rightPoint >= nums2.size() ) // 即将进行的操作下标将越界
        {
            if (midData == 0)
                midData = ( (double)( nums1[m - rightPoint] + nums1[n - rightPoint] ) ) / 2;
            else
                midData = ( midData + nums1[n - rightPoint] ) / 2;
            return midData;
        }

        // 向后推进
        if (nums1[leftPoint] <= nums2[rightPoint])
        {
            if (i == m)
                midData = (double)nums1[leftPoint];

            if (i == n)
            {
                midData = ( midData + nums1[leftPoint] ) / 2;
                return midData;
            }
            leftPoint++;
        } else
        {
            if (i == m)
                midData = (double)nums2[rightPoint];

            if (i == n)
            {
                midData = ( midData + nums2[rightPoint] ) / 2;
                return midData;
            }
            rightPoint++;
        }
    }
    return midData;
}

这里出现了一个很奇怪的事情,我的方法在leetcode上显示的时间耗时是32ms,我采用16ms的方法,提交之后还是显示的32ms,于是我在自己电脑上写了测试用例计算耗时:

#include <iostream>
#include <windows.h>
#include <vector>
#include <unordered_map>

using namespace std;

// 关闭 IO 同步
static const auto io_sync_off = []()
{
    // turn off sync
    std::ios::sync_with_stdio(false);
    // untie in/out streams
    std::cin.tie(nullptr);
    return nullptr;
}();

double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2)
{
    int size = 0;
    if(nums1.size() != 0 || nums2.size() != 0)
    {
        size = nums1.size() + nums2.size();
    }
    else
    {
        return false;
    }

    int m = 0, n = 0;
    // 判断中位数是两数平均值还是单个值
    if (size % 2 == 1) // 总个数为基数则为中间值
    {
        n = size / 2;
        m = n;
    }
    else                // 偶数则为中间俩个数的平均值
    {
        n = size / 2;
        m = n - 1;
    }

    double midData = 0;
    int leftPoint  = 0;
    int rightPoint = 0;

    // 获取中位数
    for (int i = 0; i <= n; ++i)
    {
        // 对数组进行判断是否越界
        if ( leftPoint >= nums1.size() ) // 即将进行的操作下标将越界
        {
            if (midData == 0)
                midData = ( (double)( nums2[m - leftPoint] + nums2[n - leftPoint] ) ) / 2;
            else
                midData = ( midData + nums2[n - leftPoint] ) / 2;
            return midData;
        }
        if ( rightPoint >= nums2.size() ) // 即将进行的操作下标将越界
        {
            if (midData == 0)
                midData = ( (double)( nums1[m - rightPoint] + nums1[n - rightPoint] ) ) / 2;
            else
                midData = ( midData + nums1[n - rightPoint] ) / 2;
            return midData;
        }

        // 向后推进
        if (nums1[leftPoint] <= nums2[rightPoint])
        {
            if (i == m)
                midData = (double)nums1[leftPoint];
            if (i == n)
            {
                midData = ( midData + nums1[leftPoint] ) / 2;
                return midData;
            }

            leftPoint++;
        } else
        {
            if (i == m)
                midData = (double)nums2[rightPoint];

            if (i == n)
            {
                midData = ( midData + nums2[rightPoint] ) / 2;
                return midData;
            }
            rightPoint++;
        }
    }
    return midData;
}

// 性能最优解法
int findKth(vector<int> nums1, vector<int> nums2, int k) 
{
    if (nums1.empty()) return nums2[k - 1];
    if (nums2.empty()) return nums1[k - 1];
    if (k == 1) return min(nums1[0], nums2[0]);
    int i = min((int)nums1.size(), k / 2), j = min((int)nums2.size(), k / 2);
    if (nums1[i - 1] > nums2[j - 1]) {
        return findKth(nums1, vector<int>(nums2.begin() + j, nums2.end()), k - j);
    } else {
        return findKth(vector<int>(nums1.begin() + i, nums1.end()), nums2, k - i);
    }
    return 0;
}

double findMedianSortedArrays2(vector<int>& nums1, vector<int>& nums2) 
{
    int m = nums1.size(), n = nums2.size();
    return (findKth(nums1, nums2, (m + n + 1) / 2) + findKth(nums1, nums2, (m + n + 2) / 2)) / 2.0;
}


int main() 
{
    struct timespec startTime,endTime;

// 题号 4 测试用例
#if 1
    clock_gettime(CLOCK_MONOTONIC, &startTime);
    vector<int> vector1;
    vector<int> vector2;
    vector1.push_back(1);
    vector1.push_back(2);
    vector2.push_back(4);
    vector2.push_back(6);
    clock_gettime(CLOCK_MONOTONIC, &endTime);
    cout << "pushIn vector time cost :" << endTime.tv_nsec - startTime.tv_nsec << " ns" << endl;

    // my method
    clock_gettime(CLOCK_MONOTONIC, &startTime);
    for (int i = 0; i < 10; ++i)
        findMedianSortedArrays(vector1,vector2);
    clock_gettime(CLOCK_MONOTONIC, &endTime);
    cout << "my   function time cost :" << endTime.tv_nsec - startTime.tv_nsec << "ns" << endl;

    // 最佳方法
    clock_gettime(CLOCK_MONOTONIC, &startTime);
    for (int i = 0; i < 10; ++i)
        findMedianSortedArrays2(vector1,vector2);
    clock_gettime(CLOCK_MONOTONIC, &endTime);
    cout << "best function time cost :" << endTime.tv_nsec - startTime.tv_nsec << "ns" << endl;
#endif

    return 0;  
}

我的电脑上函数运行10次耗时如图所示。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值