一道题引出的知识点(List实现(ArrayList)、集合拷贝(构造函数/浅拷贝)、List插入(addAll索引操作)、集合判重(HashSet)、迭代处理(Queue/BFS)、并发修改、索引)

让我们先来看看题目😋
3690. 拆分合并数组
在这里插入图片描述
在这里插入图片描述

这道题目非常有意思,我们可以看道数据范围非常的小啊,所以我们可以直接用暴力就可以解决了,详细题解请看灵神的题解

因为本人才刚刚学习Java,对于很多知识点都不了解,所以写这篇博客来梳理一下这道题用到的基础知识。我们先来看看灵神的代码

class Solution {
    public int minSplitMerge(int[] nums1, int[] nums2) {
        int n = nums1.length;
        List<Integer> nums2List = toList(nums2);
        Set<List<Integer>> vis = new HashSet<>();
        vis.add(toList(nums1));
        List<List<Integer>> q = List.of(toList(nums1));
        for (int ans = 0; ; ans++) {
            List<List<Integer>> tmp = q;
            q = new ArrayList<>();
            for (List<Integer> a : tmp) {
                if (a.equals(nums2List)) {
                    return ans;
                }
                for (int l = 0; l < n; l++) {
                    for (int r = l + 1; r <= n; r++) {
                        List<Integer> sub = a.subList(l, r);
                        List<Integer> b = new ArrayList<>(a);
                        b.subList(l, r).clear(); // 从 b 中移除 sub
                        for (int i = 0; i <= b.size(); i++) {
                            List<Integer> c = new ArrayList<>(b);
                            c.addAll(i, sub);
                            if (vis.add(c)) { // c 不在 vis 中
                                q.add(c);
                            }
                        }
                    }
                }
            }
        }
    }

    private List<Integer> toList(int[] nums) {
        return Arrays.stream(nums).boxed().collect(Collectors.toList());
    }
}

作者:灵茶山艾府
链接:https://leetcode.cn/problems/split-and-merge-array-transformation/solutions/3787914/bfspythonjavacgo-by-endlesscheng-86ya/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

知识点1

先来看看下面这个方法

private List<Integer> toList(int[] nums) {
        return Arrays.stream(nums).boxed().collect(Collectors.toList());
    }

🔍 逐层解析

1. Arrays.stream(nums)

  • 作用:将int数组转换为IntStream
  • 输入int[] nums (原始类型数组)
  • 输出IntStream (专门处理int的流)

2. .boxed()

  • 核心转换:将IntStream中的int元素装箱为Integer
  • 为什么需要:Java泛型不支持原始类型,List只能存储对象
  • 转换前int转换后Integer

3. .collect(Collectors.toList())

  • 终端操作:将流中的元素收集到List中
  • Collectors.toList():返回一个收集器,将元素累积到新List

知识点2

📝 代码解析

Set<List<Integer>> vis = new HashSet<>();
vis.add(toList(nums1));
List<List<Integer>> q = List.of(toList(nums1));

🔍 逐行详解

1. Set<List<Integer>> vis = new HashSet<>();

知识点:泛型集合 + 钻石运算符

  • Set接口:不允许重复元素的集合
  • List作为元素:存储整数列表的集合
  • HashSet实现:基于哈希表的Set实现,O(1)时间复杂度查找
  • 钻石运算符:编译器自动推断泛型类型

2. vis.add(toList(nums1));

知识点:集合操作 + 方法调用链

// 分解步骤:
List<Integer> firstList = toList(nums1);  // 数组转List
boolean added = vis.add(firstList);        // 添加到Set,返回是否成功添加

3. List<List<Integer>> q = List.of(toList(nums1));

知识点:工厂方法 + 不可变集合

  • List.of():Java 9+ 的工厂方法,创建不可变列表
  • 嵌套泛型List<List<Integer>> - 存储List的List
  • 单元素列表:创建只包含一个元素的不可变列表

知识点3

