编程技巧:如何优雅地合并两个有序数组?

news2024/9/20 6:14:53

目录

  • 题目
    • 引用
    • 描述
      • 1.直接合并 排序
      • 2.指针
      • 3.后逆向双指针
      • 进阶:你可以设计实现一个时间复杂度为 O(m + n) 的算法解决此问题吗?
      • 总结

题目

来自力扣

引用

合并两个有序数组

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

请你合并 nums2 到 nums1 中,使合并后的数组同样按 非递减顺序 排列。

注意:最终,合并后数组不应由函数返回,而是存储在数组 nums1 中。为了应对这种情况,nums1 的初始长度为 m + n,其中前 m
个元素表示应合并的元素,后 n 个元素为 0 ,应忽略。nums2 的长度为 n 。

描述

示例 1:

输入:nums1 = [1,2,3,0,0,0], m = 3, nums2 = [2,5,6], n = 3
输出:[1,2,2,3,5,6] 解释:需要合并 [1,2,3] 和 [2,5,6] 。 合并结果是 [1,2,2,3,5,6]
,其中斜体加粗标注的为 nums1 中的元素。

示例 2:

输入:nums1 = [1], m = 1, nums2 = [], n = 0 输出:[1] 解释:需要合并 [1] 和 [] 。
合并结果是 [1] 。

示例 3:

输入:nums1 = [0], m = 0, nums2 = [1], n = 1 输出:[1] 解释:需要合并的数组是 [] 和 [1]
。 合并结果是 [1] 。 注意,因为 m = 0 ,所以 nums1 中没有元素。nums1 中仅存的 0
仅仅是为了确保合并结果可以顺利存放到 nums1 中。

提示:

nums1.length == m + n
nums2.length == n
0 <= m, n <= 200
1 <= m + n <= 200
-109 <= nums1[i], nums2[j] <= 109

最直观的方法是先将数组 nums 2放进数组 nums 1 的尾部,
然后直接对整个数组进行排序。

1.直接合并 排序


class Solution {
    public void merge(int[] nums1, int m, int[] nums2, int n) {
     for(int i=0; i!=n; i++){
        nums1[m+i]=nums2[i];
     }
     Arrays.sort(nums1);
    }
}

2.指针

class Solution {
    public void merge(int[] nums1, int m, int[] nums2, int n) {
        int i = m - 1; // 指向 nums1 中最后一个元素的指针
        int j = n - 1; // 指向 nums2 中最后一个元素的指针
        int k = m + n - 1; // 指向 nums1 的末尾的指针

        // 从两个数组的末尾开始,比较并合并元素
        while (j >= 0) {
            if (i >= 0 && nums1[i] > nums2[j]) {
                nums1[k] = nums1[i--];
            } else {
                nums1[k] = nums2[j--];
            }
            k--;
        }
    }
}

以下是代码的逻辑和特点:

  1. 初始化指针

    • p1p2 分别初始化为 m - 1n - 1,指向 nums1nums2 的最后一个元素。
  2. 初始化尾部指针

    • tail 初始化为 m + n - 1,指向 nums1 数组的末尾位置,这是合并后数组的最后一个元素的位置。
  3. 循环逻辑

    • 循环继续进行,直到 p1p2 至少有一个到达数组的开始位置(即 p1 >= 0p2 >= 0)。
  4. 选择元素

    • 如果 p1 已经到达数组的开始位置(即 -1),则选择 nums2 中的元素。
    • 如果 p2 已经到达数组的开始位置,选择 nums1 中的元素。
    • 如果两个指针都在数组范围内,比较 nums1[p1]nums2[p2] 的值,选择较小的元素。
  5. 填充尾部

    • 将选择的元素 cur 放入 nums1[tail],然后更新 tail 的位置。
  6. 指针更新

    • 根据选择的元素来自哪个数组,相应地更新 p1p2 的值。
  7. 循环结束

    • p1p2 都到达数组的开始位置时,循环结束,此时 nums1 已经被成功合并。

这段代码正确地实现了合并两个有序数组的功能,并且避免了不必要的排序操作。它使用了双指针技术,从两个数组的末尾开始,逐步向前合并,直到所有元素都被合并到 nums1 中。这种方法的时间复杂度为 O(m+n),空间复杂度为 O(1),因为它只使用了常数级别的额外空间。

3.后逆向双指针

public class Solution {
    // 此方法将nums2合并到nums1中,使得nums1成为有序数组
    public void merge(int[] nums1, int m, int[] nums2, int n) {
        // p1指向nums1的最后一个实际元素,p2指向nums2的最后一个实际元素,tail指向合并后nums1的最后一个位置
        int p1 = m - 1, p2 = n - 1;
        int tail = m + n - 1;
        int cur; // cur用于存储当前比较的元素
        
        // 当nums1和nums2中还有元素未被合并时,继续循环
        while (p1 >= 0 || p2 >= 0) {
            // 如果nums1已经遍历完,直接将nums2中剩余的元素复制到nums1中
            if (p1 == -1) {
                cur = nums2[p2--];
            } 
            // 如果nums2已经遍历完,直接将nums1中剩余的元素复制到nums1中
            else if (p2 == -1) {
                cur = nums1[p1--];
            } 
            // 如果nums1中的当前元素大于nums2中的当前元素,将其复制到nums1的尾部,并移动p1指针
            else if (nums1[p1] > nums2[p2]) {
                cur = nums1[p1--];
            } 
            // 如果nums2中的当前元素大于等于nums1中的当前元素,将其复制到nums1的尾部,并移动p2指针
            else {
                cur = nums2[p2--];
            }
            // 将cur复制到nums1的tail位置,并将tail向前移动
            nums1[tail--] = cur;
        }
    }
}

