- 博客(114)
- 收藏
- 关注
原创 技巧----5.寻找重复数
将这几个元素一次串起来形成一个链表: 1-->3-->2-->4-->2-->4-->2.....,该链表会存在一个环,环的入口即为重复的元素。快指针两步/次 慢指针一步/次 相对速度为1 快指针必然和慢指针相遇,不会跳过慢指针。当快慢指针在环内相遇时,再从起点出发一个慢指针,两慢指针相遇点必然是环入口点。例[1,3,4,2,2]
2025-08-25 14:06:36
307
原创 技巧----4.下一个排列
从末位开始向前遍历(实际上是末位的前一个元素),找到第一个满足nums[i] < nums[i + 1]的元素i;将i以后的元素进行翻转,也就是翻转[i + 1,nums.length - 1];后面原本是降序,反转后变为升序,完全实现最小幅度的增大。再次从末位向前遍历,找到第一个满足nums[j] > nums[i]的元素j,并将i与j交换;即最小幅度的增大(不完全)下一个排列:高位尽量不变,低位通过调整实现最小幅度的增大,若原本就是递减排序(无法再增大)则直接将其逆序转变为升序排列。
2025-08-25 14:05:13
222
原创 技巧----3.颜色分类
若nums[i] == 0,则将nums[i]与nums[left]交换,再迭代i与left即可;即将小于基准的置于左边。若nums[i] == 2,则将nums[i]与nums[right]交换,再迭代right即可;即,与三路快排思想类似将数组分为三个区域,基准为1,小于基准的放左边,等于基准的放中间,大于基准的放右边。若nums[i] == 1,则无需处理,迭代i即可;最终状态:[0,L)-->0;[L,i)-->1;定义三个指针,left、right置于数组两端,i用于遍历数组。
2025-08-25 14:03:24
297
原创 技巧----2.多数元素
遍历数组,记录遍历到的元素res,迭代count遇到相同的元素count++,遇到不同的元素count--若count已经为0,又遇到了不同元素,则重置res与count;遍历结束返回res即为多数元素。定义一个count与res;res记录多数元素,count记录该元素出现次数(并非真实次数)摩尔投票只能在确定有多数元素时才能使用。
2025-08-25 13:59:52
230
原创 技巧----1.只出现一次的数字
异或运算,对于任何一个整数与他本身异或结果为0,与0异或结果为他本身;且异或运算满足交换率,异或顺序改变结果不变。则只需要遍历数组,任选一个元素与其他元素依次异或即可。
2025-08-25 13:58:13
166
原创 二维动态规划----5.编辑距离
若在S1末尾(当前处理到的末尾)插入S2[j]:则此时S1、S2末尾元素i相同,退化至相等的情况,再各自向前推一个元素即可,由于S1末尾插入元素,向前推还是i。则minOps[i][j] = 1 + minOps[i - 1][j - 1] 即替换操作 + S1、S2各自向前推一个字符对应的状态。若S1[i] == S2[j],则minOps[i][j] = minOps[i - 1][j - 1],minOps[i][j]:代表S1与S2分别以i、j结尾(长度),将S1替换为S2需要的最少操作步骤。
2025-08-25 13:55:02
1157
原创 二维动态规划----4.最长公共子序列
即当前两字符不相等,LCS[i][j]则完全取决于在此之前的字符,两串分别舍去一个字符取最大,即在LCS[i - 1][j],LCS[i][j - 1]中挑选最大的。= S2[j],则LCS[i][j] = Math.max(LCS[i - 1][j],LCS[i][j - 1])若S1[i] == S2[j],则LCS[i][j] = 1 + LCS[i - 1][j - 1],LCS[i][j]:代表S1以i(长度)结尾和S2以j(长度)结尾的最长公共子序列的长度()
2025-08-25 13:53:20
163
原创 二维动态规划----3.最长回文子串
注,回文子串长度可能是奇数也可能是偶数,枚举时奇数情况(从同一个点开始扩散) 与 偶数情况(从两个相邻的点开始扩散)需要同时扩散。从0开始枚举,假设每个元素都是一个回文子串的中心,向两遍扩散(单独一个字符是长度为1的回文子串)回文子串特点:中心对称,从中间出发向两端扩散,对比两指针指向的元素是否相同。
2025-08-25 13:51:23
132
原创 二维动态规划----2.最小路径和
(i,j):(i - 1,j)向下 或 (i,j - 1)向右 minPathSum[i][j] = Math.min(minPathSum[i- 1][j],minPathSum[i][j - 1]) + grid[i][j](1,1):(0,1)向下 或 (1,0)向右 minPathSum[1][1] = Math.min(minPathSum[0][1],minPathSum[1][0]) + grid[1][1]minPathSum[i][j]:到达(i,j)的最小路径和。
2025-08-21 12:10:37
205
原创 二维动态规划----1.不同路径
pathCount[i][j] == pathCount[i - 1][j] + pathCount[i][j - 1]pathCount[1][1] = pathCount[0][1] + pathCount[1][0]pathCount[1][2] = pathCount[0][2] + pathCount[1][1]对于第0行的所有元素其只能通过向右移动到达,即pathCount[0][j] = 1。对于第0列的所有元素其只能通过向下移动到达,即pathCount[i][0] = 1。
2025-08-21 12:08:46
245
原创 动态规划----10.最长有效括号
以下标i结尾:validLen[i]--> if(s[i] == ')') --> if(s[i - 1] == ')') --> if(s[i - validLen[i - 1] - 1] == '('])以下标i结尾:validLen[i]--> if(s[i] == ')') --> if(s[i - 1] == '(') --> validLen[i] == 2 + validLen[i - 2]validLen[]:有效长度,validLen[i]代表,以下标i结尾时连续有效括号长度。
2025-08-21 12:06:38
1092
原创 动态规划----9.分割等和子集
那么此时canMake[2]为true,当canMake[4]时,发现canMake[2] = true,且num = 2,那么会误认为canMake[4] = true。实际上num = 2,即面额为2的纸币在canMake[2]时已经使用过一次了,canMake[4]为false才对,正序遍历导致重复使用故而不能正序遍历。canMake[i] = canMake[i] || canMake[i - num],在凑目标值时,当前num要么使用,要么不使用。
2025-08-21 12:01:19
345
原创 动态规划----8.乘积最大子数组
maxProduct、minProduct 当前最大、最小乘积 res记录结果。注意,乘上一个负数会让小的变大,大的变小,保存max的同时min也需要保存。
2025-08-20 23:05:52
166
原创 动态规划----7.最长递增子序列
以下标为i结尾:maxSubseq[i]---> if(nums[i] > nums[0...i-1]) ---> Math.max(maxSubseq[0...i-1] + 1 )以下标为3结尾:maxSubseq[3]---> if(nums[3] > nums[0,1,2]) ---> Math.max(maxSubseq[0,1,2] + 1 )以下标为1结尾:maxSubseq[1]---> if(nums[1] > nums[0]) ---> maxSubseq[0] + 1。
2025-08-20 23:01:13
397
原创 动态规划----6.单词拆分
当前前1个字符组成的单词足够匹配wordDict中的单词(1 >= len) 且前1 - len个字符构成的单词在wordDict中存在(s[0,1-len)) 且当前[1-len,1)的len个单词在wordDict是否存在。当前前i个字符组成的单词足够匹配wordDict中的单词(i >= len) 且前i - len个字符构成的单词在wordDict中存在(s[0,i-len)) 且当前[i-len,i)的len个单词在wordDict是否存在。其余通过后续递推更改状态。
2025-08-20 22:59:00
297
原创 动态规划----5.零钱兑换
凑出为2的金额:Math.min(minCoins[2],minCoins[2 - coin:coins] + 1) 直接用面值2(有的话) 或先直接用一张小于等于2的面额,再加上凑出minCoins[2 - coin:coins]需要的硬币数。凑出为i的金额:Math.min(minCoins[i],minCoins[i - coin:coins] + 1) 直接用面值i(有的话) 或先直接用一张小于等于i的面额,再加上凑出minCoins[i - coin:coins]需要的硬币数。
2025-08-19 17:40:13
1221
原创 动态规划----4.完全平方数
凑出为i的完全平方数:Math.min(minNums[i],minNums[i - num:nums] + 1) 直接用元素i(有的话) 或先直接用一个小于等于i的元素,再加上凑出minNums[i - num:nums]需要的元素数。凑出为2的元素数:Math.min(minNums[2],minNums[2 - num:nums] + 1) 直接用元素2(有的话) 或先直接用一个小于等于2的元素,再加上凑出minNums[2 - num:nums]需要的元素数。
2025-08-19 17:38:31
317
原创 动态规划----3.打家劫舍
偷到第i个房间 maxRob[i]: Math.max(maxRob[i - 2] + nums[i],maxRob[i - 1]) 若偷i - 2,则maxRob[i - 2] + nums[i](偷i - 2可偷i) 若偷i - 1,则maxRob[i - 1](偷i - 1不可偷i)偷到第1个房间: maxRob[1]: Math.max(nums[0],nums[1]) (不能连着偷两个,必然是前两个中最大的) 要么偷0,要么偷1,偷最大的那个。只能间隔一个房间再偷(从0开始计算)
2025-08-18 15:46:49
424
原创 动态规划----1.爬楼梯
3阶:由1阶迈2步,或2阶迈一步;4阶:由2阶迈2步,或3阶1步;n阶:由n-2阶迈2步,或n-1阶迈1步。1阶:1步,即1种;2阶:1步+1步或直接2步,即2种。
2025-08-12 22:16:35
339
原创 贪心----4.划分字母区间
首先遍历一遍字符串,记录每个字符最后出现的位置(不断迭代字符出现的位置即可)再次遍历字符数组,维护一个变量end,代表已出现元素最后一个位置,当end == i时,代表当前所有已经出现元素都囊括其中。
2025-08-12 22:14:38
161
原创 贪心----3. 跳跃游戏 II
即,在遍历过程中将数组分为了不同的区间,当移动至end时当前区间结束,更新下一个区间终点为max_reachable,区间数即为最少需要的步数。当移动至end即当前区间终点时,需要更新end为新的max_reachable即下一个区间终点,同时步数+1。遍历时迭代遍历过的元素最远可达位置,利用end记录当前区间终点(随max_reachable变化)end,当前区间终点(随max_reachable变化)max_reachable,遍历过的元素的最远可达位置。区间代表每步最多移动的位置。
2025-08-11 22:06:26
429
原创 贪心----2.跳跃游戏
遍历数组维护一个变量max_reachable = index + nums[i];代表当前位置能到达的最远位置,能到nums.length - 1,即能达到最后一个下标。遍历需要在nums.length - 2处终止,nums.length - 1处就是最后一个位置。若遍历过程中出现max_reachable == index,则代表无法继续往下走了,直接返回false。每个位置的数值代表能走的最大步数,能否走到最后一格?
2025-08-11 22:05:07
254
原创 贪心----1.买卖股票的最佳时机
若股票价格小于当前min,则更新min最低点为当前价格,max置为-1(即遇到新的低点,之前的高点作废,无法在过去的时间卖出)min,初始化为Integer.MAX_VALUE,记录低点;resMax,记录最大利润。若股票价格大于当前高点,则更新max最高点为当前价格,并更新resMax记录最大利润。买卖股票的最佳时机:先寻找到一个低点,再在低点之后找更高的高点,记录差值,差值最大的即为最大利润。
2025-08-09 22:53:47
284
原创 堆----3.数据流的中位数
(即先将元素插入大顶堆,再把大顶堆最大的元素移到小顶堆,实现了大顶堆元素最大值 <= 小顶堆元素最小值)(由于为了实现大顶堆元素最大值 <= 小顶堆元素最小值,将大顶堆的最大元素移到了小顶堆,此时必须要平衡两堆元素,否则元素会全部留在小顶堆中)若两堆元素个数相等,则中位数:大顶堆堆顶元素(大顶堆最大值)与小顶堆堆顶元素(小顶堆最小值)的平均数。此时进行判断,若小顶堆元素更多,则将小顶堆堆顶元素(小顶堆最小元素)弹出,插入到大顶堆中;大顶堆元素最大值 <= 小顶堆元素最小值,且两堆元素相差个数不超过1。
2025-08-09 22:51:54
433
原创 堆----2.前 K 个高频元素
创建一个大小为length + 1的List数组,下标代表元素出现次数,出现次数一致的元素放在同一个数组中。长度为length的数组,可能存储的元素全部相同,则最高频元素出现次数为length。下标代表频率即元素出现次数,则List数组大小需为length + 1。首先遍历数组,使用HashMap统计每个元素出现的次数。倒数遍历List数组即可得得到前K个高频元素。
2025-08-02 21:15:08
233
原创 堆----1.数组中的第K个最大元素
交换左右指针的值,交换后,大于基准元素的在右半区,小于基准元素的在左半区,等于基准元素的平均分布在左、右半区。若基准 < len - K, 则减小范围,在[基准 + 1,right]中重新选择基准,重复上述流程。若基准 > len - K, 则减小范围,在[left, 基准 - 1]中重新选择基准,重复上述流程。在本题中,若划分后基准所在位置恰好是len - k,那么说明基准就是第K个最大元素。第K大元素---->len - K,则数组中第K个最大元素,转化为寻找排序后第len - K位置的元素。
2025-08-02 21:13:47
522
原创 栈----5.柱状图中最大的矩形
若待入栈元素比栈顶元素小,栈顶元素右边界确定即为待入栈元素,弹出栈顶元素,重复上述流程直到栈为空或待入栈元素比栈顶元素大。双重循环暴力枚举寻找边界,O(n²);引入单调栈一次遍历得出所有柱子左右边界。以当前柱子为起点,向左寻找第一个更低的为左边界,向右寻找第一个更低的为右边界。若待入栈元素比栈顶元素大,则待入栈元素左边界即为栈顶元素,入栈。左边界初始化为-1;右边界初始化为heights.length。首先将元素逐个入栈,栈中存放柱子索引。
2025-07-27 23:53:17
805
原创 栈----4.每日温度
入栈时进行判断,若待入栈元素比栈顶元素大,则下标相减得到栈顶元素的answer,将栈顶元素弹出。重复上述流程继续判断,直到 待入栈元素 <= 栈顶元素,入栈判断数组中下一个元素。若数组遍历完毕,栈中还有元素,则栈中剩余元素answer为0。首先将元素逐个入栈,栈中存放元素的索引。
2025-07-27 23:51:48
284
原创 栈----3.字符串解码
k[encoded_string] ---> k个连续的s;encoded_string = k[encoded_string](可嵌套)3.继续弹出直到栈顶元素不为数字,k读取完毕,重复拼接k次重新入栈 ---> 当前k[encoded_string]转码完毕。1.将所有字符压入栈中,直到遇到']' ---> 当前层级的encoded_string读取完毕,开始转换。2.弹出栈,将弹出的元素拼接,直到遇到'[' ---> 当前层级的encoded_string转化完毕。
2025-07-26 23:14:33
350
原创 栈----2.最小栈
push操作: 判断push后主栈的最小值,即辅助栈栈顶元素与待添加元素相比,将较小的值加入辅助栈中。辅助栈,记录当前状态主栈的最小值,即每当主栈状态发生改变时,辅助栈也要做相应更改。主栈,普通栈的功能,push、pop、top均在主栈进行。pop操作: 辅助栈也做相应pop操作,即减少一个状态。相较于传统的栈额外需要常数级时间复杂度获取栈中最小值。
2025-07-26 23:12:48
280
原创 栈----1.有效的括号
遇到右括号就弹出栈顶元素判断是否一致(当前遍历到的最后一个出现的左括号是否能闭合)左括号必定先出现,每个左括号都需要一个右括号与之匹配,后出现的左括号先匹配。若弹出的右括号与遇到的右括号不一致---> 左右类型不匹配,不合法。若遇到右括号时栈中无元素可弹出---> 右括号多余无法闭合,不合法。若字符串遍历结束栈中仍有元素---> 左括号多余无法闭合,不合法。依据后出现的左括号先匹配,很容易联想到栈,即后进先出。遍历字符串,遇到左括号就在栈中添加一个对应的右括号。
2025-07-25 23:28:27
670
原创 二分查找----6.寻找两个正序数组的中位数
i + j = (m + n + 1) / 2 ---> 左右半区元素个数相等(奇);nums1取i个、nums2取j个。中位数即为nums1前i个元素与nums2前j个元素中较大的那个,且i + j = (m + n + 1) / 2(兼容奇偶)i + j = (m + n + 1) / 2。nums1的前i个元素与nums2的前j个元素构成合并后的左半区(包括中位数),nums1[i - 1] < nums1[i] (必然满足)nums2[j - 1] < nums2[j] (必然满足)
2025-07-25 23:26:38
1611
原创 二分查找----5.寻找旋转排序数组中的最小值
nums[left] <= nums[mid] && nums[mid] <= nums[right] ---> [left,right]已经在同一区间,直接返回结果即可。仅满足:nums[left] <= nums[mid] ---> [left,right]不单调,且mid在左半区;在左半区则淘汰[left,mid],在右半区则淘汰[mid,right]均不满足,[left,right]不单调,且mid在右半区;依据数组长度,mid可能落在左半区也有可能落在右半区,最小值在右半区。
2025-07-24 23:49:54
635
原创 二分查找----4.搜索旋转排序数组
若mid在右半区:nums[mid] < target && target < nums[right]--->target与mid同在右半区,继续常规二分即可。若mid在左半区:nums[left] < target && target < nums[mid]--->target与mid同在左半区,继续常规二分即可。nums[left] <= nums[mid]---> mid必定在左半区,反之在右半区;若target不在端点到mid之间,则无法具体确定其是在mid所在半区的剩余部分,还是另一个半区。
2025-07-24 23:48:22
813
原创 二分查找----3.在排序数组中查找元素的第一个和最后一个位置
左边界:当查到目标元素时,记录下标并将right迭代为mid-1,继续向左二分直到搜寻结束,得到左边界。右边界:当查到目标元素时,记录下标并将left迭代为mid+1,继续向右二分直到搜寻结束,得到右边界。添加变量boolean isLeft,控制查找方向,一个方法即可完成左、右边界的查找。查找左边界与右边界大量代码相同,仅left与right指针迭代逻辑不同。在一个近似递增的数组中查找指定元素,该元素可能有多个找出其出现的区间。即进行两次改进二分,查找左边界与右边界。
2025-07-23 22:13:54
554
原创 二分查找----2.搜索二维矩阵
每行都是递增的,对每行进行二分,逐行查找;效率不高,每次搜索只能控制列无法兼顾到行,行被固定存在不必要的搜索。从右上或左下顶点出发,以右上为例,向左迭代列减小,向下迭代行增大;效率更高避免重复搜索。
2025-07-23 22:10:47
351
原创 二分查找----1.搜索插入位置
若 mid > target 说明目标值在左半区,淘汰右半区迭代right,重新计算mid重复上述流程。若 mid < target 说明目标值在右半区,淘汰左半区迭代left,重新计算mid重复上述流程。定义left,right两指针处于数组两端,计算mid并读取对应值与目标值做比较,再做相应处理。不断将数组拆分为两个子区间,淘汰不可能出现结果的子区间、缩小查找范围,直至搜寻到结果。以O(log n)的时间复杂度快速查找元素--->二分查找。二分查找的特性也可直接改写为递归。
2025-06-20 23:27:20
2028
原创 回溯----8.N皇后
首先选取第一行第一格放置第一个棋子,再从第二行第一个位置开始选取合法的位置(不同行不同列不同斜线)放置棋子,重复上述流程迭代行数,若放置途中出现无合法位置的情况,回溯将上一行棋子放置在其他合法位置,再重复上述流程继续放置直到n个棋子。成功放置n个棋子后得到第一种情况,开始回溯重复上述流程,直到回溯至第一行的每个格子都尝试过,得到所有结果。List<String> BoardToList 棋盘格式转换。将n个棋子放在n*n的棋盘上,不同列,不同行,不同斜线。
2025-06-19 22:51:55
1912
原创 回溯----7.分割回文串
继续回溯index,直到最初始的for循环执行完毕切割到最后一个元素,执行完毕得到所有结果。若是则保存到tempRes中,并从i + 1处开始重复上述流程从第一个元素开始切割,直到切割到最后一个元素保存结果。回溯index(起始点),迭代i(切割点),重复上述流程直到切割到最后一个元素。首先从i = 0开始,从第一个元素处开始切割,判断是否是回文子串。若不是则迭代i,从下一个元素处开始尝试切割。将字符串分割为若干回文子串;回文子串: 单个字符、正序倒序一样。
2025-06-19 22:48:16
1918
空空如也
空空如也
TA创建的收藏夹 TA关注的收藏夹
TA关注的人
RSS订阅