NC22 合并两个有序的数组

一、题目描述

NC22合并两个有序的数组
原题链接https://www.nowcoder.com/practice/89865d4375634fc484f3a24b7fe65665?tpId=117
题目大意:给定递增数组A,B,合并A,B的元素,使合并过后的数组是递增的,并且存放在数组A中
注意审题:题目中有一句话:可以假设A数组有足够的空间存放 B数组的元素,即无需考虑溢出问题,或理解为A数组空间足够

二、算法1

算法思路

  1. 总体思想:归并排序思想
  2. 该算法空间复杂度为 O ( 2 ( n + m ) ) O(2(n+m)) O(2(n+m)),时间复杂度 O ( n + m ) O(n+m) O(n+m)
  3. 开辟一个新数组C,定义指针i,j,p,其中三个指针分别指向A,B,C的开头,即i=j=p=0(注意此处的指针并不是C语言中的指针,此处代表指向一个位置的int变量)
  4. 比较i,j所指向的元素,如果A[i]<=B[j],则将A[i]放入C中,否则将B[j]放入C中,即C[p++]=A[i]<=B[j]?A[i++]:B[j++],直到有一个数组为空,或者两个同时为空
  5. 接着再将有剩余的那个数组的元素转移到C中即可
  6. 因为题目要求,最后将C数组拷贝到A数组中

代码实现

class Solution {
public:
    void merge(int A[], int m, int B[], int n) {
  		int i=0,j=0,p=0; 
		int c[m+n];//定义辅助数组C 
		while(i<m&&j<n){
			c[p++]=A[i]<=B[j]?A[i++]:B[j++];//将A[i]和B[j]中小的那个丢入C中 
		}      
		while(i<m){//如果A中有剩余 
			c[p++]=A[i++];
		}
		while(j<n){//如果B中有剩余 
			c[p++]=B[j++];
		}
		for(int i=0;i<p;i++)A[i]=c[i];//因为题目要求,将C数组拷贝到A数组中 
    }
};

三、算法2

算法思路

  1. 较上一个算法,因为A的空间充足,所以我们可以直接在A中进行操作
  2. 定义指针i,j,p,(此处指针意义同上),i,j指针指向原末尾,p指针指向合并后的A末尾 ,即i=m-1,j=n-1,p=m+n-1
  3. 整体思路:比较A[i]和B[j],其中大的,丢到A[p]中,然后p和该指针左移一格。空间复杂度为 O ( n + m ) O(n+m) O(n+m),时间复杂度 O ( n + m ) O(n+m) O(n+m),较上一个算法空间复杂度得到了优化。
  4. 这个时候,就会有同学好奇,会不会出现A[x]还没有填写,但是被覆盖的情况,最后造成A[x]丢失,以至于答案错误的情况?,答案是显然的,既然算法是正确的,那么这种情况就必然不可能出现,下面简单证明一下。
  5. 我们反过来思考,出现上面情况的原因是什么,什么时候会出现?显然是p<i时候,才会造成一个没填入,但是被覆盖的情况
  6. 证明:因为A原本是递增的,所以该情况,p移动的比i多,j指针最多导致p移动n次,此时p=m,而i指针和p指针同时移动时候不会改变p,i之间大小的关系,所以永远有p>=i
  7. 既然算法正确,那么代码也不难理解,跟算法1很相似while(~i&&~j)A[p--]=A[i]>B[j]?A[i--]:B[j--];
  8. 此处~i等价i>=0,因为-1取反过后返回的是0,所以~i的意思就是i!=-1,因为此处是从正轴向左走,所以i!=-1等价i>=0,位运算一般情况比其他运算符速度要快,这也是一种小小优化程序的方法
  9. 最后处理A,B剩余情况,B数组剩余,显然应该将B剩下的丢入A中,但是A剩余我们还需要操作吗?
  10. 答案是不用的,因为此时有’p==i’,剩下的一模一样,我们就不需要转移了(此处如果没有看懂的同学,可以看代码中的注释,此处结合代码讲解比较容易理解)

动画演示

在这里插入图片描述

在这里插入图片描述

代码实现

C++代码
class Solution {
	public:
		void merge(int A[], int m, int B[], int n) {
			int i=m-1,j=n-1,p=m+n-1;//i,j指针指向原末尾,p指针指向合并后的A末尾 
			while(~i&&~j) {// ~i  <==> i>=0,因为-1取反过后返回的是0,位运算一般情况比其他运算符速度要快 
				A[p--]=A[i]>B[j]?A[i--]:B[j--];//合并A、B数组, 比较i,j指针指向的元素,将大的那个丢进去 
			}
			while(~j){//如果B数组还有剩余,则直接全部移到A数组中 
				A[p--]=B[j--];
			}
			/*此处如果出现A数组有剩余的情况,理论上应该执行while(i>=0)A[p--]=A[i--]
			但是如果A有剩余必然有p==i(出现剩余的情况,只有可能一个出现剩余,不可能出现AB数组都剩余的情况) 
			(如果出现AB都剩余,就跟第一个for循环矛盾了) 
			既然对于指针p==i,那么此时这个while循环就会显得很多余 					
			*/	
		}
};
Python代码
class Solution:
    def merge(self ,A,m,B,n):
        i=m-1
        j=n-1
        p=m+n-1 #i,j指针指向原末尾,p指针指向合并后的A末尾 
        while i>=0 and j>=0 :# 合并A、B数组, 比较i,j指针指向的元素,将大的那个丢进去 
            if A[i]>B[j]:
                A[p]=A[i]
                p-=1
                i-=1
            else :
                A[p]=B[j]
                p-=1
                j-=1
        while j>=0 :# 如果B数组还有剩余,则直接全部移到A数组中 
            A[p]=B[j]
            p-=1
            j-=1
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值