topcode【随机算法题】【2026.5.20打卡-java版本】

旋转图像

要点:【i】【j] = [n-1-j-】【i]

java 复制代码
class Solution {
    public void rotate(int[][] matrix) {
        //[i][j] = [n-1-j][i]

        int n = matrix.length;

        for(int i = 0; i < n/2; i++){
            for(int j = i; j < n-1-i; j++){

                int temp = matrix[i][j];
                matrix[i][j] = matrix[n-1-j][i];
                matrix[n-1-j][i] =matrix[n-1-i][n-1-j];
                matrix[n-1-i][n-1-j] = matrix[j][n-1-i];
                matrix[j][n-1-i] = temp;
            }


        }
        
        
    }
}

最小覆盖长度

要点: 双指针,两个map

java 复制代码
class Solution {
    public String minWindow(String s, String t) {
        if (s == null || t == null || s.length() == 0 || t.length() == 0) {
            return "";
        }
        
        // 记录需要的字符及其数量
        Map<Character, Integer> need = new HashMap<>();
        for (char c : t.toCharArray()) {
            need.put(c, need.getOrDefault(c, 0) + 1);
        }
        
        // 记录窗口中的字符及其数量
        Map<Character, Integer> window = new HashMap<>();
        
        int left = 0, right = 0;
        int valid = 0; // 窗口中满足need条件的字符种类数
        
        // 记录最小覆盖子串的起始索引和长度
        int start = 0;
        int minLen = Integer.MAX_VALUE;
        
        while (right < s.length()) {
            // 扩大窗口
            char c = s.charAt(right);
            right++;
            
            // 更新窗口数据
            if (need.containsKey(c)) {
                window.put(c, window.getOrDefault(c, 0) + 1);
                // 如果窗口中该字符的数量达到need中需要的数量,则valid+1
                if (window.get(c).equals(need.get(c))) {
                    valid++;
                }
            }
            
            // 当窗口已包含t的所有字符时,尝试缩小窗口
            while (valid == need.size()) {
                // 更新最小覆盖子串
                if (right - left < minLen) {
                    start = left;
                    minLen = right - left;
                }
                
                // 缩小窗口
                char d = s.charAt(left);
                left++;
                
                // 更新窗口数据
                if (need.containsKey(d)) {
                    // 如果窗口中该字符的数量等于need中需要的数量,则valid-1
                    if (window.get(d).equals(need.get(d))) {
                        valid--;
                    }
                    window.put(d, window.get(d) - 1);
                }
            }
        }
        
        return minLen == Integer.MAX_VALUE ? "" : s.substring(start, start + minLen);
    }
}

轮转数组

要点:全反转, 然后前面反转,然后后面反转

java 复制代码
class Solution {
    public void rotate(int[] nums, int k) {
        int n = nums.length;
        k = k %n;
        reverse(nums, 0, n-1);
        reverse(nums, 0, k-1);
        reverse(nums, k, n-1);
        
    }

    public void reverse(int[] nums, int i, int j){
        while(i < j){
            int temp = nums[i];
            nums[i] = nums[j];
            nums[j] = temp;
            i++;
            j--;
        }
    }
}

除了自身以外数组的乘积

要点:左右的数组维护

java 复制代码
class Solution {
    public int[] productExceptSelf(int[] nums) {
        int[] Lsums = new int[nums.length];
        int[] Rsums = new int[nums.length];

        Lsums[0] = nums[0];
        for(int i = 1; i<nums.length; i++){
            Lsums[i] =  Lsums[i-1]*nums[i];
        }

        Rsums[nums.length-1] = nums[nums.length -1];
        for(int j = nums.length - 2; j>=0; j--){
            Rsums[j] = Rsums[j+1]*nums[j];
        }

        int[] ans = new int[nums.length];

        for(int i = 0; i < nums.length; i++){
            if(i == 0){
                ans[i] = Rsums[1];
            }else if(i == nums.length - 1){
                ans[i] = Lsums[i-1];
            }else{
                ans[i] = Rsums[i+1] * Lsums[i-1];
            }
        }

        return ans;
        
    }
}

