算法刷题记录——LeetCode篇(4) [第301~400题](持续更新)

news2025/3/18 12:21:03

(优先整理热门100及面试150,不定期持续更新,欢迎关注)


322. 零钱兑换

给你一个整数数组 coins ,表示不同面额的硬币;以及一个整数 amount ,表示总金额。
计算并返回可以凑成总金额所需的最少的硬币个数。如果没有任何一种硬币组合能组成总金额,返回 -1
你可以认为每种硬币的数量是无限的。

示例 1:

输入:coins = [1, 2, 5], amount = 11
输出:3 

解释:11 = 5 + 5 + 1

示例 2:

输入:coins = [2], amount = 3
输出:-1

示例 3:

输入:coins = [1], amount = 0
输出:0

提示:

  • 1 <= coins.length <= 12
  • 1 <= coins[i] <= 2^31 - 1
  • 0 <= amount <= 10^4

方法一:动态规划

使用动态规划数组 dp,其中 dp[i] 表示凑成金额 i 所需的最少硬币个数。通过逐步填充 dp 数组,最终得到 dp[amount] 的结果。

  1. 初始化

    • dp[0] = 0:金额为0时不需要任何硬币。
    • 其他金额初始化为 amount + 1,因为最多需要 amount 个硬币(全用面额1的硬币),初始值设为更大的数表示暂时不可达。
  2. 状态转移

    • 对于每个金额 i,遍历所有硬币面额 coin
    • coin ≤ i,则可以通过 i - coin 的金额加上当前硬币组成 i,即 dp[i] = min(dp[i], dp[i - coin] + 1)
  3. 结果判断

    • 如果最终 dp[amount] 仍大于 amount,说明无法凑出目标金额,返回 -1
    • 否则返回 dp[amount],即最少硬币数。

代码实现(Java)

import java.util.Arrays;

class Solution {
    public int coinChange(int[] coins, int amount) {
        if (amount == 0) {
            return 0;
        }
        int[] dp = new int[amount + 1];
        Arrays.fill(dp, amount + 1); // 初始化为一个较大的值(超过最大可能硬币数)
        dp[0] = 0; // 金额0需要0个硬币
      
        // 遍历所有金额,从1到amount
        for (int i = 1; i <= amount; i++) {
            // 遍历所有硬币面额
            for (int coin : coins) {
                if (coin <= i) { // 硬币面额不超过当前金额时,才可能使用
                    dp[i] = Math.min(dp[i], dp[i - coin] + 1);
                }
            }
        }
        // 判断结果是否有效
        return dp[amount] > amount ? -1 : dp[amount];
    }
}

方法二:BFS解法

将问题转化为图的最短路径问题,其中每个节点表示当前剩余金额,边表示使用一枚硬币。BFS按层遍历,首次到达金额0时的层数即为最少硬币数。

  1. 初始化

    • 队列存放待处理的金额,初始为 amount
    • 已访问集合 visited 防止重复处理相同金额。
    • steps 记录当前层数(即已用硬币数)。
  2. 层序遍历

    • 每层开始前记录队列大小,处理完该层所有节点后步数+1。
    • 对每个金额尝试所有硬币:
      • current - coin == 0,直接返回当前步数。
      • 若新金额合法且未访问过,加入队列并标记,避免重复处理相同金额。
  3. 终止条件

    • 队列清空时仍未找到解,返回 -1

代码实现(Java)

import java.util.*;

class Solution {
    public int coinChange(int[] coins, int amount) {
        if (amount == 0) return 0;
      
        Queue<Integer> queue = new LinkedList<>();
        Set<Integer> visited = new HashSet<>();
        queue.offer(amount);
        visited.add(amount);
        int steps = 0;
      
        while (!queue.isEmpty()) {
            int size = queue.size();
            steps++;
          
            for (int i = 0; i < size; i++) {
                int current = queue.poll();
              
                for (int coin : coins) {
                    int next = current - coin;
                  
                    if (next == 0) {
                        return steps; // 找到解,直接返回
                    }
                  
                    if (next > 0 && !visited.contains(next)) {
                        visited.add(next);
                        queue.offer(next);
                    }
                }
            }
        }
        return -1; // 队列清空仍未找到解
    }
}

