领釦网算法学习笔记 - 88

领釦网算法学习笔记

本系列的算法题目来自领釦网

数组类算法第七天

题目:88. 合并两个有序数组

给定两个有序整数数组 *nums1 *和 nums2,将 *nums2 *合并到 *nums1 ,*使得 *num1 *成为一个有序数组。。

示例:

输入:
nums1 = [1,2,3,0,0,0], m = 3
nums2 = [2,5,6],       n = 3

输出: [1,2,2,3,5,6]

说明:

  1. 初始化 nums1nums2 的元素数量分别为 mn
  2. 你可以假设 *nums1 *有足够的空间(空间大小大于或等于 m + n)来保存 nums2 中的元素。

解题过程:

思路一:

看到题的第一个想法是将数据插入然后,数据往后移,但是移动数据的代价太大,之后想的是数组长度够了,直接从后面放就好了。

代码如下:

class Solution {
    public void merge(int[] nums1, int m, int[] nums2, int n) {
        // 反向思维,从前面插入的话移动数据的代价太大,但是从后面排的话就不存在
        
        if((nums1.length == 1) && (nums1[0] == 0)){  // 去除nums1没有数据的情况
            nums1[0] = nums2[0];
            return;
        }
        
        int keynum = m+n-1; // 记录数据保存的最后一个位置
        int arr1 = m-1;
        for(int i = n-1;i>=0;i--){
            for(;arr1>=0;arr1--){
                if(nums2[i] >= nums1[arr1]){
                    nums1[keynum] = nums2[i];
                    keynum--;
                    break;
                }else{
                    nums1[keynum] = nums1[arr1];
                    keynum--;
                }
            }
            if(arr1<0){		// 如果nums1第一个值大于nums2里的值,会导致循环完后nums2的值没有插入完
                nums1[keynum] = nums2[i];	// 此时数据都是小的,直接往前插入即可
                keynum--;
            }
        }        
    }
}
// 5ms

后续思考:

其实写完一想,我觉得没必要写成嵌套循环,毕竟循环次数我是知道的,于是有了思路二。

思路二:

循环次数 = m + n 次,直接写一个这样的循环就可以了。

代码如下:

class Solution {
    public void merge(int[] nums1, int m, int[] nums2, int n) {
        // 反向思维,从前面插入的话移动数据的代价太大,但是从后面排的话就不存在
        
        if((nums1.length == 1) && (nums1[0] == 0)){
            nums1[0] = nums2[0];
            return;
        }
        int k = m+n-1;
        
       for(;k>=0;k--){
            if(m!=0 && n!=0){
                if(nums1[m-1] < nums2[n-1]){
                    nums1[k] = nums2[n-1];
                    n--;
                }else{
                    nums1[k] = nums1[m-1];
                    m--;
                }
            }else if(m==0){
                nums1[k] = nums2[n-1];
                n--;
            }else if(n == 0){
                nums1[k] = nums1[m-1];
                m--;
            }
       }
    }
}
//3ms

后续思考:

​ 这个主要要注意几个特殊的情况,

1. 第一个就是第一个数组直接没有值,第二个数组可能有且只有一个值,这种情况直接将第二个数组的值赋给第一个数组即可,
2. 第一个数组的最小值比第二个数组的部分值或者全部值都大的时候,注意判断不要越界和注意要添加完第二个数组的值;
3. 第二个数组的最小值比第一个数组的部分值或者全部值都大的时候,注意不要越界。
4. 我发现这 2、3 两种情况不是特殊情况,是肯定会出现的。

领釦上面该题其他高质量范例:

class Solution {
  public static void merge(int[] nums1, int m, int[] nums2, int n) {
		//边界:
	   if(n==0) {
		  return;
	   }
		int i = 0,j=0;
		while(i<m) {
			if(nums1[i]<=nums2[j]) {
				i++;
			}else {
				swap(nums1,i++,nums2,j);
				//调整顺序
				while(j+1<n&&nums2[j]>nums2[j+1]) {
					swap(nums2, j,(j+1));
					j++;
				}
				j = 0;
			}
		}
		
		while(j<nums2.length) {
			nums1[i++] = nums2[j++];
		}
	}

	private static void swap(int[] nums2, int i, int j) {
		// TODO Auto-generated method stub
		nums2[i] = nums2[i]^nums2[j];
		nums2[j] = nums2[i]^nums2[j];
		nums2[i] = nums2[i]^nums2[j];
	}

	private static void swap(int[] nums1, int i, int[] nums2, int j) {
		int temp = nums1[i];
		nums1[i] = nums2[j];
		nums2[j] = temp;
	}
}
// 4ms

自我整理:

​ 这里的思路就是先将最大值放到数组二中,然后将数组二的值放到数组后面去。

​ 这部分我没有这样想过,我做这题的时候只想着把数插进去,没有想到先排序然后在插入。

​ 方法很多,需要多个方位的思考。

点赞