万字总结LeetCode100(持续更新...)

核心算法思想

遍历:深度优先遍历、广度优先遍历

子问题:动态规划、贪心(维护最值问题)、分治(快排;用的很少)

双指针:快慢指针、左右指针、滑动窗口(可变、固定)

二分法:二分查找

空间换时间:前缀和(经常与HashMap搭配)、线段树(需要背诵构建过程)、动态规划、记忆化搜索(提前存储)

暴力:遍历(深度优先遍历最常用的就是递归)

输入提示:如果输入的n不大,可以将所有结果提前存储起来,相当于空间换时间.

**逆向思维:**从右向左;先卖出后买入

(ps:仅仅列出相关思想名称,具体思路、常见应用场景请看另一篇)

常用数据结构

哈希表:HashMap 、 HashSet

栈:普通栈、单调栈(应用场景:在一维数组中找第一个满足某种条件的数)

队列:普通队列、单调队列、优先队列(大/小根堆)

(ps:一般的栈与队列在java中底层结构一致,只不过用的方法不同)

特殊类型(链表、树)题目技巧

链表

链表题目

总结:

方法及结构:

空间换时间:list、数组

双指针(尤其是快慢指针)

递归:

两种具体的编写原则:

1.选择:本次有n种选择,通过遍历所有选择,找到最优解,一般返回值都是void.

此外,一般选择问题都需要-1回到之前的状态。

2.目的(这种比较难理解,需要多做题):本次局部目的,一般返回值不是void,基本上都有返回值因为是局部目的。

目的问题不需要回到之前的状态,而是所有选择都需要。

共同点:(1)边界条件(一般都是null或者等于某个值) (2)具体处理方式

题目实例:

选择:

目的:101. 对称二叉树 - 力扣(LeetCode)

树(二叉树)

前中后遍历常见含义:

中序:对于二叉搜索树,升序遍历

前序:先获取(操作)根,再获取左右子节点。适合复制一棵树。(一般的深度优先遍历就是前序遍历,操作然后递归到左右子节点)。

后序遍历:先获取左右子节点,在获取根。适合删除一棵树。

图论

常见数组:

(1)visited[] : 访问数组用来标记某个位置是否被使用

(2)int[][] directions = {{0,1}, {1,0}, {0,-1}, {-1,0}};

java 复制代码
int[][] dirs = {{0,1}, {1,0}, {0,-1}, {-1,0}};方向数组,分别表示右下上左
for (int[] dir : dirs) {
    int ni = i + dir[0]; // 新的行坐标
    int nj = j + dir[1]; // 新的列坐标
}

递归

模板:

思路:

(1)

(2)

(3)

二分查找

目的:在logn复杂度下实习查找,前提有数组需要具有一定的规律。

模板:

java 复制代码
class Solution {
    public int searchInsert(int[] nums, int target) {
        int n = nums.length;
        int left = 0, right = n - 1, ans = n;
        while (left <= right) {
            int mid = ((right - left) / 1) + left;
            if(target == nums[mid]){
                ans = mid;
                return ans;
            }
            else if (target < nums[mid]) {
                ans = mid;
                right = mid - 1;
            } else {
                left = mid + 1;
            }
        }
        return ans;
    }
}

贪心算法

常见场景:最值

目的:通过局部最优解得到全局最优解。

模板: 维护一个最值(局部最优解),根据该最值,计算下一步

动态规划

常见场景:最值;状态转移?

状态定义:

(1)以第i个为终点(不必选),目前最值是多少。

状态转移方程:第i个选不选,然后取最大值。

(2)以第i个为终点(必选),目前最值是多少。

状态转移方程:dp[i]由前面经过操作得到。

(2)以第i个为终点(必选),目前最值是多少。

状态转移方程:dp[i]由前面经过操作得到。

初始状态:

状态转移函数:

输出:

状态的数量:dp[n]还是dp[n+1]

遍历方式:从前往后、从后往前、交叉

未完成

108. 将有序数组转换为二叉搜索树 - 力扣(LeetCode)

146. LRU 缓存 - 力扣(LeetCode)

105. 从前序与中序遍历序列构造二叉树 - 力扣(LeetCode)

208. 实现 Trie (前缀树) - 力扣(LeetCode)

4. 寻找两个正序数组的中位数 - 力扣(LeetCode)

155. 最小栈 - 力扣(LeetCode)

215. 数组中的第K个最大元素 - 力扣(LeetCode)快排未完成

295. 数据流的中位数 - 力扣(LeetCode)

85.编辑距离

86.多数元素

题目总结

1.1. 两数之和 - 力扣(LeetCode)

核心算法思路:简单推理

数据结构:HashMap

技巧:

(1)由于不能用相同的数,所以不用全部直接放进去,可以在放入该值之前就进行一次判断,这样就不用担心取到同一个数了。

注意点

(1)不能重复使用同一个数,因此一开始不能将所有数全部存储哈希表中。(ps:存储也可以,但需要增加一步校验(i!=j)确保该值没有被(自己)使用过。)

15. 三数之和 - 力扣(LeetCode)

核心算法思路:简单推理

数据结构:HashMap

技巧:

(1)与两数之和,基本上一模一样,只不过加一层循环,这层循环的便是target.

(2)常见函数:

List排序:

Collections.sort(list);//升序排序

Collections.sort(list,Collections.reverseOrder());//降序排序

针对List是数组(两个元素)的排序

Collections.sort(list,(a,b) -> b-a);//降序排序

HashSet转化为List:List<List<>> list = new ArrayList<>(set);

数组转为List: list = Arrays.asList(array)

2.49. 字母异位词分组 - 力扣(LeetCode)

核心算法思路:简单推理

数据结构:HashMap

技巧:

(1)利用26字母有限的特点,将异位词统一。

具体方式:

(1)记录字符串26字符的个数:'char' - 'a'

(2)将字符数组转化:如果数量不为0,则输出'a'+int

注意点