复杂度分析

  • 动态规划时间复杂度:外层循环:O(amount),内层循环:O(coins.length),总复杂度:O(amount * coins.length)
  • BFS时间复杂度:最坏情况:O(amount * coins.length),实际效率取决于硬币面额分布,大额硬币可能加速收敛。

对比总结

方法优势劣势
BFS无需预计算所有状态,快速收敛空间复杂度高(队列膨胀)
动态规划适合多次查询,空间可控需遍历全部状态

347. 前 K 个高频元素

给你一个整数数组 nums 和一个整数 k ,请你返回其中出现频率前 k 高的元素。你可以按任意顺序返回答案。

示例 1:

输入: nums = [1,1,1,2,2,3], k = 2
输出: [1,2]

示例 2:

输入: nums = [1], k = 1
输出: [1]

提示:

  • 1 <= nums.length <= 105
  • k 的取值范围是 [1, 数组中不相同的元素的个数]
  • 题目数据保证答案唯一,换句话说,数组中前 k 个高频元素的集合是唯一的

进阶: 你所设计算法的时间复杂度必须优于 O(n log n) ,其中 n 是数组大小。

方法一:最小堆(优先队列)

使用最小堆维护当前频率最高的k个元素,遍历时保持堆的大小不超过k,时间复杂度为O(n log k)

  1. 统计频率:使用哈希表记录每个元素的出现次数。
  2. 维护最小堆:将元素按频率加入堆,堆大小超过k时移除堆顶(最小频率元素)。
  3. 提取结果:堆中剩余的元素即为前k高的频率元素。

代码实现(Java)

class Solution {
    public int[] topKFrequent(int[] nums, int k) {
        // 统计频率
        Map<Integer, Integer> freqMap = new HashMap<>();
        for (int num : nums) {
            freqMap.put(num, freqMap.getOrDefault(num, 0) + 1);
        }

        // 创建最小堆,按频率升序排序
        PriorityQueue<Map.Entry<Integer, Integer>> heap = new PriorityQueue<>(
            (a, b) -> a.getValue() - b.getValue()
        );

        // 维护堆的大小为k
        for (Map.Entry<Integer, Integer> entry : freqMap.entrySet()) {
            heap.offer(entry);
            if (heap.size() > k) {
                heap.poll();
            }
        }

        // 提取结果
        int[] res = new int[k];
        int idx = 0;
        while (!heap.isEmpty()) {
            res[idx++] = heap.poll().getKey();
        }

        return res;
    }
}

方法二:桶排序

基于频率的桶排序,时间复杂度为O(n),适合处理大数据量。

  1. 统计频率:记录每个元素出现的次数及最大频率。
  2. 构建频率桶:将元素按频率存入对应桶中。
  3. 逆序收集结果:从高频率到低频率遍历桶,收集前k个元素。

代码实现(Java)

class Solution {
    public int[] topKFrequent(int[] nums, int k) {
        // 统计频率
        Map<Integer, Integer> freqMap = new HashMap<>();
        for (int num : nums) {
            freqMap.put(num, freqMap.getOrDefault(num, 0) + 1);
        }

        // 创建桶数组
        List<Integer>[] bucket = new List[nums.length + 1];
        for (Map.Entry<Integer, Integer> entry : freqMap.entrySet()) {
            int freq = entry.getValue();
            if (bucket[freq] == null) {
                bucket[freq] = new ArrayList<>();
            }
            bucket[freq].add(entry.getKey());
        }

        // 收集结果
        int[] res = new int[k];
        int idx = 0;
        for (int i = bucket.length - 1; i >= 0 && idx < k; i--) {
            if (bucket[i] != null) {
                for (int num : bucket[i]) {
                    res[idx++] = num;
                    if (idx == k) break;
                }
            }
        }

        return res;
    }
}

