当前位置:首页 > 文章列表 > 文章 > java教程 > 最少分组数实现数组拆分方法

最少分组数实现数组拆分方法

2025-12-12 23:06:44 0浏览 收藏
推广推荐
免费电影APP ➜
支持 PC / 移动端,安全直达

最近发现不少小伙伴都对文章很感兴趣,所以今天继续给大家介绍文章相关的知识,本文《最少分组数实现数组转换》主要内容涉及到等等知识点,希望能帮到你!当然如果阅读本文时存在不同想法,可以在评论中表达,但是请勿使用过激的措辞~

将数组转换为目标数组所需的最小分组数

本文探讨了如何通过最少切割次数将一个具有唯一值的输入数组转换为另一个目标数组。核心思想是利用目标数组的元素索引映射,然后遍历输入数组,识别其中连续且顺序正确的元素序列。通过这种方法,我们可以精确计算出需要切割的最小分组数量,从而实现数组的重新排列。

问题定义

给定两个长度相同、元素均为唯一整数的数组,例如 arr1 = [1, 4, 3, 2] 和 arr2 = [1, 2, 4, 3]。我们的目标是将 arr1 切割成最少数量的连续子数组(即“块”),然后通过重新排列这些块来形成 arr2。

例如,对于 arr1 = [1, 4, 3, 2] 和 arr2 = [1, 2, 4, 3]: 我们可以将 arr1 切割为 (1), (4,3), (2) 三个块。 然后重新排列这些块为 (1), (2), (4,3),即可得到 arr2。 因此,所需的最小分组数为 3。

约束条件:

  • 数组中的所有值都是唯一的。
  • 两个数组的大小相同,最大可达1000。
  • 数组中的值均为整数。

错误尝试分析

一种直观但错误的尝试是简单地计算两个数组在相同位置上不匹配元素的数量:

public int process(int[] inp, int[] desired) {
   int ans = 0;
   for (int i = 0; i < inp.length; i++) {
      if (inp[i] != desired[i]) ans++;
   }
   return ans;
}

这种方法的问题在于,它只统计了元素位置上的差异,而没有考虑元素的相对顺序以及通过切割和重排可以形成的连续块。例如,[1,4,3,2] 和 [1,2,4,3],在索引1处 4 != 2,索引2处 3 != 4,索引3处 2 != 3,会得到 3。但对于 [1,2,3] 和 [3,2,1],会得到 3,而实际答案是 3 ((3), (2), (1))。对于 [1,3,2] 和 [1,2,3],会得到 2,实际答案是 2 ((1), (3,2) -> (1), (2), (3) 错误,应该是 (1), (3), (2) 得到 3。正确的切割是 (1), (3,2) -> (1), (2), (3) 无法得到。正确切割是 (1), (3), (2),答案是 3。)。显然,这种基于逐个位置比较的方法无法捕捉到通过重排连续块来最小化切割数的本质。

核心算法与实现

解决此问题的关键在于理解“连续块”的定义。如果 arr1 中的两个相邻元素在 arr2 中也是相邻且顺序正确的,那么它们就可以被视为同一个块的一部分。我们需要做的就是找出 arr1 中有多少次这种“连续性”被打破。

算法步骤:

  1. 建立目标数组索引映射: 由于数组元素唯一,我们可以创建一个映射(Map),将 arr2 中的每个元素与其在 arr2 中的索引关联起来。这个映射将帮助我们快速查找 arr1 中元素在 arr2 中的期望位置。
  2. 遍历输入数组并识别连续块:
    • 初始化一个计数器 count 为 1,表示至少有一个块。
    • 获取 arr1 的第一个元素在 arr2 中的索引,并将其存储为 prevIndex。
    • 从 arr1 的第二个元素开始遍历:
      • 对于当前元素,查找它在 arr2 中的索引,并将其存储为 nextIndex。
      • 如果 nextIndex 等于 prevIndex + 1,这意味着当前元素在 arr2 中紧接着前一个元素,它们属于同一个连续块。此时,更新 prevIndex = nextIndex。
      • 如果 nextIndex 不等于 prevIndex + 1,这意味着当前元素打破了与前一个元素的连续性,形成了一个新的块。此时,将 count 增加 1,并更新 prevIndex = nextIndex。
  3. 返回 count: 最终的 count 值即为所需的最小分组数。