📝 代码解析

List<Integer> sub = a.subList(l, r);
List<Integer> b = new ArrayList<>(a);
b.subList(l, r).clear(); // 从 b 中移除

🔍 逐行详解

1. List<Integer> sub = a.subList(l, r);

知识点:视图切片 + 区间操作

  • subList(l, r):返回原列表从索引l到r-1的视图
  • 不是拷贝suba共享底层数据,修改会相互影响
  • 前闭后开[l, r) 包含l,不包含r
  • 示例a = [1,2,3,4,5], sublist(1,4) = [2,3,4]

2. List<Integer> b = new ArrayList<>(a);

知识点:深拷贝 + 构造函数复制

  • 新建独立列表ba的完整拷贝,两者独立
  • 内存开销:O(n)空间复杂度
  • 修改隔离:对b的修改不会影响原列表a

3. b.subList(l, r).clear();

知识点:视图操作 + 批量删除

  • 链式调用subList().clear()直接在原列表上操作
  • 高效删除:一次性删除区间元素,比循环remove高效
  • 底层实现:调用removeRange(l, r)方法

知识点4

📝 代码解析

List<Integer> c = new ArrayList<>(b);
c.addAll(i, sub);
if (vis.add(c)) { // c 不在 vis 中
    q.add(c);
}

🔍 逐行详解

1. List<Integer> c = new ArrayList<>(b);

知识点:防御性拷贝 + 状态隔离

  • 创建副本:基于列表b创建全新的ArrayList
  • 状态独立:对c的修改不会影响原列表b
  • 内存代价:O(n)空间复杂度,但保证数据安全

2. c.addAll(i, sub);

知识点:列表插入 + 索引操作

  • 批量插入:将sub列表的所有元素插入到位置i
  • 索引位置i必须在[0, c.size()]范围内
  • 元素移动:插入点后的元素自动后移

3. if (vis.add(c)) { q.add(c); }

知识点:集合判重 + 条件添加

  • vis.add©:尝试将列表c添加到HashSet
    • 返回true:元素不存在,成功添加
    • 返回false:元素已存在,添加失败
  • 条件入队:只有新状态才加入处理队列

⚡ 完整流程示例

// 初始状态
List<Integer> b = Arrays.asList(1, 2, 3);
List<Integer> sub = Arrays.asList(7, 8);
Set<List<Integer>> vis = new HashSet<>();
Queue<List<Integer>> q = new LinkedList<>();

// 执行过程
List<Integer> c = new ArrayList<>(b);  // c = [1, 2, 3]
c.addAll(1, sub);                      // c = [1, 7, 8, 2, 3]
if (vis.add(c)) {                      // 如果[1,7,8,2,3]不在vis中
    q.add(c);                          // 加入待处理队列
}

🎯 核心知识点

HashSet判重机制

// List的equals和hashCode决定重复判断
List<Integer> list1 = Arrays.asList(1, 2, 3);
List<Integer> list2 = Arrays.asList(1, 2, 3);

vis.add(list1);  // 返回true,成功添加
vis.add(list2);  // 返回false,内容相同,视为重复

BFS/状态搜索模式

// 典型的状态空间搜索框架
while (!q.isEmpty()) {
    List<Integer> current = q.poll();
    
    for (每个可能的操作) {
        List<Integer> nextState = new ArrayList<>(current);  // 创建副本
        // 对nextState进行修改...
        
        if (visited.add(nextState)) {  // 新状态检测
            q.add(nextState);          // 加入待处理
        }
    }
}

性能考虑

// 优化思路:使用更高效的数据结构
Set<String> vis = new HashSet<>();  // 存储序列化字符串
// 将List转为String存储,减少内存占用
String state = c.toString();
if (vis.add(state)) {
    q.add(new ArrayList<>(c));
}

关键点:这套模式是图遍历/状态搜索算法的核心,确保不重复处理相同状态!

感谢您的观看,期待关注。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值