复杂度分析

1、最小堆

  • 时间复杂度:O(n log k),其中n为数组长度,k为结果数量。
    空间复杂度:O(n),哈希表和堆的空间。
  • 优点:空间占用相对较低,适合k较小的情况。
    缺点:当k接近n时,时间复杂度接近O(n log n)

2、桶排序

  • 时间复杂度:O(n),所有操作均为线性时间。
    空间复杂度:O(n),桶数组和哈希表的空间。
  • 优点:时间复杂度严格为O(n),适合大数据量。
    缺点:需要额外空间存储桶,最大频率较高时空间占用大。

394. 字符串解码

给定一个经过编码的字符串,返回它解码后的字符串。
编码规则为: k[encoded_string],表示其中方括号内部的 encoded_string 正好重复 k 次。注意 k 保证为正整数。
你可以认为输入字符串总是有效的;输入字符串中没有额外的空格,且输入的方括号总是符合格式要求的。
此外,你可以认为原始数据不包含数字,所有的数字只表示重复的次数 k ,例如不会出现像 3a2[4] 的输入。

示例 1:

输入:s = "3[a]2[bc]"
输出:"aaabcbc"

示例 2:

输入:s = "3[a2[c]]"
输出:"accaccacc"

示例 3:

输入:s = "2[abc]3[cd]ef"
输出:"abcabccdcdcdef"

示例 4:

输入:s = "abc3[cd]xyz"
输出:"abccdcdcdxyz"

提示:

  • 1 <= s.length <= 30
  • s 由小写英文字母、数字和方括号 '[]' 组成
  • s 保证是一个有效的输入
  • s 中所有整数的取值范围为 [1, 300]

方法:栈辅助法

利用栈来处理嵌套的括号结构,保存每层括号外的字符串和重复次数。遍历字符串时解析数字,遇到左括号时将当前状态压栈,遇到右括号时弹出栈顶元素进行字符串拼接。

  1. 初始化栈和变量:使用两个栈分别保存当前层的字符串和重复次数,当前字符串和数字变量记录正在处理的部分。
  2. 遍历字符
    • 数字:累加计算多位数。
    • 左括号:压栈当前字符串和数字,重置变量。
    • 右括号:弹出栈顶的字符串和数字,将当前字符串重复后拼接。
    • 字母:直接追加到当前字符串。
  3. 返回结果:最终拼接完成的字符串即为答案。

代码实现(Java)

class Solution {
    public String decodeString(String s) {
        Stack<Integer> numStack = new Stack<>();
        Stack<StringBuilder> strStack = new Stack<>();
        StringBuilder currentStr = new StringBuilder();
        int num = 0;

        for (char c : s.toCharArray()) {
            if (Character.isDigit(c)) {
                num = num * 10 + (c - '0');
            } else if (c == '[') {
                strStack.push(currentStr);
                numStack.push(num);
                currentStr = new StringBuilder();
                num = 0;
            } else if (c == ']') {
                int k = numStack.pop();
                StringBuilder prevStr = strStack.pop();
                String temp = currentStr.toString();
                prevStr.append(temp.repeat(k));
                currentStr = prevStr;
            } else {
                currentStr.append(c);
            }
        }

        return currentStr.toString();
    }
}

复杂度分析

  • 时间复杂度O(n × m),其中n是字符串长度,m是最大重复次数。每个字符处理一次,字符串拼接的时间取决于重复次数。
  • 空间复杂度O(n),栈的深度最坏情况下与字符串长度成线性关系。

博客源文件Gitee仓库:

gitee.com/richardmilos/allen-csdn-notes

(持续更新,未完待续)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2317215.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

AD绘图基本操作