Java 实现示例:

import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

public class ArrayGroupConverter {

    /**
     * 计算将 arr1 转换为 arr2 所需的最小分组数。
     *
     * @param arr1 输入数组
     * @param arr2 目标数组
     * @return 最小分组数
     */
    public static int process(int[] arr1, int[] arr2) {
        // 1. 建立目标数组 arr2 的元素到索引的映射
        Map<Integer, Integer> indexByValue = mapIndices(arr2);

        // 至少有一个分组
        int count = 1;

        // 获取 arr1 第一个元素在 arr2 中的索引
        int prevIndex = indexByValue.get(arr1[0]);

        // 从 arr1 的第二个元素开始遍历
        for (int i = 1; i < arr1.length; i++) {
            // 获取当前 arr1 元素在 arr2 中的索引
            int nextIndex = indexByValue.get(arr1[i]);

            // 如果当前元素在 arr2 中的索引紧接着前一个元素,则它们属于同一块
            if (nextIndex == prevIndex + 1) {
                prevIndex++; // 移动 prevIndex 到当前元素的索引
            } else {
                // 否则,连续性被打破,需要一个新的块
                prevIndex = nextIndex; // 更新 prevIndex 为新块的起始索引
                count++;               // 增加分组计数
            }
        }

        return count;
    }

    /**
     * 将数组元素映射到其索引。
     *
     * @param arr 需要映射的数组
     * @return 元素到索引的映射
     */
    public static Map<Integer, Integer> mapIndices(int[] arr) {
        return IntStream.range(0, arr.length)
            .boxed()
            .collect(Collectors.toMap(
                i -> arr[i],       // 键是数组元素的值
                Function.identity() // 值是元素的索引
            ));
    }

    public static void main(String[] args) {
        // 示例测试
        int[] arr1 = {1, 4, 3, 2};
        int[] arr2 = {1, 2, 4, 3};
        System.out.println("Input arr1: " + java.util.Arrays.toString(arr1));
        System.out.println("Desired arr2: " + java.util.Arrays.toString(arr2));
        System.out.println("Minimum number of groups: " + process(arr1, arr2)); // 预期输出: 3

        int[] arr3 = {1, 2, 3};
        int[] arr4 = {1, 2, 3};
        System.out.println("\nInput arr1: " + java.util.Arrays.toString(arr3));
        System.out.println("Desired arr2: " + java.util.Arrays.toString(arr4));
        System.out.println("Minimum number of groups: " + process(arr3, arr4)); // 预期输出: 1

        int[] arr5 = {3, 2, 1};
        int[] arr6 = {1, 2, 3};
        System.out.println("\nInput arr1: " + java.util.Arrays.toString(arr5));
        System.out.println("Desired arr2: " + java.util.Arrays.toString(arr6));
        System.out.println("Minimum number of groups: " + process(arr5, arr6)); // 预期输出: 3
    }
}

示例运行与输出:

