首页 > 其他分享 >合并两个有序数组

合并两个有序数组

时间:2024-12-29 12:29:12浏览次数:6  
标签:数组 复杂度 合并 number numFirIndex 有序 nums1 nums2 数字

合并两个有序数组

小哆啦今天开始每日更新力扣150算法题目。

题目链接88. 合并两个有序数组 - 力扣(LeetCode)

小哆啦的最初的做法

在遥远的代码星球上,小哆啦接到了一个任务——要把两个数组 nums1nums2 合并成一个有序的大数组。他灵机一动,决定先搞个大盘子(新数组 arr),让两个小盘子里的数依次比大小,谁小就先跳到大盘子里。

于是小哆啦开始了以下操作:

  1. 他站在两个数组的开头,左手拿着 nums1 的第一个数字,右手拿着 nums2 的第一个数字。
  2. 小哆啦心想:“嘿,谁小谁先跳到大盘子里!我再挪个位置继续比。”
  3. 如果一个盘子里的数字跳完了,小哆啦直接把另一个盘子里的所有数字统统倒进大盘子。

最后,他把大盘子里的数字一个个搬回到 nums1 的空位置,任务完成!

小哆啦的最初代码实现

function merge(nums1: number[], m: number, nums2: number[], n: number): void {
    let numFirIndex: number = 0, numSecIndex: number = 0; // 两个指针指向两个盘子的起始位置
    let arr: number[] = []; // 小哆啦的大盘子,用来临时存放合并后的结果

    // 开始“比大小游戏”
    while (numFirIndex < m || numSecIndex < n) {
        if (numFirIndex === m) {
            // nums1 的数字搬完了,剩下的全是 nums2 的
            arr.push(nums2[numSecIndex]);
            numSecIndex++;
        } else if (numSecIndex === n) {
            // nums2 的数字搬完了,剩下的全是 nums1 的
            arr.push(nums1[numFirIndex]);
            numFirIndex++;
        } else if (nums1[numFirIndex] <= nums2[numSecIndex]) {
            // nums1 的当前数字更小,放到大盘子里
            arr.push(nums1[numFirIndex]);
            numFirIndex++;
        } else {
            // nums2 的当前数字更小,放到大盘子里
            arr.push(nums2[numSecIndex]);
            numSecIndex++;
        }
    }

    // 把大盘子里的内容重新搬回 nums1
    for (let index: number = 0; index < m + n; index++) {
        nums1[index] = arr[index];
    }
}

时间复杂度 & 空间复杂度:

  • 时间复杂度: O(m+n)
    小哆啦每次比较或搬运一个数字,总共要处理 m+n个数字,所以时间复杂度是线性的。
  • 空间复杂度: O(m+n)
    大盘子 arr 的大小取决于两个数组的总长度,所以需要额外的 O(m+n)的空间来存储中间结果。

小哆啦的原地合并大冒险

厨房的主厨对他说:“小哆啦,你这方法不够优雅啊!”。

小哆啦挠了挠头,觉得很不服气,但他还是决定重新思考。
好嘞!让我们把这个故事写得更详细些,配合小哆啦的天才灵感,打造一个史诗级的代码故事!

天才的灵感

小哆啦猛然发现,nums1 其实很贴心地在后面给自己留了一大段空地(多出来的 0),这些空地完全可以用来直接放合并的结果啊!

于是,他兴奋地喊道:“我直接在 nums1 里操作,从后面开始比大小,把大数字依次放到后面的位置,这样就不会覆盖掉未处理的数据啦!”

就这样,小哆啦的“原地合并法”诞生了:

function merge(nums1: number[], m: number, nums2: number[], n: number): void {
    let numFirIndex: number = m - 1; // 指向 nums1 的有效部分的最后一个元素
    let numSecIndex: number = n - 1; // 指向 nums2 的最后一个元素
    let mergeIndex: number = m + n - 1; // 指向 nums1 的总长度最后一个位置

    // 从后往前合并数字
    while (numSecIndex >= 0) {
        if (numFirIndex >= 0 && nums1[numFirIndex] > nums2[numSecIndex]) {
            // 如果 nums1 的当前数字更大,就放到最后的位置
            nums1[mergeIndex] = nums1[numFirIndex];
            numFirIndex--;
        } else {
            // 如果 nums2 的当前数字更大(或等于),就放到最后的位置
            nums1[mergeIndex] = nums2[numSecIndex];
            numSecIndex--;
        }
        mergeIndex--; // 每次放完一个数字,往前挪一个位置
    }
}

详细解析

  1. 两个指针,一个目标:

    • 小哆啦决定用两个指针分别指向 nums1nums2 的最后一个有效数字。
    • 再用一个指针指向 nums1 的末尾(也就是空地的最后一个位置),每次比较后把更大的数字放到这里。
  2. 从后往前,避免覆盖:

    • 因为 nums1 后面有一段空地,所以他从后往前填充,不会覆盖掉 nums1 的有效部分。
  3. 特殊情况处理:

    • 如果 nums1 的有效数字用完了,直接把 nums2 剩下的数字搬过来。
    • 如果 nums2 的数字用完了,不用管了,因为 nums1 剩下的数字本身就已经有序。
  4. 循环结束的条件:

    • 小哆啦只需要管 nums2 是否处理完,因为 nums1 的有效部分已经在目标数组里。

