
算法的魔法世界
文章平均质量分 96
算法题精讲
手握风云-
[就读于河北科技大学]
[23级 软件工程专业]
[目标:拿到一个大厂offer]
展开
专栏收录文章
- 默认排序
- 最新发布
- 最早发布
- 最多阅读
- 最少阅读
-
动态规划算法的欢乐密码(四):简单多状态DP问题(下)
本文讲解了四种股票买卖问题的动态规划解法。1)含冷冻期问题:定义三种状态(买入、可交易、冷冻期),通过状态转移方程计算最大利润;2)含手续费问题:将状态分为"买入"和"卖出",在卖出时扣除手续费;3)最多交易两次问题:增加交易次数维度,处理不同交易次数下的状态转移;4)最多交易k次问题:在第三种问题基础上,将交易次数扩展为k次。所有解法都采用动态规划,通过状态转移方程和适当的初始化来计算最大利润,时间复杂度均为O(n)。原创 2025-08-09 21:42:40 · 748 阅读 · 32 评论 -
动态规划算法的欢乐密码(三):简单多状态DP问题(上)
《算法的魔法世界》专栏通过四个经典动态规划问题展示了解题思路:1.按摩师问题使用状态转移方程f[i]=g[i-1]+nums[i]和g[i]=max(f[i-1],g[i-1]);2.环形房屋抢劫问题转化为两次线性打家劫舍;3.删除点数问题通过预处理数组转化为类似打家劫舍问题;4.粉刷房子问题通过三维状态表示颜色选择。每个问题都详细分析了状态表示、转移方程和边界条件,提供了完整的Java实现代码,展示了动态规划在优化问题中的灵活应用。原创 2025-07-19 18:30:01 · 1062 阅读 · 41 评论 -
优选算法的链脉之韵:链表专题
本文介绍了链表算法中的常用技巧和典型例题解析。重点讲解了虚拟头节点、快慢指针等技巧的应用,并通过5道经典题目(两数相加、两两交换节点、重排链表、合并K个有序链表、K个一组翻转链表)详细展示了链表操作的实现方法。每道题目都提供了完整的Java代码实现,包括如何处理边界条件、反转链表的具体步骤以及分治思想的应用。这些例题涵盖了链表操作的核心知识点,对提高链表算法解题能力具有重要指导意义。原创 2025-07-07 01:39:39 · 1704 阅读 · 40 评论 -
贪心算法的 “反直觉” 时刻(一):重新认识局部最优
《贪心算法:魔法世界中的最优解探索》 贪心算法是一种通过局部最优选择寻求全局最优解的算法策略,其核心在于"活在当下"的决策思路。本文通过四个经典例题展示了贪心算法的实际应用: 柠檬水找零问题中,优先使用10美元找零的策略可确保交易顺利进行; 数组减半问题证明,反复减半当前最大元素是最优操作方案; 最大数构造问题通过自定义排序规则实现最优排列; 摆动序列问题通过识别波峰波谷来获取最长子序列。 每个案例都包含详细的代码实现和数学证明,揭示了贪心算法虽简单但需要严谨验证的特点。这些例题展示了贪原创 2025-06-28 22:48:50 · 1022 阅读 · 26 评论 -
动态规划算法的欢乐密码(二):路径问题
这里与上一题不同的是,上一题的参数是两个整数,这道题是一个二维数组,需要注意下标的映射,原始数组的下标与填表时的下标是[i-1][j-1]。接下来根据最近的一步划分问题,如下图所示,可以从A、B、C到达最后一行,那么此时的最小路径和分别为dp[i-1][j-1](表示为x)、dp[i-1][j](表示为y)、dp[i-1][j+1](表示为z),到达最后一行的状态表示dp[i][j]为前一个位置的最小路径和再加上它本身,所以状态转移方程为dp[i][j]=min(x,y,z)+matrix[i][j]。原创 2025-06-13 22:38:55 · 1232 阅读 · 24 评论 -
二叉树深搜:在算法森林中寻找路径
根据前面学过的知识,对一棵二叉搜索树进行中序遍历,得到的结果是一个有序序列,所以我们可以先定义一个全局变量prev,当遍历到某一个节点时,用prev存储前一个节点值,如下图所示,根据中序遍历,当我们遍历到1时,将prev赋值为1,然后进行比较,后面的数如果越来越大,那就是二叉搜索树。当我们遍历到某一节点时,如果发现左右子树均为空,并且节点值为0,那我们就可以把这个节点剪掉。从根节点出发,先去遍历左子树,当遇到叶子节点时,返回该节点的布尔值,然后回溯到它的父亲节点,然后遍历右子树,再回溯父亲节点并遍历自身。原创 2025-05-15 19:56:36 · 1450 阅读 · 58 评论 -
动态规划算法的欢乐密码(一):斐波那契数模型
当我们支付cost[i]向后走两步到达i+2位置时,再从i+2位置到达终点,花费为dp[i+2]+cost[n],所以递推公式为dp[i]=Math.min(dp[i+1],dp[i+2])+cost[i]。而题目也已经将前三个状态给了出来,dp[0]=0、dp[1]=1、dp[2]=1。接着利用之前的状态来推导dp[n]的值,根据最近的一步或者两步来划分问题:先到达dp[n]的前一个位置dp[n-1],然后支付cost[n-1]走一步,到达dp[n]花费为dp[n-1]+cost[n-1];原创 2025-04-19 20:48:26 · 1231 阅读 · 74 评论 -
巧用递归算法:破解编程难题的“秘密武器”
递归与循环一样,都是去执行相同的子问题,那么二者之间是可以相互转化的,那么我们什么时候用循环迭代效率更好,什么时候用递归效率更高。如下图所示,递归的细节展开图与二叉树的深搜极其相似。我们要想将递归转化为循环的话,就得借助栈来实现,因为当我们遍历一个子问题,我们需要保存上一个的信息方便调用方法执行右侧。但一个递归的细节展开图越复杂时,用递归就比较好解决;如果展开图只有一个分支,用循环迭代就比较方便。原创 2025-04-12 15:53:06 · 1526 阅读 · 71 评论 -
优选算法的妙思之流:分治——归并专题
如果p1所指的元素比p2所指的元素大,那么p1后面的元素就都是比p2大,就可以快速的统计出数目,然后再让p2++。找完右区域的逆序对,然后对右区域进行排序。方法2也同理,如果nums[p1]>= nums[p2],就让p1向右移动,直到nums[p1]<nums[p2],再让p2向右移动,p1同样不用回退。当我们在左右区域里面分别寻找逆序对时,如果数组长度较大,那么我们还可以再接着划分,继续按照上面的思路来找出逆序对的总数,这个部分就可以在递归中完成,所以我们在处理一左一右时也可以排个序。原创 2025-04-06 21:29:07 · 1849 阅读 · 63 评论 -
优选算法的妙思之流:分治——快排专题
接下来进行分类讨论:如果nums[i]=0,我们让nums[left+1]与nums[i]进行交换,然后i++,left++,就能保证[left+1,i-1]区间还都是1,还可能有一种极端情况,就是i=left+1,自身与自身进行交换,还是得需要left和i++,综上我们就可以写成nums[++left]与nums[i++]进行交换。第二种解法,利用大根堆,创建一个大小为k的大根堆,将数组的前k个元素丢进大根堆中,然后再将数组剩余的元素与堆顶元素比较,如果小就交换并调整堆,最后堆里面就是最小的k个数;原创 2025-04-05 00:02:43 · 1333 阅读 · 67 评论 -
优选算法的还原之术:模拟专题
先初始化两个指针left和right,比较left和right是否相等,相等right就向右移动,当right的指向与left不相等时,right就停止,此时的left直接移动到right的位置。我们以示例1为例,先创建一个n行的矩阵,先向下填充,当x=n时,开始右上填充,当x=0时,又开始向下填充……如上图,k后面又是c,我们还需要给c记录上1,而我们要求的是所需不同青蛙的最少数目,所以我们从k里面借一个1,再去遍历最后一个"croak",最终k里面存的就是最终结果。当遍历到c时,哈希表中c加1。原创 2025-03-31 21:31:14 · 1585 阅读 · 68 评论 -
优选算法的逻辑之网:位运算专题
本文介绍了位运算的基础知识及其应用,包括6种位运算操作(与、或、异或、取反、左移、右移)的具体用法和运算规律。通过多个编程例题(如判定字符唯一性、寻找丢失数字、两数之和、只出现一次的数字等)详细讲解了位运算的解题思路和代码实现。重点阐述了利用位运算替代哈希表、高斯求和等传统方法,以及如何通过异或运算的特性高效解决问题。文章还提供了位运算的优先级建议和实用技巧,展示了位运算在算法优化中的强大功能。原创 2025-03-29 08:45:22 · 1452 阅读 · 38 评论 -
优选算法的速算之钥:前缀和专题(二)
我们先来回顾一下二位前缀和的计算:dp[i][j] = dp[i-1][j] + dp[i][j-1] - dp[i-1][j-1] + mat[i][j]。如果我们要在预处理矩阵dp[x][y]里面里的值,我们需要在mat[x-1][y-1]的位置找,在answer[x+1][y+1]的位置填入。我们先来思考暴力枚举:利用双指针left和right,当right移动到某一个位置时,left与right构成的区间之和为k时,此时right不能停止,数组元素可能为负的,有可能后面还存在和为k的子数组。原创 2025-03-26 08:32:10 · 1241 阅读 · 41 评论 -
优选算法的速算之钥:前缀和专题(一)
A区域的和可以很好的算出来:dp[i-1][j-1],但是B、C区域的和又不好算了。我们需要求的是某段数组元素的和,那么就可以利用前缀和思想,i左侧的元素之和f(i) = f(i-1) + nums[i-1],i右侧的元素之和g(i) = g(i+1) + nums[i+1]。第一步,我们先预处理出一个前缀和数组int[] dp,dp[i]代表的是arr数组区间[1,i]的和。而数组dp中每一个元素的算法不用再从头加到尾,直接利用递推公式dp[i] = dp[i-1] + arr[i]。原创 2025-03-23 18:46:29 · 1019 阅读 · 38 评论 -
优选算法的折半之智:二分查找专题(二)
通过上面的示例1,我们可以很明显地发现“二段性”,左边是严格大于某个值的递增区间,右边是严格小于某个值递增区间,并且数组中没有重复元素,折线表示如下图所示。从上图中我们已经可以看出,数组已经被分成两段,左边是递增的,右边是递减的,符合“二段性”,可以利用二分查找。我们注意一个细节:如果给定的数组为[0,1,2,3],数组元素与下标都是一一对应的,而缺失的数字为最后一个元素加一。在缺失的数字之前,数组元素与数组下标是一一对应的,到缺失的位置时,缺失的数字比数组元素小1。或者是递增的,或者是递减的。原创 2025-03-17 14:55:52 · 2162 阅读 · 55 评论 -
优选算法的折半之智:二分查找专题(一)
当x与目标值进行比较时,left可以移动到x右边,那么x的左边已经判断过了都是小于目标值,但右边还是未知的,即使left与right相遇,那这个数依然是未知的。right指针移动的时候,一直是在合法的区间,left一直想要跳出这个不合法的区间,直到两个指针相遇,无需判断,相遇的位置就是我们要找的结果。我们先来思考暴力解法:由于x是非负整数,那么x的平方根定义是小于等于x的。如果x=17,我们可以从1开始向右枚举,如果找到一个数n,n*n小于等于x,且(n+1)*(n+1)大于x,则x的平方根则为n。原创 2025-03-14 20:58:02 · 1333 阅读 · 53 评论 -
优选算法的时序之窗:滑动窗口专题(二)
只不过这次的count是统计字符的种类,因为在找字母异位词时,子串和字符串是一一对应的关系,这里字符却是大于等于的关系。我们看下图,我们从s中找出一段符合要求的子串,然后让left向后移动一步,此时会出现两种情况,要么缩小的区间还是符合要求,要么不符合要求,我们就让right向右移动,并且在这期间right是不需要回退的。我们还是先来思考一下暴力解法:先定义两个指针,我们以其中一个指针为起点,另一个指针向右移动,找到所有符合条件的子串,从里面挑出最短的长度。进窗口,把s中的字符串丢进哈希表中统计。原创 2025-03-03 23:06:35 · 6642 阅读 · 66 评论 -
优选算法的时序之窗:滑动窗口专题(一)
因为数组是具有单调性的,两个指针不用回退,就可以省去很多不必要的枚举,让子数组里的元素不停进出,维护更新子数组的和。是不需要的(如下图所示),right要么不动,要么向右移动,此时又符合同向双指针的解法,也就是可以使用滑动窗口。我们取出一段子数组,里面的水果种类kinds恰好为2,此时让left向右移动,会出现两种结果:kinds不变,right也不需要向右移动;“判断”,当子数组的和大于target时(这里不写等于,是为了防止代码书写混乱的),left指针向左移动,完成“出窗口”的操作;原创 2025-02-27 22:53:18 · 2080 阅读 · 57 评论 -
优选算法的协作之舞:双指针专题(二)
但此时还有一种特殊情况,如果复写之后最后一个元素为零,那么dest就会越界,所以我们还要单独处理一下边界,只需把倒数第二个元素直接复写成零,然后cur向前移动一位,dest向前移动两位。我们先对三元子数组进行去重,当我们left或者是right移动一位所指向的值不变,那么就继续移动,此时我们需要注意指针是否会越界。首先我们依然想到的是先排序,然后暴力枚举,利用三层for循环来判断三数之和是否为零,由于题目不要求输出的顺序和三元组的顺序,所以我们需要对三元组进行去重的操作,那么此时的时间复杂度就是。原创 2025-02-17 16:14:41 · 1387 阅读 · 48 评论 -
优选算法的协作之舞:双指针专题(一)
本文介绍了双指针算法及其在数组问题中的应用。主要内容包括:1. 双指针算法思想:通过两个指针在数组/链表中高效遍历和操作;2. 典型例题解析:查找两数之和(有序数组)、盛水容器最大面积、移动零元素、有效三角形个数;3. 解题思路:利用数组特性(如单调性、边界条件)优化暴力解法,其中重点讲解了如何通过指针移动和比较来降低时间复杂度。每种解法都附有详细代码实现,展示了如何将双指针技巧具体应用于不同场景。原创 2025-02-03 15:14:32 · 12481 阅读 · 61 评论