随机知识

设计模式(高频,必问 2-3 个)

核心题:你用过哪些设计模式?项目中怎么用的?

面试官为什么这么问:考察代码设计能力,看你写代码是不是只求"能跑就行"。实习生能说出 3 种以上并讲清应用场景,就是优秀。

需要掌握的模式及回答方向

模式 一句话核心 你的结合场景
单例模式 全局唯一实例 Spring 的 Bean 默认就是单例,我项目里用枚举做全局配置类
工厂模式 创建对象不暴露 new 根据大模型类型创建不同的调用器
策略模式 封装算法,可互换 不同模型接口对应不同策略实现,避免 if-else
代理模式 控制访问,增强功能 Spring AOP 就是动态代理,事务就是这么实现的
观察者模式 一对多通知 Spring 的事件机制,比如用户注册完发邮件通知
模板方法模式 定义骨架,子类实现细节 JdbcTemplateRedisTemplate 都是

面试官爱追问

  • 单例双重检查锁为什么加 volatile?→ 你已经知道(禁止指令重排)
  • Spring 用了哪些设计模式?→ 至少能说代理、模板方法、单例、工厂

准备建议:不用单独背,上面每个点都能和 Spring 框架、你的项目关联讲出来。

候选人

好的。设计模式本质上是解决特定问题的成熟方案,我在项目里用过六种,每种都有明确的应用场景。

第一种,单例模式------全局唯一实例。

单例的核心是保证一个类在整个应用中只有一个实例,节省资源并保证状态一致。Spring 容器中的 Bean 默认就是单例的,这是最典型的应用。我项目里用枚举单例管理全局配置,枚举天然防反射和反序列化破坏,是最安全的方式。实现上优先用枚举,非枚举场景用静态内部类持有实例保证延迟加载和线程安全。

重点补充 :如果面试官追问双重检查锁定(DCL),我需要说明 DCL 中 volatile 的作用。instance = new Singleton() 底层拆成分配内存、初始化对象、将引用指向内存三步,JVM 可能重排让引用先指向未初始化好的内存。另一个线程在第一次检查时发现引用不为 null,直接返回未初始化完成的对象,导致程序出错。volatile 通过内存屏障禁止这种重排序,保证对象完全初始化后才被外界看到。

第二种,工厂模式------解耦对象创建。

当一个对象的创建逻辑比较复杂,或者需要根据条件创建不同实现类时,我用工厂模式。它分简单工厂、工厂方法和抽象工厂三个层次。简单工厂用一个工厂类根据参数返回不同的产品实例;工厂方法是定义一个创建对象的接口,让子类决定实例化哪个类;抽象工厂用于创建一系列相关的产品族。

在面试 Agent 项目里,大模型调用层正是用得上的场景。不同模型(GPT-4、Claude、文心一言)接口不同、认证方式不同。定义一个统一的模型调用接口,然后为每个模型写一个具体实现类,再用工厂根据配置动态创建对应的调用器。新增模型只需添加实现类和工厂分支,业务代码零改动。

第三种,策略模式------消除 if-else。

策略模式定义一系列算法,把它们封装起来,让它们可以互相替换,与工厂模式的区别在于前者关注"创建",后者关注"行为互换"。当代码里出现大量 if-else 或 switch 判断行为逻辑时,策略模式就很有用。

项目里面试题评分模块用了策略模式。不同题目类型(算法题、系统设计题、行为面试题)的评分规则完全不同。定义统一的评分策略接口,每种题型实现对应的策略类,再用一个工厂或 Map 根据题目类型获取对应策略执行。新增题目类型只需增加一个策略实现类,无需修改原有评分逻辑,完全符合开闭原则。