时间复杂度 & 空间复杂度

  • 时间复杂度: O(m+n))
    小哆啦依次处理每个数字,总共 m+n 个数字,时间复杂度是线性的。

  • 空间复杂度: O(1)
    小哆啦直接在 nums1 上操作,没有用额外的大盘子,内存利用率非常高

    这次,小哆啦的操作堪称艺术!
    他不仅完成了任务,还节约了空间,成为了代码星球上的“环保小达人”。
    现在,厨房宽敞了,锅碗瓢盆也不乱放,效率杠杠的

标签:数组,复杂度,合并,number,numFirIndex,有序,nums1,nums2,数字
From: https://blog.csdn.net/weixin_62520914/article/details/144803501

相关文章

  • 数组与字符串 - 一维数组、二维数组、字符串处理
    引言数组和字符串是编程中非常常见的数据结构,用于存储和操作一组相同类型的数据。C++提供了丰富的语法和库函数来处理数组和字符串,使得这些操作既简单又高效。本文将详细介绍C++中一维数组、二维数组和字符串的使用方法,并通过示例帮助读者更好地理解。1.一维数组一维数......
  • Go基础之数组,切片,Map
    目录1数组1.1简介1.1.1声明数组1.1.2初始化数组1.3访问数组元素1.4多维数组1.4.1二维数组1.4.2初始化二维数组1.4.3访问二维数组1.5数组与函数2切片2.1简介2.1.1定义切片2.1.2切片初始化2.1.3len()和cap()函数2.1.4空(nil)切片2.2切片操作2.2.1切片截取2.2.2......
  • 2024-12-28:求出出现两次数字的 XOR 值。用go语言,给定一个数组 nums,其中的数字出现的频
    2024-12-28:求出出现两次数字的XOR值。用go语言,给定一个数组nums,其中的数字出现的频率要么是一次,要么是两次。请找出所有出现两次的数字,并计算它们的按位XOR值。如果没有数字出现两次,则返回0。1<=nums.length<=50。1<=nums[i]<=50。nums中每个数字要么出现过一......
  • 34. 在排序数组中查找元素的第一个和最后一个位置
    在排序数组中查找元素的第一个和最后一个位置给你一个按照非递减顺序排列的整数数组nums,和一个目标值target。请你找出给定目标值在数组中的开始位置和结束位置。如果数组中不存在目标值target,返回[-1,-1]。你必须设计并实现时间复杂度为O(logn)的算法解决此问题。示......
  • js里面对数组的一些独特/特殊函数
    数组.foreach(函数)这个函数里面默认的参数就是数组里面的每一个元素注意:这里面函数不需要返回参数vararr1=[1,2,3,4,5,6,7]arr1.foreach(function(item){console.log(item)})得到1234567新数组=数组.filter(函数)本质上是foreach的进阶版,在函数中对每一个......
  • C语言——指针(字符指针、指针数组、数组指针、数组参数、指针参数)
    文章目录一、字符指针二、指针数组三、数组指针1.数组指针的创建与意义2.数组指针的使用4.数组参数、指针参数1.一维数组传参2.二维数组传参一、字符指针一般的我们字符指针如下:将c的地址取出来放到p中去,指针p的类型是char*。#include<stdio.h>intmain()......
  • c语言书籍排序 多数组协同排序 按价格排序【书名同步】 带有空格的字符串读取
    题目:编写程序,从键盘输入n(n<10)本书的名称和定价并存入结构数组中,按单价从小到大排序并输出排序后的书籍信息。输入输出示例:括号内为说明,无需输入输出输入样例:3(n=3)ProgramminginC21.5ProgramminginVB18.5ProgramminginDelphi20输出样例:Programmingin......
  • LeetCode 23 : 合并K个升序链表
    题目:解题思路:1.将多个链表合并为两个链表2.使用21题用的,将两个有序链表合并代码示例:packagecom.zy.leetcode.LeetCode_23;/***@Author:zy*@Date:2024-12-26-21:37*@Description:合并K个升序链表*多路递归*/publicclassListNode_23{priv......
  • 树状数组学习笔记
    树状数组概念\(a[i]\)数组存储当前序列数据\(s[i]\)用来存储区间和,其中下标i值代表的是一段区间,其区间长度取决于low_bit(i)例如:\(s[4]\),4对应二进制100,因此low_bit(i)=100,其长度为4,所以s[4]存储的为a[1]~a[4]的和。\(s[6]\),6对应二进制110,因此low_bit(i)=10,其长度为2,......
  • 写一个方法把如下字符串按运算符切割成数组`18x2÷9+1-6`
    在前端开发中,你可以使用JavaScript的String.prototype.split()方法和正则表达式来达到这个目的。由于你的字符串中包含多种运算符(x、÷、+、-),你可能需要使用一个稍微复杂的正则表达式来匹配这些运算符,并将字符串分割成数组。下面是一个示例函数,它接受一个字符串作为输入,并返回一......