【冲击秋招,面试经典150题】1.合并两个有序数组(数组/字符串)

题目描述

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

请你 合并 n u m s 2 nums2 nums2 n u m s 1 nums1 nums1 中,使合并后的数组同样按 非递减顺序 排列。

题解:

常规1:暴力插入法

思路:遍历数组 n u m s 2 nums2 nums2 ,对每次遍历得到的元素 n u m num num ,去遍历数组 n u m s 1 nums1 nums1 查找待插入的位置。

抽象的话也不多说,直接上 Code:

class Solution {
    public void merge(int[] nums1, int m, int[] nums2, int n) {
        if(n == 0) return; // nums2中没有元素,不用插入
        // nums1中没有元素,则直接把nums2复制给nums1即可
        if(m == 0) {
            for(int i = 0; i < n; i++) {
                nums1[i] = nums2[i];
            }
            return;
        }
        int pos = 0, size = m; // 定义pos为插入位置索引,size为当前nums1中包含的元素个数
        for(int i = 0; i < n; i++) {
            int num = nums2[i]; // 取出当前遍历的元素
            boolean flag = false; // 定义flag,标志是否已经插入到nums1中,初始为false,代表未插入
            for(; pos < size; pos++) {
            	// 找到插入位置
                if(num <= nums1[pos]) {
                    insertNum(num, nums1, pos, size); // 插入方法
                    flag = !flag; // 改变插入状态为已插入
                    pos++; // pos索引向后移
                    break; // 
                }                    
            }
            // nums2中剩余未插入元素直接插入到nums1的末尾
            if(pos == size && !flag) {
                while(i < n) {
                    nums1[pos++] = nums2[i++];
                }
                break;
            }
            size++; // 插入后要对size进行自增加1
        }
    }

    public void insertNum(int num, int[] nums, int from, int size) {
    	// 从后向前遍历,把待插入位置后面的元素全部向后移动一个索引位置
        for(int i = size; i > from; i--) {
            nums[i] = nums[i - 1];
        }
        nums[from] = num; // 将待插入元素插入到待插入的索引位置
    }
}

这个方法应该是读完题,立即可以想到的方法之一,比较常规。

常规2:类比归并法

思路:另外开辟一个长度为 m + n m + n m+n 的数组,模拟归并排序的归并操作。

直接上 Code:

class Solution {
    public void merge(int[] nums1, int m, int[] nums2, int n) {
        if(n == 0) return;
        if(m == 0) {
            for(int i = 0; i < n; i++) {
                nums1[i] = nums2[i];
            }
            return;
        }
        int[] mergeNums = new int[m + n]; // 定义mergeNums为模拟归并的数组,长度为m + n
        int i = 0, j = 0, k = 0; // 定义i,j,k为nums1,nums2和mergeNums的遍历索引
        // 遍历nums1和nums2,比较每次遍历的两个元素,其中较小的一个插入到mergeNums中
        while(i < m && j < n) {
            if(nums1[i] < nums2[j]) mergeNums[k] = nums1[i++];
            else mergeNums[k] = nums2[j++];
            k++;
        }
        while(i < m) mergeNums[k++] = nums1[i++]; // 如果nums1中还有元素,则全部插入到mergeNums末尾
        while(j < n) mergeNums[k++] = nums2[j++]; // 如果nums2中还有元素,则全部插入到mergeNums末尾
        for(int p = 0; p < m + n; p++) nums1[p] = mergeNums[p]; // 将mergeNums复制给nums1
    }
}

这个方法是类比归并排序中的归并操作,也是常规操作。

常规3:逆序归并法

思路:由于 n u m s 1 nums1 nums1 的长度是 m + n m + n m+n ,我们不妨利用 n u m s 1 nums1 nums1 初始没有元素的空间,也就是逆序归并法,每次比较出最大的元素从后往前插入到 n u m s 1 nums1 nums1 中。

直接上 Code:

class Solution {
    public void merge(int[] nums1, int m, int[] nums2, int n) {
        if(n == 0) return;
        if(m == 0) {
            for(int i = 0; i < n; i++) {
                nums1[i] = nums2[i];
            }
            return;
        }
        int i = m - 1, j = n - 1, k = m + n - 1; // 定义从后向前的索引
        while(i >= 0 && j >= 0) {
            if(nums1[i] > nums2[j]) nums1[k] = nums1[i--];
            else nums1[k] = nums2[j--];
            k--;
        }
        while(i >= 0) nums1[k--] = nums1[i--];
        while(j >= 0) nums1[k--] = nums2[j--];
    }
}

非常规:排序法

思路:直接把 n u m s 2 nums2 nums2 中元素插入到 n u m s 1 nums1 nums1 的末尾,之后对 n u m s 1 nums1 nums1 进行排序操作。

话不多说,直接上 Code:

class Solution {
    public void merge(int[] nums1, int m, int[] nums2, int n) {
        if(n == 0) return;
        if(m == 0) {
            for(int i = 0; i < n; i++) {
                nums1[i] = nums2[i];
            }
            return;
        }
        int i = m, j = 0;
        while(j < n) nums1[i++] = nums2[j++]; // 直接把nums2插入到nums1末尾
        mergeSort(nums1, 0, m + n - 1); // 对nums1进行排序,这里使用归并排序
    }

    public void mergeSort(int[] nums, int l, int r) {
        if(l >= r) return;
        int mid = (l + r) >> 1;
        mergeSort(nums, l, mid);
        mergeSort(nums, mid + 1, r);
        int[] mergeNums = new int[r - l + 1];
        int i = l, j = mid + 1, k = 0;
        while(i <= mid && j <= r) {
            if(nums[i] < nums[j]) mergeNums[k] = nums[i++];
            else mergeNums[k] = nums[j++];
            k++;
        }
        while(i <= mid) mergeNums[k++] = nums[i++];
        while(j <= r) mergeNums[k++] = nums[j++];
        for(int p = l; p <= r; p++) nums[p] = mergeNums[p - l];
    }
}

这个方法可能读完题就能想到,但并非此题真正意图,因为题中明确提示了,按 非递减顺序 排列。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值