第四种,代理模式------无侵入增强。

代理模式给一个对象提供一个代理对象,由代理控制对原对象的访问,核心作用是在不修改原始类的情况下,增加额外的功能。Spring AOP 就是最典型的应用。

项目里的事务管理、日志记录都靠 AOP 实现。比如 @Transactional 注解,Spring 为加了该注解的 Bean 生成代理对象,在方法调用前后自动开启和提交/回滚事务。业务代码完全无感知,干净纯粹。日志切面记录请求耗时和异常信息,同样不侵入业务方法。

第五种,模板方法模式------定义算法骨架。

模板方法定义一个算法的骨架,把一些步骤延迟到子类实现。Spring 里的 JdbcTemplate、RedisTemplate 就是模板方法模式的典型应用。JdbcTemplate.execute() 定义了"获取连接 → 执行 SQL → 处理结果集 → 关闭连接"的固定流程,SQL 语句和结果映射由传入的回调或匿名子类实现,整体流程由模板严格控制。

第六种,观察者模式------一对多通知。

观察者模式定义对象间的一对多依赖关系,当主题对象状态改变时,所有依赖它的观察者都会收到通知并自动更新。

Spring 的事件机制就是观察者模式的标准实现。在一个订单服务里,订单支付成功后发布 PaymentSuccessEvent,积分服务监听该事件自动增加积分,短信服务监听该事件发送支付成功通知。订单服务只负责发布事件,不关心谁在监听,完全解耦。新增消费者只需添加监听器,无需修改订单服务代码。还可以用 @Async 把事件处理变成异步,不阻塞主流程。

面试官追问:Spring 用了哪些设计模式?

Spring 框架大量使用设计模式,常用的有代理模式(AOP)、单例模式(默认 Bean 作用域)、工厂模式(BeanFactory)、模板方法模式(JdbcTemplate、RestTemplate)、观察者模式(Spring Event、ApplicationListener)、责任链模式(拦截器链、Filter Chain)。这些模式共同支撑起 Spring 的 IoC、AOP 等核心能力。


总结 :设计模式不是炫技的工具,而是解决实际问题的套路。我用单例管理全局资源、工厂解耦对象创建、策略消除冗长 if-else、代理无侵入增强功能、模板方法统一复杂流程、观察者解耦事件通知。核心原则是对扩展开放,对修改关闭,保证代码的可维护性和可扩展性。设计模式的价值在于选择,而不是堆砌------如果一个场景不需要用模式,强行套用反而让代码变得更复杂。

碎碎念:后续会更新每天学习的八股和算法 题,暑假实习找不到了,开始准备秋招的第10天。努力连续更新100天!把项目收尾?接个agent看看?然后要开始搞科研,要不然毕不了业了。时间不够用了哇。好,睡前再看一下八股吧,该背背!!!

相关推荐
我还记得那天7 小时前
C语言递归实现汉诺塔问题
c语言·开发语言
不吃土豆的马铃薯7 小时前
Spdlog 入门:日志记录器与日志槽基础详解
服务器·开发语言·c++·c·日志·spdlog
此生决int7 小时前
算法从入门到精通——前缀和
c++·算法·蓝桥杯
AI瓦力7 小时前
技术分享 | 彻底解决图片“躺平”问题:Java 后端强制校准图片方向
java
武子康7 小时前
Java-219 RocketMQ Spring Boot 集成指南:生产者与消费者实战
java·spring boot·分布式·kafka·消息队列·rocketmq·java-rocketmq
凯瑟琳.奥古斯特7 小时前
传输层核心功能解析
开发语言·网络·职场和发展
RainCityLucky7 小时前
Java Swing 自定义组件库分享(七)
java·笔记·后端
大大杰哥7 小时前
leetcode hot100(4)矩阵
算法·leetcode·矩阵
Fuyo_11197 小时前
C++中的活字印刷术——模板·初阶
开发语言·c++·笔记