0

0

优化数组重排:最小分组策略

碧海醫心

碧海醫心

发布时间:2025-11-22 17:10:12

|

485人浏览过

|

来源于php中文网

原创

优化数组重排:最小分组策略

本文详细阐述了如何计算将一个具有唯一值的数组通过重新排列最少数量的连续子数组(分组)转换为另一个目标数组所需的最小分组数。通过构建目标数组的索引映射,并迭代检查源数组中元素的相对顺序,我们可以高效地识别出连续的、无需内部调整的片段,从而确定所需的最少分组数量。

理解数组分组与重排问题

在处理数组转换问题时,我们有时会遇到这样的场景:给定一个源数组 arr1 和一个目标数组 arr2,两者包含相同且唯一的元素,长度也一致。我们的目标是将 arr1 切割成最少数量的连续子数组(即“分组”),然后通过重新排列这些子数组,使其最终形态与 arr2 完全一致。问题的核心在于找到这个最小的分组数。

例如,考虑源数组 arr1 = [1, 4, 3, 2] 和目标数组 arr2 = [1, 2, 4, 3]。我们可以将 arr1 分割成 (1), (4, 3), (2) 这三个子数组。然后,通过重新排列这些子数组为 (1), (2), (4, 3),我们就能得到 arr2。因此,对于这个例子,最小分组数是 3。

值得注意的是,一个简单的逐元素比较并计数不匹配项的方法是无效的。例如,对于 [1,4,3,2] 和 [1,2,4,3],如果仅计算不匹配项,会发现 4!=2, 3!=4, 2!=3,得到 3 个不匹配,但这种方法无法反映出通过重排分组实现转换的本质,因为它没有考虑元素的相对位置和连续性。

核心算法思路

解决此类问题的关键在于利用数组中元素的唯一性。由于所有元素都是唯一的,每个元素在目标数组 arr2 中都有一个固定的、唯一的索引位置。我们可以通过构建一个映射(Map)来快速查询 arr2 中每个元素的值对应的索引。

算法的核心思想是遍历源数组 arr1,并追踪 arr1 中当前元素及其前一个元素在 arr2 中的相对位置。如果 arr1 中的当前元素在 arr2 中的索引恰好是其前一个元素在 arr2 中索引的“下一个”,那么这两个元素在 arr1 中形成的连续序列,在 arr2 中也是连续的,它们可以归为一个分组。反之,如果它们在 arr2 中的索引不连续,则意味着当前元素开启了一个新的分组。

详细步骤

  1. 构建索引映射: 创建一个 Map,将目标数组 arr2 中的每个元素值作为键,其在 arr2 中的索引作为值。这个映射将允许我们以 O(1) 的时间复杂度查询任何元素在 arr2 中的目标位置。

  2. 初始化分组计数器: 初始化一个计数器 count 为 1。这是因为即使 arr1 和 arr2 完全相同,也至少需要一个分组(即整个数组本身)。

  3. 追踪前一个元素的索引: 获取 arr1 的第一个元素在 arr2 中的索引,并将其存储在 prevIndex 变量中。

  4. 遍历源数组 arr1: 从 arr1 的第二个元素开始,遍历直到数组结束。对于 arr1 中的每一个当前元素:

    • 使用之前构建的索引映射,查找当前元素在 arr2 中的索引,并将其存储在 nextIndex 变量中。
    • 判断是否属于同一分组: 比较 nextIndex 和 prevIndex + 1。
      • 如果 nextIndex == prevIndex + 1,这意味着当前元素在 arr2 中紧接着前一个元素。它们属于同一个逻辑分组。此时,更新 prevIndex = nextIndex,继续追踪。
      • 如果 nextIndex != prevIndex + 1,这意味着当前元素在 arr2 中的位置与前一个元素不连续。这表示我们遇到了一个新的分组的开始。此时,增加 count,并更新 prevIndex = nextIndex,以开始追踪这个新分组。
  5. 返回结果: 遍历结束后,count 的值即为所需的最小分组数。

示例演练