进阶:你可以设计实现一个时间复杂度为 O(m + n) 的算法解决此问题吗?


class Solution {
    public void merge(int[] nums1, int m, int[] nums2, int n) {
          // 创建两个指针分别指向两个数组的开头
    int p1 = 0, p2 = 0;
    // 创建一个临时数组来存储合并后的结果
    int[] temp = new int[m + n];
    // 创建一个指针指向临时数组的开头
    int p = 0;

    // 循环遍历两个数组,直到其中一个数组遍历完毕
    while (p1 < m && p2 < n) {
        if (nums1[p1] < nums2[p2]) {
            temp[p++] = nums1[p1++];
        } else {
            temp[p++] = nums2[p2++];
        }
    }

    // 如果 nums1 数组还有剩余元素,将其全部复制到临时数组中
    while (p1 < m) {
        temp[p++] = nums1[p1++];
    }

    // 如果 nums2 数组还有剩余元素,将其全部复制到临时数组中
    while (p2 < n) {
        temp[p++] = nums2[p2++];
    }

    // 将临时数组的元素复制回原始数组 nums1
    System.arraycopy(temp, 0, nums1, 0, m + n);
    }
}

总结

一天的学习结束啦,晚上可以休息啦。明天见

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1963139.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

用友时空KSOA 多处 SQL注入漏洞复现

0x01 产品简介 用友时空 KSOA 是建立在 SOA 理念指导下研发的新一代产品,是根据流通企业前沿的 IT 需求推出的统一的IT基础架构,它可以让流通企业各个时期建立的 IT 系统之间彼此轻松对话。 0x02 漏洞概述 用友时空KSOA系统 PrintZP.jsp、PrintZPFB.jsp、PrintZPYG.jsp、P…

单细胞|MEBOCOST·基于代谢物的细胞通讯预测(一)

import os,sys import scanpy as sc import pandas as pd import numpy as np from matplotlib import pyplot as plt import seaborn as sns from mebocost import mebocost 1. 创建 mebocost 对象 adata sc.read_h5ad(data/demo/raw_scRNA/demo_HNSC_200cell.h5ad) ## che…

鸿蒙应用框架开发【首选项】 本地数据与文件

首选项 简介 本示例使用ohos.data.preferences接口&#xff0c;展示了使用首选项持久化存储数据的功能。 效果预览 使用说明 1.点击顶部titleBar的右侧切换按钮&#xff0c;弹出主题菜单&#xff0c;选择任意主题则切换相应的主题界面&#xff1b; 2.退出应用再重新进入&a…

【简历】广东某二本大学:JAVA秋招简历指导,简历通过率比较低

注&#xff1a;为保证用户信息安全&#xff0c;姓名和学校等信息已经进行同层次变更&#xff0c;内容部分细节也进行了部分隐藏 简历说明 这是一份25届二本Java同学的简历。二本同学因为学校本身不是特别出彩&#xff0c;求职目标基本是小公司。并且这个同学项目部分重复度非…

Python环境搭建与PyCharm安装激活教程

一、Python环境搭建 标题&#xff1a;下载Python解释器 步骤1&#xff1a;访问Python官网 打开浏览器&#xff0c;输入Python官方网站地址&#xff1a;Welcome to Python.org。 步骤2&#xff1a;下载Python安装包 将鼠标移动到Downloads菜单上&#xff0c;选择适合你操作…

【区块链+绿色低碳】北京:全国首例区块链 + 绿色出行项目 | FISCO BCOS应用案例

在 2021 年全国两会上&#xff0c;“碳达峰”和“碳中和”被首次写入《政府工作报告》&#xff0c;我国争取在 2030 年前实现“碳达峰”&#xff0c; 2060 年前实现“碳中和”。随着经济社会的快速发展、工业化和城镇化进程不断加快&#xff0c;交通运输领域作为碳排放“大 户”…

智能小家电的跨境渠道有哪些?入驻百思买还是选择做亚马逊?——WAYLI威利跨境助力商家

在全球化贸易背景下&#xff0c;智能小家电企业面临机遇与挑战。消费者追求高品质生活&#xff0c;智能小家电市场需求旺盛&#xff0c;跨境销售成为拓展市场、提升品牌影响力的关键。选择合适的跨境渠道至关重要。以下是智能小家电跨境销售的主要渠道&#xff0c;并深入分析了…