对于 arr1 = [1, 4, 3, 2] 和 arr2 = [1, 2, 4, 3]:

  1. mapIndices(arr2) 会生成 {1:0, 2:1, 4:2, 3:3}。
  2. count = 1。
  3. prevIndex = indexByValue.get(arr1[0]) = indexByValue.get(1) = 0。
  4. i = 1 (arr1[1] = 4):
    • nextIndex = indexByValue.get(4) = 2。
    • nextIndex (2) 不等于 prevIndex (0) + 1。
    • prevIndex = 2,count = 2。 (此时识别出 (1) 为一个块,(4 为新块的开始)
  5. i = 2 (arr1[2] = 3):
    • nextIndex = indexByValue.get(3) = 3。
    • nextIndex (3) 等于 prevIndex (2) + 1。
    • prevIndex = 3。 (此时 (4,3) 构成一个块)
  6. i = 3 (arr1[3] = 2):
    • nextIndex = indexByValue.get(2) = 1。
    • nextIndex (1) 不等于 prevIndex (3) + 1。
    • prevIndex = 1,count = 3。 (此时 (4,3) 为一个块,(2) 为新块的开始)

最终返回 count = 3。

Input arr1: [1, 4, 3, 2]
Desired arr2: [1, 2, 4, 3]
Minimum number of groups: 3

Input arr1: [1, 2, 3]
Desired arr2: [1, 2, 3]
Minimum number of groups: 1

Input arr1: [3, 2, 1]
Desired arr2: [1, 2, 3]
Minimum number of groups: 3

注意事项与总结

  • 唯一性是关键: 数组元素必须是唯一的,这样才能确保 mapIndices 正确地将每个元素映射到唯一的索引。如果存在重复元素,此方法将失效。
  • 时间复杂度:
    • 构建映射:O(N),其中 N 是数组长度。
    • 遍历输入数组:O(N)。
    • 总时间复杂度为 O(N),效率较高。
  • 空间复杂度:
    • 存储映射:O(N),用于存储 N 个元素的索引。
  • 算法本质: 该算法实际上是在寻找 arr1 中有多少个“断点”,这些断点使得 arr1 中的相邻元素在 arr2 中不再是顺序连续的。每个断点都意味着需要开始一个新的切割块。起始的第一个块也算一个,所以初始化 count 为 1。

通过上述方法,我们能够高效且准确地计算出将一个数组通过切割和重排转换为另一个目标数组所需的最小分组数,这在处理序列重排或优化操作时具有实际应用价值。

本篇关于《最少分组数实现数组拆分方法》的介绍就到此结束啦,但是学无止境,想要了解学习更多关于文章的相关知识,请关注golang学习网公众号!

企查查官网入口及企业查询方法企查查官网入口及企业查询方法
上一篇
企查查官网入口及企业查询方法
CAD导入背景图步骤详解
下一篇
CAD导入背景图步骤详解
查看更多
最新文章
查看更多
课程推荐
  • 前端进阶之JavaScript设计模式
    前端进阶之JavaScript设计模式
    设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
    543次学习
  • GO语言核心编程课程
    GO语言核心编程课程
    本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
    516次学习
  • 简单聊聊mysql8与网络通信
    简单聊聊mysql8与网络通信
    如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
    500次学习
  • JavaScript正则表达式基础与实战
    JavaScript正则表达式基础与实战
    在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
    487次学习
  • 从零制作响应式网站—Grid布局
    从零制作响应式网站—Grid布局
    本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
    485次学习
查看更多
AI推荐
  • ChatExcel酷表:告别Excel难题,北大团队AI助手助您轻松处理数据
    ChatExcel酷表
    ChatExcel酷表是由北京大学团队打造的Excel聊天机器人,用自然语言操控表格,简化数据处理,告别繁琐操作,提升工作效率!适用于学生、上班族及政府人员。
    3280次使用
  • Any绘本:开源免费AI绘本创作工具深度解析
    Any绘本
    探索Any绘本(anypicturebook.com/zh),一款开源免费的AI绘本创作工具,基于Google Gemini与Flux AI模型,让您轻松创作个性化绘本。适用于家庭、教育、创作等多种场景,零门槛,高自由度,技术透明,本地可控。
    3492次使用
  • 可赞AI:AI驱动办公可视化智能工具,一键高效生成文档图表脑图
    可赞AI
    可赞AI,AI驱动的办公可视化智能工具,助您轻松实现文本与可视化元素高效转化。无论是智能文档生成、多格式文本解析,还是一键生成专业图表、脑图、知识卡片,可赞AI都能让信息处理更清晰高效。覆盖数据汇报、会议纪要、内容营销等全场景,大幅提升办公效率,降低专业门槛,是您提升工作效率的得力助手。
    3521次使用
  • 星月写作:AI网文创作神器,助力爆款小说速成
    星月写作
    星月写作是国内首款聚焦中文网络小说创作的AI辅助工具,解决网文作者从构思到变现的全流程痛点。AI扫榜、专属模板、全链路适配,助力新人快速上手,资深作者效率倍增。
    4631次使用
  • MagicLight.ai:叙事驱动AI动画视频创作平台 | 高效生成专业级故事动画
    MagicLight
    MagicLight.ai是全球首款叙事驱动型AI动画视频创作平台,专注于解决从故事想法到完整动画的全流程痛点。它通过自研AI模型,保障角色、风格、场景高度一致性,让零动画经验者也能高效产出专业级叙事内容。广泛适用于独立创作者、动画工作室、教育机构及企业营销,助您轻松实现创意落地与商业化。
    3901次使用
微信登录更方便
  • 密码登录
  • 注册账号
登录即同意 用户协议隐私政策
返回登录
  • 重置密码