LeetCode 88、合并两个有序数组(c++)(0基础也可看懂附带语法)(官方数据结构入门计划第四题)

134 阅读3分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第4天,点击查看活动详情

先看一下题目:

给你两个按 非递减顺序 排列的整数数组 nums1 和 nums2,另有两个整数 m 和 n ,分别表示 nums1 和 nums2 中的元素数目。 

 请你 合并 nums2 到 nums1 中,使合并后的数组同样按 非递减顺序 排列。

 注意:最终,合并后数组不应由函数返回,而是存储在数组 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 

[1]、合并之后再排序

这个想法不会特别难想(但一开始我没往这个方向想,一开始我就是写的第二种,后来看一下别人还有什么不同的想法,才看到的,但这个思想理解起来很自然,而且并不是很难,代码也很简洁,就是效率有点低),但是有一个缺点就是没有利用到数组是有序的这个条件。

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());//头文件是algorithm,sort就是把容器排序的函数
    }
};

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

空间复杂度:O(log(m+n))。

时间和空间复杂度都是快速排序的。

[2]、逆向双指针法

因为本来第一个数组就有多的空位是用来插入第二个数组的,所以我们想这两个数组开始从大到小检索,这样就能在第一个数组最后一个空位从后往前插入元素,这样就充分利用了空间,和数组有序这个特点。

nums1 = [1,2,3,0,0,0], m = 3, nums2 = [2,5,6], n = 3 ,用这个例子来举例就是先检索nums1的3和nums2的6,6比3大,6插入nums1最后一个位置(也就是最后一个0),然后用nums2的指针往左移就检索到了5,再用5和3比较,(指针检索,插入完当前的数就往左移,直至边界)依次循环上面的步骤。

此处代码是leetcode官方给的,和我写的有出入。

class Solution {
public:
    void merge(vector<int>& nums1, int m, vector<int>& nums2, int n) {
        int p1 = m - 1, p2 = n - 1;
//p1是nums1的指针,指向nums1最后一个“有效”(而不是空位)元素,即例子一中的3        int tail = m + n - 1;
//这个就是指向nums1的最后一个元素,就是空位,也就是例子一中的最后一个0        int cur;
        while (p1 >= 0 || p2 >= 0) {//或代表两个数组要同时检索完才推出循环
            if (p1 == -1) {//p1==-1代表nums1检索完了,后面就只需要检索nums2就可以了
                cur = nums2[p2--];
            }
            else if (p2 == -1) {//与上同理
                cur = nums1[p1--];
            }
            else if (nums1[p1] > nums2[p2]) {
//如果两个数组都没有检索完,就按如解释所说的不停检索,比较                cur = nums1[p1--];
            }
            else {
                cur = nums2[p2--];
            }
            nums1[tail--] = cur;
        }
    }
};



//这个是我写的,我和官方有一点不同的就是,跳出循环我用的是&&,而不是||,但大体上大同小异,上面的
官方解给了注释,下面的我就不写了。
class Solution {
public:
    void merge(vector<int>& nums1, int m, vector<int>& nums2, int n) {
        
        int Size=nums1.size()-1;
        m--;
        n--;
        while(m>=0&&n>=0){               
               if(nums1[m]>nums2[n]){                nums1[Size]=nums1[m];
                m--;
                Size--;
            }
            else{
                nums1[Size]=nums2[n];
                n--;
                Size--;
            }
        }
        if(m<0){
            while(n>=0){
            nums1[Size]=nums2[n];
            n--;
            Size--;
        }
            }
        if(n<0){
                while(m>=0){
                nums1[Size]=nums1[m];
                m--;
                Size--;
            }
        }
        return ;
    }
};

时间复杂度:O(m+n)  指针最多移动m+n次

空间复杂度:O(1) 因为这个是原地算法,只用了常数个空间变量