爆款!288页Python核心知识笔记(附思维导图,建议收藏)

不少朋友在学习Python时&#xff0c;都会做大量的笔记&#xff0c;随着学习进度的增加&#xff0c;笔记越来越厚&#xff0c;但有效内容反而越来越少。 今天就给大家分享一份288页Python核心知识笔记&#xff0c;相较于部分朋友乱糟糟的笔记&#xff0c;这份笔记更够系统地总结…

对JAVA的包package的理解

一直理解不了java中的包&#xff0c;包括下图中的这种在几乎每个java程序的开头都有的import和package。今天在实际应用中突然理解了。 第一行代码 package org.example 是用来标识当前这个Java文件属于org.example。这里说明一下&#xff0c;通常一个.java文件就是一个Java类…

【Webpack 踩坑】img 标签图片加载不出来

问题&#xff1a;在html的img标签路径解析错误&#xff0c;导致加载不出来 一直用框架开发&#xff0c;好久没用过webpack写原生代码&#xff0c;一下子踩了好多坑… 图片位置&#xff1a; 其中一个就是在html中写了图片地址&#xff1a; <!-- src/pages/index.html --&…

鸿蒙(HarmonyOS)DatePicker+TimePicker时间选择控件

一、操作环境 操作系统: Windows 11 专业版、IDE:DevEco Studio 3.1.1 Release、SDK:HarmonyOS 3.1.0&#xff08;API 9&#xff09; 二、效果图 可实现两种选择方式&#xff0c;可带时分选择&#xff0c;也可不带&#xff0c;使用更加方便。 三、代码 SelectedDateDialog…

Qt for MCUs 2.8 LTS已发布

本文翻译自&#xff1a;Qt for MCUs 2.8 LTS released 原文作者&#xff1a;Qt Group高级产品经理Yoann Lopes 我们很高兴地宣布Qt for MCUs 2.8 LTS版本已发布&#xff0c;该版本带来了激动人心的新变化&#xff0c;如GUI的构建模块、构建工具工作流程的改进、对Infineon TRA…

书生大模型实战营闯关 - 8GB显存玩转书生大模型demo

创建开发机 创建一个使用10%GPU算力&#xff0c;cuda12.2系统的开发机&#xff0c;并启动。由于开发机的IO性能较差&#xff0c;开发机共享盘中已经创建好了本次实验所需要的conda环境 # 启动共享的conda环境 conda activate /root/share/pre_envs/icamp3_demo部署cli模型 创…

解读Solana流动性质押发展现状:市场格局的悄然转变

随着区块链技术的发展和去中心化金融&#xff08;DeFi&#xff09;生态系统的壮大&#xff0c;流动性质押&#xff08;Liquid Staking&#xff09;已经成为市场中的热门话题。尽管以太坊在这一领域占据了主导地位&#xff0c;但Solana也在快速追赶&#xff0c;并展现出其独特的…

微服务事务管理(分布式事务问题 理论基础 初识Seata XA模式 AT模式 )

目录 一、分布式事务问题 1. 本地事务 2. 分布式事务 3. 演示分布式事务问题 二、理论基础 1. CAP定理 1.1 ⼀致性 1.2 可⽤性 1.3 分区容错 1.4 ⽭盾 2. BASE理论 3. 解决分布式事务的思路 三、初识Seata 1. Seata的架构 2. 部署TC服务 3. 微服务集成Se…

通俗易懂生成式人工智能(Generative AI)

生成式人工智能&#xff08;Generative AI&#xff09; 人工智能是目标&#xff0c;生成式人工智能就是我们的目标之一。 基本概念 什么是生成式人工智能&#xff1f; 生成式人工智能就是让机器产生复杂的、有结构的物件&#xff0c;如&#xff1a;文本、语音、图像等。而这…

笔试练习day1

目录 数字统计题目解析解法(枚举数字拆分)代码 两个数组的交集题目解析解法哈希表代码 点击消除题目解析解法栈代码 感谢各位大佬对我的支持,如果我的文章对你有用,欢迎点击以下链接 &#x1f412;&#x1f412;&#x1f412; 个人主页 &#x1f978;&#x1f978;&#x1f97…

4 款最佳 C# 无头浏览器

摘要&#xff1a; 在当今大数据时代&#xff0c;高效的数据采集成为众多项目的关键一环。对于偏好C#语言的开发者而言&#xff0c;无头浏览器是实现网页自动化交互、数据抓取的强大工具。本文将深入探讨四款顶尖的C#无头浏览器库&#xff0c;分析它们的特性和应用场景&#xf…

No J-Link found

线拔了&#xff0c;重插即可&#xff0c;顺便按按板子上的按钮 这里配置如下图

根据需求修改el-tab的默认样式

根据需求修改el-tab的默认样式 样式代码&#xff1a; <style lang"scss" scoped>//去掉了最下面的那条线:deep(.el-tabs--card > .el-tabs__header){border-bottom: none}//单独给每一项添加下边框、修改背景色:deep(.el-tabs--card > .el-tabs__heade…