(1)不能重复使用同一个数,因此一开始不能将所有数全部存储哈希表中。(ps:存储也可以,但需要增加一步校验(i!=j)确保该值没有被(自己)使用过。)

3.128. 最长连续序列 - 力扣(LeetCode)

核心算法思路:简单推理:找到起点方式

数据结构:HashSet

技巧:利用HashSet在O(1)复杂度下找到某个序列的起点,然后确定该起点的长度,最后对比获取最大长度。

4.283. 移动零 - 力扣(LeetCode)

核心算法思路:双指针

数据结构:快慢指针

技巧:

要求"原地"的一般只能是双指针来完成交换。

具体方式:

(1)slow指的是0的位置,也就是slow指针等待fast找到最近的非零数与其交换。

5.11. 盛最多水的容器 - 力扣(LeetCode)

核心算法思路:双指针

数据结构:左右指针

技巧:

面积题目:的确定,一般来说都是固定其中一个然后遍历另一个,但是这种方式的时间复杂度较高,不符合题目要求。

具体方式:

我认为这个题目算是一个背诵题,不是太容易想到。

左右指针的移动条件:高较小的指针进行移动,因为只有变化较小的,才可能获取比当前更大的面积。

6.42. 接雨水 - 力扣(LeetCode)

核心算法思路:空间换时间(动态规划);单调栈;双指针

数据结构:记忆化搜索(数组)

技巧:

方法一:动态规划

(1)计算每个位置左右两侧的最值问题:可以用记忆化搜索两次遍历将最值提前获取,避免每次都要针对某个位置获取其最值。

具体方式:

(1)从左向右找到找到每个位置左侧的最值

(2)从右向左找到找到每个位置右侧的最值

方法二:单调栈

搁置,这个真想不到用栈呀。o(╥﹏╥)o

方法三:双指针

如果用双指针我认为便是一个背诵题目,极难想到。

基本目标:找到某个位置两侧的最高值中较低的一个。

官方题解双指针思路解释

以左侧为例(left指针<=right指针时):left指针能走到当前位置,说明右侧一定存在一个值比我左边所有值都大,因此说明我左侧最大值肯定是最值中较小的一个,这样便能确定该位置对应的雨水量。

7.3. 无重复字符的最长子串 - 力扣(LeetCode)

核心算法思路:双指针

数据结构:HashMap

技巧:

(1)左右指针确定一个滑动窗口。

(2)HashMap用来确定最新的重复字符的位置。

具体方式:

(1)左指针是起点,右指针指的是最新的位置

(2)当右指针所指的字符在HashMap中存在时,left跳跃到该位置的下一个,并且计算刷新current的值,并检验是否更新最大值。

注意点:

(1)如果fast指向的字符在hashmap中存在,还需要判断在这个map中该字符的位置是否大于等于 left**,**只有大于等于才需要跳转,否则就跑到前面了,没有意义。

8.438. 找到字符串中所有字母异位词 - 力扣(LeetCode)

核心算法思路:双指针(固定滑动窗口)

数据结构:List(固定长度)

技巧:

(1)利用字母有限性:将字符串相等转换为数组是否相等。

数组判断函数:Arrays.equal(arr1,arr2);

具体方式:

(1)先将初始长度的数组构建起来。

(2)for循环遍历,将数组第一个位置对应字符--,下一个位置(i+len)对应字符数++

9.560. 和为 K 的子数组 - 力扣(LeetCode)

核心算法思路:空间换时间(前缀和)

数据结构:HashMap

技巧:

(1)利用HashMap查找前面前缀和符和要求的数量。

具体方式:

(1)将所有前缀和存储起来,key是前缀和,value是数量。

注意点:

(1)不要将全部的前缀和提前存储,否则可能是查询到后面的前缀和,不符合要求。因此想要只查询前面的前缀和,需要遍历加入。

(2)千万别忘记map.put(0,1),需要提前将一个前缀和为0,次数为1的提前放进去。

10.239. 滑动窗口最大值 - 力扣(LeetCode)

核心算法思路:简单推理

数据结构:优先队列(大根堆);单调队列

技巧:

方法一(优先队列):

(1)构建大根堆,存储的为(value,index)

具体方式:

(1)利用前k个值构建初始窗口(大根堆)。

(2)每次来新数,便peek堆顶的元素,判断是否过期,如果没有过期则为答案,如果过期则pop,直到找到没有过期的最大值。

注意点:

(1)优先队列(大根堆)构建方法。

java 复制代码
PriorityQueue<int[]>  pq = new PriorityQueue<int[]>(
     new Comparator<int[]>(){
        public int compare(int[] pair1,int[] pair2){
                return pair1[0] != pair2[0] ? pair2[0] - pair1[0] : pair2[1] - pair2[1];
            }

        }
);

方法二(单调队列):

具体方式:

(1)for k次来构建初始双端队列。

(2)每次来新数,while循环队列不为空并且队尾的值小于新值,则pop队尾元素,最后加入该新值,while循环peak队首元素是否为过期,如果过期则pop,否则作为答案。

11.76. 最小覆盖子串 - 力扣(LeetCode)

核心算法思路:双指针(快慢指针)

数据结构:HashMap

技巧:

(1)相较于之前的利用26个小写字母作为判断字符串是否相等的方式,存在一个弊端,那就是必须字符串str必须全部为小写或者全部为大写。用HashMap可以进行优化,这样可以存储大小写甚至数字。

具体方法:

(1)快指针先找到包括所有子串字符的字符串。

(2)慢指针开始移动,缩小到当前最短位置。记录刷新答案子串的strat与end.

(3)慢指针移动到下一个位置。

注意点:

(1)遍历HashMap的方式:

java 复制代码
for(Map.Entry<> entry : map.entrySet()){
    entry.getKet();
    entry.getValue();

}

12.53. 最大子数组和 - 力扣(LeetCode)

核心算法思路:动态规划;线段树

数据结构:无(数组)

技巧:

