0

0

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

聖光之護

聖光之護

发布时间:2025-11-22 17:46:02

|

369人浏览过

|

来源于php中文网

原创

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

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

问题定义

给定两个长度相同、元素均为唯一整数的数组,例如 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 中有多少次这种“连续性”被打破。

算法步骤:

迷你天猫商城
迷你天猫商城

迷你天猫商城是一个基于Spring Boot的综合性B2C电商平台,需求设计主要参考天猫商城的购物流程:用户从注册开始,到完成登录,浏览商品,加入购物车,进行下单,确认收货,评价等一系列操作。 作为迷你天猫商城的核心组成部分之一,天猫数据管理后台包含商品管理,订单管理,类别管理,用户管理和交易额统计等模块,实现了对整个商城的一站式管理和维护。所有页面均兼容IE10及以上现代浏览器。部署方式1、项目

下载
  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 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 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。

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

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
counta和count的区别
counta和count的区别

Count函数用于计算指定范围内数字的个数,而CountA函数用于计算指定范围内非空单元格的个数。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

198

2023.11.20

golang map内存释放
golang map内存释放

本专题整合了golang map内存相关教程,阅读专题下面的文章了解更多相关内容。

75

2025.09.05

golang map相关教程
golang map相关教程

本专题整合了golang map相关教程,阅读专题下面的文章了解更多详细内容。

36

2025.11.16

golang map原理
golang map原理

本专题整合了golang map相关内容,阅读专题下面的文章了解更多详细内容。

61

2025.11.17

java判断map相关教程
java判断map相关教程

本专题整合了java判断map相关教程,阅读专题下面的文章了解更多详细内容。

42

2025.11.27

页面置换算法
页面置换算法

页面置换算法是操作系统中用来决定在内存中哪些页面应该被换出以便为新的页面提供空间的算法。本专题为大家提供页面置换算法的相关文章,大家可以免费体验。

414

2023.08.14

C++ 设计模式与软件架构
C++ 设计模式与软件架构

本专题深入讲解 C++ 中的常见设计模式与架构优化,包括单例模式、工厂模式、观察者模式、策略模式、命令模式等,结合实际案例展示如何在 C++ 项目中应用这些模式提升代码可维护性与扩展性。通过案例分析,帮助开发者掌握 如何运用设计模式构建高质量的软件架构,提升系统的灵活性与可扩展性。

8

2026.01.30

c++ 字符串格式化
c++ 字符串格式化

本专题整合了c++字符串格式化用法、输出技巧、实践等等内容,阅读专题下面的文章了解更多详细内容。

9

2026.01.30

java 字符串格式化
java 字符串格式化

本专题整合了java如何进行字符串格式化相关教程、使用解析、方法详解等等内容。阅读专题下面的文章了解更多详细教程。

8

2026.01.30

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Kotlin 教程
Kotlin 教程

共23课时 | 3万人学习

C# 教程
C# 教程

共94课时 | 8万人学习

Java 教程
Java 教程

共578课时 | 53.7万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号