一、基本操作 注意&#xff1a;快捷键都要在英文模式下才能生效 1、移动 按住鼠标右键移动 2、切换桌面栅格距离 G 3、英寸和毫米 尺寸切换 Q 4、元件在3D模式下的移动 3D视角鼠标左键只起到选择元器件并移动之的功能&#xff0c; 单纯鼠标右键只能平移桌面 shift鼠…

dfs(十二)21. 合并两个有序链表 递归解决

21. 合并两个有序链表 将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。 示例 1&#xff1a; 输入&#xff1a;l1 [1,2,4], l2 [1,3,4] 输出&#xff1a;[1,1,2,3,4,4]示例 2&#xff1a; 输入&#xff1a;l1 [], l2 [] …

51单片机指令系统入门

目录 基本概念讲解 一、机器指令​ 二、汇编指令​ &#xff08;一&#xff09;汇编指令的一般格式 &#xff08;二&#xff09;按字节数分类的指令 三、高级指令 总结​ 基本概念讲解 指令是计算机&#xff08;或单片机&#xff09;中 CPU 能够识别并执行的基本操作命令…

安全无事故连续天数计算,python 时间工具的高效利用

安全天数计算&#xff0c;数据系统时间直取&#xff0c;安全标准高效便捷好用。 笔记模板由python脚本于2025-03-17 23:50:52创建&#xff0c;本篇笔记适合对python时间工具有研究欲的coder翻阅。 【学习的细节是欢悦的历程】 博客的核心价值&#xff1a;在于输出思考与经验&am…

如何玩DeepSeek!15分钟快速创建GIS动态数据可视化仪表盘

DeepSeek最近火遍全球&#xff0c;大家用的都用的不亦乐乎。国外呢&#xff1f;当然也是&#xff0c;最近一上YouTube、X等都是deepseek的推送。 今天介绍一下&#xff0c;我在YouTube上看到的GIS行业与DeepSeek结合的一个案例&#xff1a; 快速轻松构建交互式地图仪表盘&…

课上测试:MIRACL共享库使用测试

MIRACL(MultiprecisionIntegerandRationalArithmeticC/cLibrary)是著名的密码算法库&#xff0c;设法去官网下载安装MIRACL&#xff0c;提交安装过程截图或过程文本&#xff08;3分&#xff09;. 去github官网下载.zip文件 使用如下命令进行解压 unzip -j -aa -L MIRACL-mast…

网络编程知识预备阶段

1. OSI七层模型 OSI&#xff08;Open System Interconnect&#xff09;七层模型是一种将计算机网络通信协议划分为七个不同层次的标准化框架。每一层都负责不同的功能&#xff0c;从物理连接到应用程序的处理。这种模型有助于不同的系统之间进行通信时&#xff0c;更好地理解和…

STM32微控制器_03_GPIO原理与应用

核心内容 STM32 GPIO基本原理&#xff08;熟悉&#xff09;GPIO输出功能HAL库编程实现的应用&#xff08;重点&#xff09;GPIO输入功能HAL库编程实现的应用&#xff08;重点&#xff09; 一.STM32 GPIO基本原理 1.GPIO简介 STM32的GPIO相当于STM32的四肢&#xff0c;一个S…

零拷贝分析

kafka 零拷贝 请求 - 网口 - socket - 用户态 - 内核缓存区 - 内核态&#xff08;磁盘信息&#xff09; 磁盘 - 内核缓存区 - 用户缓存区 - 网络缓存区 零拷贝&#xff08;Zero-Copy&#xff09; 是一种高效的数据传输技术&#xff0c;旨在减少数据在内存中的拷贝次数&#x…

从Instagram到画廊:社交平台如何改变艺术家的展示方式

从Instagram到画廊&#xff1a;社交平台如何改变艺术家的展示方式 在数字时代&#xff0c;艺术家的展示方式正在经历一场革命。社交平台&#xff0c;尤其是Instagram&#xff0c;已经成为艺术家展示作品、与观众互动和建立品牌的重要渠道。本文将探讨社交平台如何改变艺术家的…