动态规划使用条件:当前值能否由上一个状态计算得来

核心:

(1)dp的定义:以i结尾的最大值

(2)状态转移方程:dp[i] = dp[i-1] + nums[i]; //dp[i-1]>0

(3)遍历方式:从左向右递增即可

补充:线段树非常适合解决这个问题

13.56. 合并区间 - 力扣(LeetCode)

核心算法思路:简单推理

数据结构:无(数组)

技巧:

(1)将数组按照第一个元素的大小进行升序排序。

(2)for遍历,并与答案列表中最后一个元素进行对比。

注意点:

(1)单个元素为数组的排序方式:

java 复制代码
        Arrays.sort(nums,new Comparator<int[]>(){
            public int compare(int[] p1, int[]p2){
                return p1[0] != p2[0] ? p2[0]-p1[0] : p2[1]-p1[1];
            }
        });

(2)将list转化为数组的方式:

java 复制代码
int[][] arr = merged.toArray(new int[m][]);

14.189. 轮转数组 - 力扣(LeetCode)

核心算法思路:简单推理

数据结构:无(数组)

技巧:

方法1:取模构建新数组

方法2:双指针原地交换

方法3:翻转数组(背诵题):三次翻转

  1. 238. 除自身以外数组的乘积 - 力扣(LeetCode)

核心算法思路:空间换时间

数据结构:无(数组)

技巧:

(1)提前存储好当前位置左右两侧的乘积即可。

  1. 41. 缺失的第一个正数 - 力扣(LeetCode)

核心算法思路:简单推理

数据结构:HashSet

技巧:

(1)利用HashSey在O(1)时间复杂度下判断i(1-n)是否存在,返回结果。

注意点:

(1)技巧存在一个问题:空间复杂度是O(n)不符合要求。

以后再认真解决这个问题吧,感觉有些难o(╥﹏╥)o

  1. 73. 矩阵置零 - 力扣(LeetCode)

核心算法思路:简单推理

数据结构:无

技巧:

(1)for循环遍历,找到需要置零的行数组与列数组即可。

(2)优化空间复杂度,将第一行与第一列作为标记数组,遇到等于0的则,打上标记也就是对应第一行/列置0。值得注意的是第一行与第一列需要单独判断,其本身是否包括0,如果不包括就不变,如果包括就全部置零。

  1. 54. 螺旋矩阵 - 力扣(LeetCode)

核心算法思路:简单推理

数据结构:无

技巧:

(1)设计一个上下左右的二维数组,即右:(1,0) 下 :(0,1) 左: (-1,0) 上: (0,-1)

具体过程:

(1)到达边界后,切换方向也就是x=ans[(i+1)%4][0]; y = ans[(i+1)%4];

(2)结束条件是list中的数量等于amount.

注意点:

(1)技巧存在一个问题:空间复杂度是O(n)不符合要求。

  1. 48. 旋转图像 - 力扣(LeetCode)

核心算法思路:简单推理

数据结构:无

技巧:

(1)这个题目是一个公式推理题,推出了i与j转换后的公式,遍历替换即可。

具体过程:

arr[i][j] --> arr[j][cow-i]

  1. 240. 搜索二维矩阵 II - 力扣(LeetCode)

核心算法思路:简单推理;二分法

数据结构:无

技巧:

方法1:Z字查找:从右上角开始遍历

方法2:先确定在行数,在进行二分查找

具体过程:

(1)到达边界后,切换方向也就是x=ans[(i+1)%4][0]; y = ans[(i+1)%4];

(2)结束条件是list中的数量等于amount.

注意点:

(1)技巧存在一个问题:空间复杂度是O(n)不符合要求。

  1. 160. 相交链表 - 力扣(LeetCode)

核心算法思路:简单推理;

数据结构:HashSet

技巧:

HashSet辅助查找是否存在某个节点。

具体过程:

将其中一个链表的所有节点存储到HashSet,然后遍历另一个链表,如果有存在的则返回该节点,如果没有直接返回null.

  1. 206. 反转链表 - 力扣(LeetCode)

核心算法思路:简单推理

数据结构:链表(题目)

技巧:

(1)前节点(prev),当前节点(curr),后节点(nex)。

(2)核心思想就是让当前节点指向前节点,但不能丢失后节点。

注意点:

(1)while(cur != null) 作为循环条件,如果以cur.next作为循环条件,最后一个为cur的时候,next为null,便进入不了循环,也就意味者,最后一个节点没办法指向前一个节点。(其实也可以以cur.next作为条件,只不过需要再额外写一步)

  1. 234. 回文链表 - 力扣(LeetCode)

核心算法思路:简单推理

数据结构:链表(题目);普通数组

技巧:

方法1:反转链表,可以抛弃比较麻烦。

方法2:将链表的值复制到数组中,然后从后向前遍历比较

  1. 141. 环形链表 - 力扣(LeetCode)

核心算法思路:简单推理;双指针

数据结构:HashSet;快慢指针

技巧:

**方法1:**存储节点到哈希表中,判断是否存在(while(head))

方法2: 可以想到快慢指针 ,但是比较难,算是一个背诵题,目前不打算背诵,如果后面用到再进行背诵。

  1. 21. 合并两个有序链表 - 力扣(LeetCode)

核心算法思路:简单推理

数据结构:List

技巧:

方法1:空间换时间:List<ListNode> 存储所有节点,利用List排序解决。

方法2:简单推理,依次比较两个链表的节点值,值小的向后移动。

循环条件:(while L1 !=null && L2!=null)

  1. 2. 两数相加 - 力扣(LeetCode)

核心算法思路:简单推理

数据结构:链表(题目)

技巧:

(1)设计进位标记值jin与哨兵节点。

(2)

while(L1!=null || L2!=null) 都为null才退出循环,提前为null的值设置为0;(这种循环更便捷)

while(L1!=null && L2!=null) 其中一个为null就退出,但是需要额外判断哪一个为null,需要写两个相同的循环。

  1. 19. 删除链表的倒数第 N 个结点 - 力扣(LeetCode)