我们以 arr1 = [1, 4, 3, 2] 和 arr2 = [1, 2, 4, 3] 为例,逐步演示算法:

  1. 构建 arr2 的索引映射:indexByValue = {1:0, 2:1, 4:2, 3:3}

    瑞志企业建站系统(ASP版)2.2
    瑞志企业建站系统(ASP版)2.2

    支持模板化设计,基于标签调用数据 支持N国语言,并能根据客户端自动识别当前语言 支持扩展现有的分类类型,并可修改当前主要分类的字段 支持静态化和伪静态 会员管理功能,询价、订单、收藏、短消息功能 基于组的管理员权限设置 支持在线新建、修改、删除模板 支持在线管理上传文件 使用最新的CKEditor作为后台可视化编辑器 支持无限级分类及分类的移动、合并、排序 专题管理、自定义模块管理 支持缩略图和图

    下载
  2. 初始化:count = 1prevIndex = indexByValue.get(arr1[0]) = indexByValue.get(1) = 0

  3. 遍历 arr1:

    • i = 1 (元素 arr1[1] = 4):nextIndex = indexByValue.get(4) = 2 比较 nextIndex (2) 和 prevIndex + 1 (0 + 1 = 1)。 2 != 1。不连续,说明 4 开启了一个新分组。 count 增加到 2。 prevIndex 更新为 nextIndex = 2。 当前分组:(1) 结束,新分组 (4) 开始

    • i = 2 (元素 arr1[2] = 3):nextIndex = indexByValue.get(3) = 3 比较 nextIndex (3) 和 prevIndex + 1 (2 + 1 = 3)。 3 == 3。连续,说明 3 属于当前分组。 prevIndex 更新为 nextIndex = 3。 当前分组:(4,3) 继续

    • i = 3 (元素 arr1[3] = 2):nextIndex = indexByValue.get(2) = 1 比较 nextIndex (1) 和 prevIndex + 1 (3 + 1 = 4)。 1 != 4。不连续,说明 2 开启了一个新分组。 count 增加到 3。 prevIndex 更新为 nextIndex = 1。 当前分组:(4,3) 结束,新分组 (2) 开始

  4. 遍历结束: 返回 count = 3。这与我们预期的结果一致。

代码实现

以下是使用 Java 语言实现上述算法的示例代码:

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

public class ArrayGroupingConverter {

    /**
     * 计算将 arr1 转换为 arr2 所需的最少分组数。
     * 两个数组包含唯一的整数值,且长度相同。
     *
     * @param arr1 源数组
     * @param arr2 目标数组
     * @return 最少分组数
     */
    public static int process(int[] arr1, int[] arr2) {
        // 1. 构建 arr2 中元素值到其索引的映射
        Map indexByValue = mapIndices(arr2);

        // 2. 初始化分组计数器为 1 (至少有一个分组)
        int count = 1;

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

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

            // 5. 判断是否属于同一分组
            if (nextIndex == prevIndex + 1) {
                // 如果连续,更新 prevIndex,继续当前分组
                prevIndex++;
            } else {
                // 如果不连续,增加分组计数,并更新 prevIndex 开始新分组
                prevIndex = nextIndex;
                count++;
            }
        }

        // 6. 返回最终分组数
        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("源数组: " + java.util.Arrays.toString(arr1));
        System.out.println("目标数组: " + java.util.Arrays.toString(arr2));
        System.out.println("所需的最少分组数: " + process(arr1, arr2)); // 预期输出: 3

        int[] arr3 = {1, 2, 3, 4};
        int[] arr4 = {1, 2, 3, 4};
        System.out.println("\n源数组: " + java.util.Arrays.toString(arr3));
        System.out.println("目标数组: " + java.util.Arrays.toString(arr4));
        System.out.println("所需的最少分组数: " + process(arr3, arr4)); // 预期输出: 1

        int[] arr5 = {4, 3, 2, 1};
        int[] arr6 = {1, 2, 3, 4};
        System.out.println("\n源数组: " + java.util.Arrays.toString(arr5));
        System.out.println("目标数组: " + java.util.Arrays.toString(arr6));
        System.out.println("所需的最少分组数: " + process(arr5, arr6)); // 预期输出: 4
    }
}

输出示例:

源数组: [1, 4, 3, 2]
目标数组: [1, 2, 4, 3]
所需的最少分组数: 3

源数组: [1, 2, 3, 4]
目标数组: [1, 2, 3, 4]
所需的最少分组数: 1

源数组: [4, 3, 2, 1]
目标数组: [1, 2, 3, 4]
所需的最少分组数: 4

注意事项与性能分析

  • 数据约束: 该算法依赖于数组中元素值的唯一性。如果存在重复值,则元素到索引的映射将不再唯一,算法需要进行修改以处理这种情况。
  • 时间复杂度:
    • 构建索引映射:O(N),其中 N 是数组的长度。
    • 遍历源数组:O(N),每次查找映射为 O(1)。
    • 因此,总时间复杂度为 O(N)。
  • 空间复杂度:
    • 存储索引映射:O(N),因为映射需要存储 N 个键值对
    • 因此,总空间复杂度为 O(N)。

这种方法在处理给定约束(数组大小最大为 1000)下表现高效,是解决此类问题的理想方案。

总结

通过将目标数组的元素索引化,并迭代检查源数组中元素在目标数组中的相对顺序,我们能够精确地识别出连续的、无需内部调整的子序列。这种策略使得我们能够计算出将源数组转换为目标数组所需的最少分组数量。此方法不仅逻辑清晰,而且在时间和空间效率上都表现出色,适用于处理具有唯一元素的大规模数组转换问题。

热门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

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

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

412

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.6万人学习

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

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