✎ 一次有趣的经历

&#x1f4c6;2025年3月17日 | 周一 | ☀️晴 &#x1f4cd;今天路过学院楼7&#xff0c;见到了满园盛开的花&#x1f33a;&#xff0c;心情瞬间明朗&#xff01; &#x1f4cc;希望接下来的日子也能像这些花一样&#xff0c;充满活力&#x1f525;&#xff01; &#x1…

快!快!快!NDPP时延测试数据公布!

在全方位认识NDPP第3期《NDPP在金融场景的应用》中&#xff0c;我们重点介绍了NDPP的典型应用场景行情解码硬件加速和策略计算加速&#xff0c;并帮助某百亿私募用户基于NDPP实现期货业务加速的案例。 近期&#xff0c;中科驭数凭借低时延产品荣获信创“大比武”行业融合赛道三…

激光雷达“开卷”2.0,头部Tier1入局

高阶智驾的普及&#xff0c;正在催生激光雷达市场的巨大潜在增长空间。 本周&#xff0c;汽车激光雷达主力供应商之一的禾赛科技发布财报&#xff0c;去年第四季度激光雷达总交付量为222,054台&#xff0c;同比增长153.1%&#xff0c;超过2023年全年。2024全年激光雷达总交付量…

力扣No.376.摆动序列

题目&#xff1a; 链接&#xff1a; https://leetcode.cn/problems/wiggle-subsequence/description/ 代码&#xff1a; class Solution {public int wiggleMaxLength(int[] nums) {int nnums.length;//状态表示:int[] fnew int[n];int[] gnew int[n];//初始化:for(int i0;i…

C语言中qsort函数的详解,以及模拟

引言 C语言中qsort函数的详解和模拟实现qsort函数&#xff0c;这里为了使用冒泡排序来模拟qsort函数 一、详解qsort函数 在 C 语言中&#xff0c;qsort 函数是一个标准库函数&#xff0c;用于对数组进行快速排序&#xff08;Quick Sort&#xff09;。它位于 <stdlib.h>…

9、讲一讲你理解的虚拟内存【中高频】

计算机早期&#xff0c;CPU 是直接操作 物理内存&#xff08;Physical Memory&#xff09;的&#xff0c;但这会导致 内存空间无法完全隔离&#xff0c;一个程序修改了另一个程序的地址空间&#xff0c;就会导致程序崩溃&#xff1b;同时物理内存大小有限&#xff0c;一旦超出这…

算法刷题整理合集(四)

本篇博客旨在记录自已的算法刷题练习成长&#xff0c;里面注有详细的代码注释以及和个人的思路想法&#xff0c;希望可以给同道之人些许帮助。本人也是算法小白&#xff0c;水平有限&#xff0c;如果文章中有什么错误或遗漏之处&#xff0c;望各位可以在评论区指正出来&#xf…

高数1.5 极限的运算法则

1. 预备知识 2.四则求极限法则 3.复合运算求极限法则

【鸿蒙开发】Hi3861学习笔记- 定时器中断

00. 目录 文章目录 00. 目录01. 概述02. 定时器相关API2.1 hi_timer_create2.2 hi_timer_start2.3 hi_timer_stop2.4 hi_timer_delete 03. 硬件设计04. 软件设计05. 实验现象06. 附录 01. 概述 定时器&#xff0c;顾名思义就是用来计时的&#xff0c;我们常常会设定计时或闹钟…

Power Apps 技术分享:画布应用使用表单控件

前言 表单控件&#xff0c;是画布应用里一个非常好用的控件&#xff0c;我们今天简单介绍下&#xff0c;如何使用这个控件。 正文 1.首先&#xff0c;我们需要有一个数据源&#xff0c;我们这里用上一篇博客新建的数据源&#xff0c;如下图&#xff1a; 2.新建一个页面&#xf…