核心算法思路:简单推理;空间换时间;双指针

数据结构:List;HashMap;快慢指针

技巧:

方法1(空间换时间):所有节点存储到List中,直接遍历即可

方法2(HashMap):所有节点存储到Map中,key为位置,value为节点。

方法2(双指针):让快指针前k+1个slow位置循环条件while(fast !=null)

  1. 24. 两两交换链表中的节点 - 力扣(LeetCode)

核心算法思路:简单推理;空间换时间

数据结构:List;

技巧:

方法1(空间换时间):所有节点存储到List中,在List中遍历交换,然后遍历输出。

方法2(简单推理):prev、curr、nex三个节点交换;

循环条件 while( curr !=null && curr.next !=null)

注意点:

(1)循环条件的确立,需要先判断迭代一次需要哪些节点参与,比如本题需要四个节点参与。

此外最好不要在循环前提前写next(哨兵节点除外),用现有的条件直接完成循环即可。

  1. 25. K 个一组翻转链表 - 力扣(LeetCode)

核心算法思路:简单推理(模拟);空间换时间

数据结构:List;

技巧:

方法1(空间换时间):所有节点存储到List中,在List中遍历倒序,然后遍历输出。

方法2(模拟):可以想到,但是有很多细节问题,当做一个背诵题

(1)reverse(startNode,endNode)翻转链表中,循环while(pre !=end),因为需要将最后一个节点也翻转,如果以curr作为循环,则会缺少一个节点。

(2)判断剩余节点是否够k个,提前写 ListNode tail = pre,然后循环k次,找到最后一个节点end。

注意点:

(1)前置节点pre 一般在while循环前写好,因为pre会在while中用到.

(2)pre永远不可能为null

(3)求k个node,需要从pre开始遍历k次.

  1. 138. 随机链表的复制 - 力扣(LeetCode)

核心算法思路:简单推理(模拟)

数据结构:HashMap

技巧:

两次遍历:

(1)第一次遍历创建新旧节点映射表,具体为HashMap<Node,Node> 旧节点新节点映射表

(2)第二次遍历:clone.next = map.get(curr.next);

  1. 148. 排序链表 - 力扣(LeetCode)

核心算法思路:空间换时间;归并排序

数据结构:数组;

技巧:

方法1:将节点的值存储为数组,然后排序,之后创建链表返回。

方法2:背诵题(自底向上归并排序):非常麻烦,目前不打算完成了。

  1. 23. 合并 K 个升序链表 - 力扣(LeetCode)

核心算法思路:空间换时间;模拟

数据结构:List<Node>;优先队列

技巧:

方法1(List):将所有节点全部放入List,按照val进行排序后,最后遍历返回。

方法2(模拟):顺序合并,依次合并,可以编写一个合并函数,返回合并的头结点。用ans来维护即可。

方法3(优先队列):优先队列维护每个list最小的节点

具体方法:

(1)将k个list的头节点全部入队。

(2)根据优先队列的性质,如果队列不为null,从中取出第一个节点,加入ans中。

(3)然后如果该节点的下一个节点存在则入队。

  1. 94. 二叉树的中序遍历 - 力扣(LeetCode)背诵题

核心算法思路:递归;迭代

数据结构:栈

技巧:

方法1(递归):采用根左右的递归方式返回即可。

方法2(栈):背诵题

具体方法:

(1)将某一个节点的所有左节点入栈,然后pop出栈中的元素,将其加入ans中。

(2)root = root.right继续重复。循环条件while( root != null && !stack.isEmpty() )

注意点:

(1)针对递归可以设计一个全局变量ans用来保存结果,因此递归函数可以是void.

  1. 104. 二叉树的最大深度 - 力扣(LeetCode)

核心算法思路:遍历(广度优先遍历)、深度优先遍历

数据结构:队列;递归

技巧:

方法一: 利用队列先进先出的特点,进行广度优先遍历,记录层数即可。ps:需要记录每层的节点数量,需要poll出全部的节点才能进入下一层。

**方法二:**深度优先遍历,每次都有两种走向,当访问到null,取最大值即可。

  1. 226. 翻转二叉树 - 力扣(LeetCode)

核心算法思路:遍历(广度优先遍历)、深度优先遍历

数据结构:队列;递归

技巧:

方法一: 利用队列先进先出的特点,进行广度优先遍历,针对每个节点交换其左右子节点即可。

**方法二:**深度优先遍历,每次都有两种走向,当访问到null,每次到一个节点交换即可。

  1. 101. 对称二叉树 - 力扣(LeetCode)

核心算法思路:遍历(广度优先遍历)、深度优先遍历

数据结构:队列;递归

技巧:

方法一(队列):同样还是队列,将节点的左节点、右节点同时poll出,然后比较,最后入队。while(!queue.isEmpty())

方法二(递归):判断u.left 与 v.right 以及其相反,其本身是否相等。

注意点:

(1)如果节点是null,还是能放入队列,具体效果如下:{1,2,null,2,3}

(2)null与int比较会报错,注意校验。

  1. 543. 二叉树的直径 - 力扣(LeetCode)

核心算法思路:深度优先遍历

数据结构:递归

技巧:

目的:设计一个全局遍历在递归过程中记录,求某个节点左子树的最大深度与右子树的最大深度+1.

