每日一题:11.2 合并排序的数组

每日一题11.2 合并排序的数组

一、题目概述

刚刚打开这道题的时候,我的想法是:简单题,我应该能很快的AC,但是看到了vector,心里还是有点打怵,因为只是在假期,自己看C++primer plus的时候,浅了解过这一个知识,但从没实际应用过。

再看这个题的要求,排序,说到排序其实还算比较熟悉,毕竟数据结构学过不少,冒泡,插入,归并,快排,都是实操了不少次的算法,这个题我觉得插入排序好一点,但是看到函数名merge又让我联想到归并排序。

二、个人思路

我第一遍的代码是这样子的:

class Solution {
public:
    void merge(vector<int>& A, int m, vector<int>& B, int n) {
        int pos=0;
        for(int i=0;i<n;i++){
            if(A[pos+1]>=B[i]||A[pos+1]==0){
                A.insert(A.begin()+pos+1,B[i]);
                pos++;            
            }
            if(pos==m){
                break;
            }
        }
        A.insert(A.begin()+m+pos,B.begin()+pos,B.begin()+n);
        A.erase(A.begin()+m+n,A.begin()+n+n+m);
        for(int a:A){
            cout<<a;
        }
    }
};

大致的意思是,把A和B两个vector中的数据(先不管0)插入排序,这里的插入我使用了vector的insert,然后再把最后面的所有0都去掉。这里要注意,vector不同于数组,在+1或者-1上逻辑不太一样,有时候会溢出,有时候会少消掉一个0多消掉一个有效的数据,这个到时候写的时候注意一下,我也不太好解释,后面我想清楚怎么解释了再来补充进来。

真是诡计多端的0啊。

这里运用了两个新知识

A.insert(A.begin()+x,B.begin()+x,B.begin()+y);
A.insert(A.begin()+pos+1,B[i])

A.erase(A.begin()+x,A.begin()+y);

这里面insert的两个用法,含义分别是:1.在vectorA的第x个位置上,插入vectorB的第x个位置上的数据~第y个位置上的数据(不包括第y个),也就是说,我们设x=8,y=12,那我们插入的就是B[8]~B[11];第二个就很好理解了,在A的pos+1位置上插入一个B[i]。

erase的用法是,把A从第x个到第y个(不包括y)个数据都除去,这里面就涉及到我上面提到的少消去0这种错误。

以上两个对我来讲算是新知识,这些是我在C++ vector的用法(整理)中查到的,原文大家点击链接就可以看到。vector的相关知识还有很多,但这次我就先只学两个了,多了我也记不住。

这个代码能通过测试样例:

 

但是在最后提交的时候,出现了这样的问题:

这东西我也看不懂,只能看懂一个堆缓冲区溢出,别的不明白是什么意思,可能是某些特殊的数据点我过不去吧,但是样例能过,证明我这个思路和操作应该是对的,但是健壮性不是太好,这个我再慢慢改,同时希望大家可以帮我挑一下错误,非常感谢大家。。。

三、大佬思路

我粗略的翻了一下题解,第一种是最基础的,先把所有数据整合到一起,然后再排序,但我感觉这样没什么意思,而且两个vector已经是被排好序的了,这种方法浪费了一个特征,就没有采纳。

第二种思路是双指针方法:这个方法大概意思就是,弄出一个长度是A.length+B.length的静态数组,然后从A和B里一个一个挑,有归并排序那个味儿了,对得起他的函数名字merge,hhhh。但是我还是感觉,因为他新开了一段空间,没有好好利用vector的性质进行解题,感觉他的空间复杂度应该会比我大一些。

借鉴大佬的代码:

class Solution {
public:
    void merge(vector<int>& A, int m, vector<int>& B, int n) {
        int pa = 0, pb = 0;
        int sorted[m + n];
        int cur;
        while (pa < m || pb < n) {
            if (pa == m) {
                cur = B[pb++];
            } else if (pb == n) {
                cur = A[pa++];
            } else if (A[pa] < B[pb]) {
                cur = A[pa++];
            } else {
                cur = B[pb++];
            }
            sorted[pa + pb - 1] = cur;
        }
        for (int i = 0; i != m + n; ++i) {
            A[i] = sorted[i];
        }
    }
};

时间复杂度:O(m+n)。
指针移动单调递增,最多移动 m+n 次,因此时间复杂度为 O(m+n)。

空间复杂度:O(m+n)。
需要建立长度为 m+n 的中间数组 sorted

作者:LeetCode-Solution
链接:https://leetcode.cn/problems/sorted-merge-lcci/solution/mian-shi-ti-1001-he-bing-pai-xu-de-shu-zu-by-leetc/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

第三种方法就是逆向双指针,他这个没有用新的数组空间,和我一样直接用的vectorA现成的开好的空间。所谓逆向就是,方法二他是从头往后查,这个他是从尾巴开始查,哪个数最大,他就把那个数插到最后面,把那个0覆盖了,不用像我那样最后还有一步把0都消去那一步,感觉这个方法大体思路跟我的差不多,只不过他是从尾巴往前,我是从头往后,而且他的比我的要简单一点。

这说明什么朋友们,这说明思考问题要换个角度思考,像方法三就是倒过来思考了。

这是方法三的大佬代码:

class Solution {
public:
    void merge(vector<int>& A, int m, vector<int>& B, int n) {
        int pa = m - 1, pb = n - 1;
        int tail = m + n - 1;
        int cur;
        while (pa >= 0 || pb >= 0) {
            if (pa == -1) {
                cur = B[pb--];
            } else if (pb == -1) {
                cur = A[pa--];
            } else if (A[pa] > B[pb]) {
                cur = A[pa--];
            } else {
                cur = B[pb--];
            }
            A[tail--] = cur;
        }
    }
};

时间复杂度:O(m+n)。
指针移动单调递减,最多移动 m+n 次,因此时间复杂度为 O(m+n)。

空间复杂度:O(1)。
直接对数组 A 原地修改,不需要额外空间。

作者:LeetCode-Solution
链接:https://leetcode.cn/problems/sorted-merge-lcci/solution/mian-shi-ti-1001-he-bing-pai-xu-de-shu-zu-by-leetc/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

虽然我觉得我的思路和大佬的思路是差不多的,但是人家的代码跑出来就是全AC,我的后面就有错误。。。所以一定不能眼高手低,觉得自己会和写出来证明自己会真的是两码事。

这让我想起,21年3月份,我有幸旁听了一场东北大学外教的英语口语课,老师的名字叫Evanbell他还提问了我,问我新学期的目标是什么,我说我要培养自己的自律性,要好好制定各个阶段的计划,Evanbell老师很认可我的想法,但他也对我说,To think is one thing,to conduct is another,他告诉我如果去想了,就一定要去做,知行合一才是最好的,这句话也送给看到这篇博客的朋友们,在学习的道路上,我们共勉。

四、总结

今天学到了两个新知识,分别是vector类中的insert函数的两个不同的用法,还有erase函数,并且使用了昨天那道题中涉及到的for ( int a : A ) 这样一个遍历语法,然后日后的学习中,还要培养逆向思维,就比如今天这道题的逆向双指针法。

期待明天的每日一题,我能学到更多新东西,并且能够巩固今天的新知识。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值