递归的目标:求子树的深度(背诵题

  1. 102. 二叉树的层序遍历 - 力扣(LeetCode)

核心算法思路:广度优先搜索

数据结构:队列(背诵题)

  1. 98. 验证二叉搜索树 - 力扣(LeetCode)

核心算法思路 :中序遍历(两种方式也可以背诵一下)

数据结构:递归

技巧:

  • 中序遍历->升序遍历,之后一一校验即可。
  1. 230. 二叉搜索树中第 K 小的元素 - 力扣(LeetCode)

核心算法思路:中序遍历

数据结构:递归

技巧:

  • 中序遍历->升序遍历,之后返回第k个即可(比如用List辅助)
  1. 199. 二叉树的右视图 - 力扣(LeetCode)

核心算法思路:层序遍历

数据结构:队列

技巧:

(1)存储List<List<Node>>,返回每层的最后一个节点即可。

  1. 114. 二叉树展开为链表 - 力扣(LeetCode)

核心算法思路:前序遍历

数据结构:迭代,List

技巧:

(1)存储List<Node>,存储前序遍历的节点,最后构造单链表即可。

  1. 437. 路径总和 III - 力扣(LeetCode)

核心算法思路:深度优先遍历;前缀和

数据结构:递归;HashMap

技巧:

方法一(前缀和):

前序遍历(深度优先搜索)找到前面节点中符合条件的前缀和节点数量。

方法二(暴力遍历):

以每个节点为起点进行深度优先遍历,用全局变量ans记录答案。

可以先用一个list记录所有的节点,然后for循序进行遍历求解记录ans.

  1. 236. 二叉树的最近公共祖先 - 力扣(LeetCode)

核心算法思路:深度优先遍历;

数据结构:HashMap+HashSet

技巧:

(1)通过深度优先遍历再利用HashMap存储所有的父子节点,其中key是子节点,value是父节点。

(2)然后将其中一个节点的parent全部放入HashSet中,然后另一个节点通过HashMap找到最先存在的公共祖先节点。

注意点:

(1)java中hashmap中,如果get(key),如果这个key不存在会返回null.

  1. 124. 二叉树中的最大路径和 - 力扣(LeetCode)

核心算法思路:(后序遍历)深度优先遍历+动态规划;

数据结构:无

技巧:

(1)利用动态规划的思想,后序遍历得到子节点的最大值,才能确定当前节点(下一个节点)的值。

图论

  1. 200. 岛屿数量 - 力扣(LeetCode)

核心算法思路:深度优先遍历

数据结构:visited数组、dirs[][]方向数组

技巧:

(1)没有技巧直接双重for所有节点,判断该节点是否被访问&&是否等于1,如果是则岛屿数量+1。注意每次+1后都需要深度优先遍历更新visited.

注意点:

(1)可以不用visited,直接修改原数组为1即可。

  1. 994. 腐烂的橘子 - 力扣(LeetCode)

核心算法思路:广度优先遍历

数据结构:队列;visited;direction

技巧:

(1)利用队列实现广度优先遍历,最后判断感染的节点数与所有非0节点数量是否相同(直接遍历是否还存在新鲜橘子也可以,更加安全)

具体步骤:

(1)队列存储所有初始被感染节点的位置({x,y})。

(2)循环while(!queue.isEmpty()) 计算其所有节点的数量n并poll出来n次,进行感染,重新入队。注意利用visited保障不要重复入队(也可以不用visited,因为每次在入队前都需要先判断该位置是否为新鲜节点1,如果是则修改为感染节点2,然后将其入队。)。

(3)最后计算所有非0节点数量与所有被感染的数量是否相同。(直接遍历是否还存在新鲜橘子也可以,更加安全)

多种方法:

(1)队列存储位置方式:

  • 直接存储数组{x,y}
  • 存储数值(x*C + y),获取时x=value/C; y = value%C

(2)ans计算方式

  • 每次循环时ans+1,然后计算队列中的元素数,然后poll相应的次数,进行感染。
  • 利用HashMap<Integer,Integer>,key是节点(位置),value是时间。每次被感染后存储位置ncode;时间=map.get(node)+1.
  1. 207. 课程表 - 力扣(LeetCode)

核心算法思路:广度优先遍历;

数据结构:队列;HashMap

技巧:

拓扑排序**背诵题:**入度与出度

(1)第一步找到入度为0,也就是起始节点,将它们全部入队。

(2)循环(队列不等于null),找到该节点为入度的list(这里需要提前存储key是前置节点,value是一个list表示以key为前置节点的集合),将其入度-1,同时判断减1后,入队是否为0,如果为0则入队。

(3)每次出队一个便是课程数+1,最后返回判断。

回溯

  1. 46. 全排列 - 力扣(LeetCode)

核心算法思路:回溯

数据结构:visited[]

技巧:

(1)输出的长度确定:每个位置(固定的)有n种选择机会,但选择时需要用访问数组确认这个是否被选择过。

  1. 78. 子集 - 力扣(LeetCode)

核心算法思路:回溯

数据结构:无

技巧:

(1)输出长度不确定:输入每个位置(输入的组)有2种选择机会,要么选要么不选。

  1. 17. 电话号码的字母组合 - 力扣(LeetCode)

核心算法思路:回溯

数据结构:HashMap<String,String>

技巧:

(1)输出的长度确定:每个位置(输入的组)有对应3-4种选择机会,分别遍历。

  1. 39. 组合总和 - 力扣(LeetCode)

核心算法思路:回溯

数据结构:无

技巧:

(1)输出的长度确定:每个位置(输入的组)有对应3-4种选择机会,分别遍历。

  1. 22. 括号生成 - 力扣(LeetCode)

核心算法思路:回溯

数据结构:无

技巧:

(1)输出的长度确定:每个位置(输入的组)有对应2种选择机会,分别遍历.但是需要注意的是每次选择需要进行判断,如果当前"(" 不大于n,则可以放,")"不大于左括号可以放。

  1. 79. 单词搜索 - 力扣(LeetCode)

核心算法思路:回溯

数据结构:visited[][];dirctions[][]

技巧:

(1)**长度固定:**纯遍历+回溯,需要注意的是判断边界与是否访问。

  1. 131. 分割回文串 - 力扣(LeetCode)

核心算法思路:动态规划+回溯

数据结构:无

技巧:

这种长度完全不固定,并且有多种结果,需要for循环遍历每种可能。

(1)动态规划求出从i到j的字符串是否回文,进行记录。

从后往前遍历进行状态转移:

java 复制代码
for (int i = n - 1; i >= 0; --i) {
            for (int j = i + 1; j < n; ++j) {
                f[i][j] = (s.charAt(i) == s.charAt(j)) && f[i + 1][j - 1];
            }
       }

(2)回溯以每一个i为起点,j为终点的字符串,如果true则进行进行下一步。

  1. 51. N 皇后 - 力扣(LeetCode)

核心算法思路:回溯

数据结构:visited[][]

技巧:

(1)按行遍历,而不是按格子遍历;并且每次n种选择选择。

(2)难点就是对角线的判断。

(3)因为需要进行回退,所以需要将visited进行填充的时候,需要时+-1,而不是直接设置0/1来判断该位置是否能被选择。

java 复制代码
public static boolean areOnSameDiagonal(int n, int x1, int y1, int x2, int y2) {
        // 检查是否在同一正对角线(左上到右下):行列差相等
        boolean sameMainDiagonal = (x1 - y1 == x2 - y2);
        
        // 检查是否在同一反对角线(右上到左下):行列和相等
        boolean sameAntiDiagonal = (x1 + y1 == x2 + y2);
        
        return sameMainDiagonal || sameAntiDiagonal;
    }

二分查找

  1. 35. 搜索插入位置 - 力扣(LeetCode)

核心算法思路:二分查找

数据结构:无

技巧:

(1)按照模板遍历查询,大于目标值的时候,赋予ans=mid即可。

  1. 74. 搜索二维矩阵 - 力扣(LeetCode)

核心算法思路:二分查找

数据结构:无

技巧:

(1)方法一:找到目标行,二分查找即可。

(2)方法二:Z字搜索之前做过。

  1. 34. 在排序数组中查找元素的第一个和最后一个位置 - 力扣(LeetCode)

核心算法思路:二分查找

数据结构:无

技巧:

(1)分别寻找两侧边界,按照模板写即可,在找到一个值之后,根据寻找的是左边界还是右边界继续寻找,而不是结束。

  1. 33. 搜索旋转排序数组 - 力扣(LeetCode)

核心算法思路:二分查找

数据结构:无

技巧:

(1)先根据nums[n-1]确定mid落地,也就是落在前半段还是后半段。

(2)根据nums[0]与nums[n-1]进行条件判断,right与left移动。

  1. 153. 寻找旋转排序数组中的最小值 - 力扣(LeetCode)

核心算法思路:二分查找

数据结构:无

技巧:

核心目标:找到前半段,然后再找到第一个数。

(1)先根据nums[n-1]确定mid落地,也就是落在前半段还是后半段。

(2)根据nums[0]与nums[n-1]进行条件判断,right与left移动。

  1. 20. 有效的括号 - 力扣(LeetCode)

核心算法思路:逻辑推理

数据结构:栈

技巧:

能够优先处理当前最新的事务,正好与本题契合,每次需要判断左括号的类型,并且需要将之前没有处理的进行存储。

(1)每次遇到右括号,判断最新存储的左括号否匹配,匹配则继续,否则返回false.

  1. 394. 字符串解码 - 力扣(LeetCode)

核心算法思路:逻辑推理

数据结构:栈

技巧:

能够优先 处理当前最新的事务,正好与本题契合,每次需要判断左括号的类型,并且需要将之前没有处理的进行存储

(1)创建两个栈,一个栈存储数字,另一个栈存储string

(2)每次遇到"["代表新元素的到来,将前面的的num与str分别存储到两个栈中。

(3)当遇到"]"时代表需要将数字pop出,然后将合成的str循环拼接,之后pop字符串栈中最新的数据再进行拼接。

  1. 739. 每日温度 - 力扣(LeetCode)

核心算法思路:逻辑推理;空间换时间

数据结构:单调栈

技巧:

(1)方案1:单调栈 其实不算一种特意 处理的一种结构,它是根据逻辑推理自动具有了单调性。

(2)方案2: 根据输入范围 进行优化,温度范围是30-100,所以可以提取存储所有温度的位置,注意从后往前存储。

单调栈具体方案

(1)每次将遇到的值进行判断,如果大于栈顶元素,则pop,并将其ans置为当前位置。

(2)如果小于则进栈。

(3)最后将栈中剩余的元素的ans置0.

空间换时间具体方案

(1)从后往前存储各个温度的最考前的位置。

(2)遍历大于当前温度的位置。

注意点:

(1)单调栈存储的元素,可以是数组,arr[0]是温度值,arr[1]是位置.或者新建一个tems数组存储温度,而栈存储位置。每次判断就是t>tems[stack.peek()].

  1. 84. 柱状图中最大的矩形 - 力扣(LeetCode)

核心算法思路:逻辑推理

数据结构:单调栈(递增)

技巧:

暴力面积题目:

(1)固定高:向两边扩散,分别找到两侧第一个小于当前高的位置。

(2)固定起点:找到最矮的一个高,算面积。

优化暴力方案一:

(1)目的是找到该位置两侧首次小于当前高度的位置。

(2)两次遍历:从左向右遍历,每到一个位置就与栈顶元素比较,如果小于栈顶元素,则出栈,直到找到不小于该高度的值,并记录。从右向左遍历同理。

  1. 215. 数组中的第K个最大元素 - 力扣(LeetCode)

核心算法思路:分治(快排);空间换时间(看输入提示)

数据结构:堆;

技巧:

方案一:大根堆,但是时间复杂度为n*logn

方案二:桶排序

方案三:快排优化(背诵)

  1. 347. 前 K 个高频元素 - 力扣(LeetCode)

核心算法思路:逻辑推理

数据结构:大根堆

技巧:

方案一:大根堆,将元素放入大根堆中,然后poll出k次即可。大根堆创建如下

java 复制代码
  // int[] 的第一个元素代表数组的值,第二个元素代表了该值出现的次数
        PriorityQueue<int[]> queue = new PriorityQueue<int[]>(new Comparator<int[]>() {
            public int compare(int[] m, int[] n) {
                return m[1] - n[1];
            }
        });

方案二:逻辑推理:将hashmap转换为list,然后进行sort排序,输出k次即可。具体代码如下

java 复制代码
import java.util.*;

public class HashMapSortByValue {
    public static void main(String[] args) {
        HashMap<Integer, Integer> map = new HashMap<>();
        map.put(1, 50);
        map.put(2, 30);
        map.put(3, 80);
        map.put(4, 10);
        
        // 将entrySet转换为List
        List<Map.Entry<Integer, Integer>> list = new ArrayList<>(map.entrySet());
        
        // 按value升序排序
        Collections.sort(list, new Comparator<Map.Entry<Integer, Integer>>() {
            @Override
            public int compare(Map.Entry<Integer, Integer> o1, Map.Entry<Integer, Integer> o2) {
                return o1.getValue().compareTo(o2.getValue());
            }
        });
        
        // 转换为LinkedHashMap保持顺序 也可以不转化
        LinkedHashMap<Integer, Integer> sortedMap = new LinkedHashMap<>();
        for (Map.Entry<Integer, Integer> entry : list) {
            sortedMap.put(entry.getKey(), entry.getValue());
        }
        
        System.out.println("升序排序: " + sortedMap);
    }
}

贪心算法

  1. 121. 买卖股票的最佳时机 - 力扣(LeetCode)

核心算法思路:贪心(逆向思维)

数据结构:无

技巧:

(1)逆向思维:假设第i天卖出,而不是买入再遍历。

(2)维护当前的一个**最值(小),**根据这个最值进行下一个计算。

注意点:

(1)每个位置都要遍历,因为需要更新最值,确保每一步都能得到一个最值

  1. 55. 跳跃游戏 - 力扣(LeetCode)

核心算法思路:贪心

数据结构:无

技巧:

(1)维护当前的一个**最值(大),**根据这个最值进行下一个计算。

注意点:

(1)每个位置都要遍历,因为需要更新最值,确保每一步都能得到一个最值

  1. 45. 跳跃游戏 II - 力扣(LeetCode)

核心算法思路:贪心

数据结构:无

技巧:

(1)维护当前的一个**最值(大),**根据这个最值进行下一个计算(类似for循环中的i<max),但是这个max是会更新的,每次都取最值。

(2)在维护一个跳跃点(staM),每次到达该跳跃点则ans++,同时更新下一个跳跃点(curM)。

  1. 70. 爬楼梯 - 力扣(LeetCode)

核心算法思路:贪心

数据结构:hashMap

技巧:

目标:找到子串的结束位置,但这个位置是会改变的,所以维护一个end位置(最值),这个最值是当前最好的位置,但是到达该最好位置之前,真正的最值会不断更新优化。

动态规划

  1. 70. 爬楼梯 - 力扣(LeetCode)

核心算法思路:动态规划

数据结构:无

技巧:

状态定义:以i个为终点(必选)

初始状态:dp[0] dp[1](根据状态转移函数确定)

状态转移函数:dp[i] = dp[i-1] + dp[i-2]

输出:dp[n]

  1. 118. 杨辉三角 - 力扣(LeetCode)

核心算法思路:动态规划

数据结构:无

技巧:

唯一难点:就是处理数据,思路很简单。

  1. 198. 打家劫舍 - 力扣(LeetCode)

核心算法思路:动态规划

数据结构:无

技巧:

状态定义:以i个为终点(不必选)

初始状态:dp[0] dp[1](根据状态转移函数确定)

状态转移函数: dp[i] = Math.max(nums[i]+dp[i-2],dp[i-1]);

输出:dp[n]

  1. 279. 完全平方数 - 力扣(LeetCode)

核心算法思路:动态规划

数据结构:无

技巧:

任务:每个i肯定是dp[i-j*j] + 1(j*j)

状态定义:以i个为最少组成n的完全平方数(i为终点)

初始状态:dp[1] (根据状态转移函数确定)

状态转移函数:

复制代码
minn = Math.min(minn, f[i - j * j]);

输出:dp[n]

74.零钱兑换

核心算法思路:动态规划

数据结构:无

技巧:

任务:每个i肯定是dp[i-j] + 1(j)

状态定义:以i个为最少组成n的数量(i为终点)

初始状态:dp[0] (根据状态转移函数确定)

状态转移函数: dp[i] = Math.min(dp[i], dp[i - coins[j]] + 1);

输出:dp[n]

75.单词拆分

核心算法思路:动态规划

数据结构:无

技巧:

状态定义:以i为结尾的子串是否存在(i为终点)

初始状态:dp[0] (根据状态转移函数确定)空串肯定存在

状态转移函数:

if (dp[j] && wordDictSet.contains(s.substring(j, i))) --> dp[i] = true;

输出:dp[n]

注意点:

(1)快捷转化(List->set)

复制代码
Set<String> wordDictSet = new HashSet(wordDict);

76.最长增长子序列

核心算法思路:动态规划

数据结构:无

技巧:

状态定义:以i为结尾的长度

初始状态:dp[0] (根据状态转移函数确定)

状态转移函数:

复制代码
dp[i] = Math.max(dp[i], dp[j] + 1);

输出:dp[n]

时间复杂度:O(n*n)

**优化:**贪心+二分

辅助数组:p[i]代表长度为i+1,最后一个最小的数值

主循环遍历一次,每次遇到严格大于最后一个p[i]时,增加一个长度。

遇到严格小于p[i]时,二分查找替换第一个大于新值的位置。

注意点:

(1)贪心不容易理解:要让序列上升的更慢,才能找到更长的递增子序列。

(2)二分查找到第一个大于该新值的位置。

java 复制代码
                int l = 1, r = len, pos = 0,flag=0; 
                while (l <= r) {
                    int mid = (l + r) >> 1;
                    if(d[mid] == nums[i]){
                        flag=1;
                        break;
                    }
                    else if (d[mid] < nums[i]) {
                        pos = mid;
                        l = mid + 1;
                    } else {
                        r = mid - 1;
                    }
                }
                if(flag==0){
                    d[pos + 1] = nums[i];
                }

77.https://leetcode.cn/problems/maximum-product-subarray/description/?envType=study-plan-v2&envId=top-100-liked

核心算法思路:动态规划

数据结构:无

技巧:

状态定义:以i为结尾的长度

初始状态:dp[0] (根据状态转移函数确定)

状态转移函数:

复制代码
dp[i] = Math.max(dp[i], dp[j] + 1);

输出:dp[n]

时间复杂度:O(n*n)

**优化:**贪心+二分

辅助数组:p[i]代表长度为i+1,最后一个最小的数值

主循环遍历一次,每次遇到严格大于最后一个p[i]时,增加一个长度。

遇到严格小于p[i]时,二分查找替换第一个大于新值的位置。

注意点:

78.乘积最大子数组https://leetcode.cn/problems/maximum-product-subarray/solutions/250015/cheng-ji-zui-da-zi-shu-zu-by-leetcode-solution/?envType=study-plan-v2&envId=top-100-liked

核心算法思路:动态规划

数据结构:无

技巧:

状态定义:以i为结尾的最大最小值

初始状态:dp[0] (根据状态转移函数确定)

状态转移函数:

java 复制代码
maxF[i] = Math.max(maxF[i - 1] * nums[i], Math.max(nums[i], minF[i - 1] * nums[i]));
minF[i] = Math.min(minF[i - 1] * nums[i], Math.min(nums[i], maxF[i - 1] * nums[i]));

输出:for循环最大值

时间复杂度:O(n)

注意点:

(1)结果范围是32整数,所以中间过程用long比较好一些,最后结果(int) ans;

80.最长有效括号

核心算法思路:动态规划

数据结构:无

技巧:

状态定义:以i为结尾的最大最小值

初始状态:dp[0] (根据状态转移函数确定)

状态转移函数:

java 复制代码
maxF[i] = Math.max(maxF[i - 1] * nums[i], Math.max(nums[i], minF[i - 1] * nums[i]));
minF[i] = Math.min(minF[i - 1] * nums[i], Math.min(nums[i], maxF[i - 1] * nums[i]));

输出:for循环最大值

时间复杂度:O(n)

注意点:

(1)结果范围是32整数,所以中间过程用long比较好一些,最后结果(int) ans;

二维动态规划

  1. 不同路径

核心算法思路:动态规划

数据结构:无

技巧:

状态定义:以i,j位置为终点,有多少种方式(必选)

初始状态:dp[0] [1-j]与dp[1-i][0](根据状态转移函数确定;或者理解)

状态转移函数:dp[i][j] = dp[i-1][j] + dp[i][j-1]

输出:dp[i][j]

  1. 最小路径和

核心算法思路:动态规划

数据结构:无

技巧:

状态定义:以i,j位置为终点,最小值(必选)

初始状态:dp[0] [1-j]与dp[1-i][0](根据状态转移函数确定;或者理解)

状态转移函数:dp[i][j] = dp[i-1][j] + dp[i][j-1]

输出:dp[i][j]

  1. 最长回文子串

核心算法思路:动态规划

数据结构:无

技巧:

状态定义:以i,j位置为分别为起点与终点,是否为回文串

初始状态:dp[i] [i]=true(根据状态转移函数确定;或者理解)

状态转移函数:dp[i][j] = dp[i-1][j+1]

输出:dp[i][j]

遍历方式

(1)起点是从后往前遍历;

(2)终点是从起点往后遍历。

  1. 最长公共子序列

核心算法思路:动态规划

数据结构:无

技巧:

状态定义:以i,j位置为分别为两个字符串的位置

初始状态:dp[0 ][0]、dp[0][1]、dp[1][0](根据状态转移函数确定)

状态转移函数:dp[i][j] = Math.max( dp[i-1][j] ,dp[i][j-1] ) 或者 (dp[i-1][j-1]+1)

输出:dp[i][j]

技巧

  1. 只出现一次的数字

核心算法思路:技巧

数据结构:无

技巧:

异或运算,最后剩下的就是出现一次的。

java 复制代码
int single = 0;
for (int num : nums) {
  single ^= num;
}
  1. 多数元素

核心算法思路:双指针

数据结构:无

技巧:

方法1:双指针

两次遍历:第一次将所有0放到最左边,第二次将所有2放到最右边

以第一次遍历为例:

(1)left指针位置为第一个不为0的位置。

(2)right指针一直向右遍历。

方法2:三指针:l、mid、r

循环:while( mid <= r)

遍历一遍也可以,l指针指向放0的地方,r指针指向放2的地方。

  1. 下一个排序

核心算法思路:双指针

数据结构:无

技巧:

方法1:双指针

两次遍历:第一次将所有0放到最左边,第二次将所有2放到最右边

以第一次遍历为例:

(1)left指针位置为第一个不为0的位置。

(2)right指针一直向右遍历。

方法2:三指针:l、mid、r

循环:while( mid <= r)

遍历一遍也可以,l指针指向放0的地方,r指针指向放2的地方。

注意点:

(1)结果范围是32整数,所以中间过程用long比较好一些,最后结果(int) ans;

相关推荐
gihigo19981 小时前
MATLAB运动估计基本算法详解
开发语言·算法·matlab
hetao17338371 小时前
2026-02-09~02-12 hetao1733837 的刷题记录
c++·算法
ADDDDDD_Trouvaille2 小时前
2026.2.12——OJ72-74题
c++·算法
励ℳ2 小时前
机器学习-LASSO算法指南
人工智能·算法·机器学习
不光头强2 小时前
SpringBoot 开发第三天 学习内容
java·spring boot·学习
黎雁·泠崖2 小时前
【魔法森林冒险】12/14 场景系统:5大场景的任务串联
java·开发语言
Vic101012 小时前
算法D1-20260212:双指针专题
java·数据结构·算法