力扣学习计划——数据结构与算法基础笔记

数据结构

  1. 数组

    题目:136. 只出现一次的数字

    题目:169. 多数元素

    题目:15. 三数之和(难点)

    这个方法就是我们常说的「双指针」,当我们需要枚举数组中的两个元素时,如果我们发现随着第一个元素的递增,第二个元素是递减的,那么就可以使用双指针的方法。

    如果需要和上一次枚举的数不相同,这个技巧在排列组合问题上面也用到了:

    if first > 0 and nums[first] == nums[first - 1]:
    	continue
    

    即相同的话就直接判断下一个元素

    题目:75. 颜色分类(荷兰国旗问题,经典)

    题目:56. 合并区间

    题目:706. 设计哈希映射

    题目:119. 杨辉三角 II

    题目:48. 旋转图像

    主对角线翻转+水平翻转,或者,y=x翻转+竖直翻转,答案是前者,代码更简洁

    题目:59. 螺旋矩阵 II

    题目:240. 搜索二维矩阵 II

    题目:435. 无重叠区间

    class Solution:
        def eraseOverlapIntervals(self, intervals: List[List[int]]) -> int:
            if not intervals:
                return 0
            
            intervals.sort(key=lambda x: x[1])
            n = len(intervals)
            right = intervals[0][1]
            ans = 1
    
            for i in range(1, n):
                if intervals[i][0] >= right:
                    ans += 1
                    right = intervals[i][1]
            
            return n - ans
    

    贪心,按照区间左端点排序,符合要求的添加,则剩余的都是被删除的

    题目:334. 递增的三元子序列

    左右考虑;贪心,背方法

    题目:238. 除自身以外数组的乘积

    前缀和+后缀和,智商碾压

    题目:560. 和为 K 的子数组

  2. 字符串

    题目:415. 字符串相加

    题目:409. 最长回文串

    题目:290. 单词规律

    题目:763. 划分字母区间

    题目:49. 字母异位词分组

    题目:43. 字符串相乘

    题目:187. 重复的DNA序列

    题目:5. 最长回文子串

    马拉车还是没听懂,去再听一遍左神的,自己写一下代码

  3. 链表

    题目:2. 两数相加

    题目:142. 环形链表 II

    题目:160. 相交链表(互相指向头节点)

    题目:82. 删除排序链表中的重复元素 II

    哑节点+快慢指针实现不占用额外空间

    题目:24. 两两交换链表中的节点

    题目:707. 设计链表

    题目:25. K 个一组翻转链表

    题目:143. 重排链表(经典题)

  4. 栈/队列

    题目:155. 最小栈

    据说有不使用额外空间(辅助栈)的做法

    题目:1249. 移除无效的括号

    题目:1823. 找出游戏的获胜者约瑟夫环

  5. 题目:108. 将有序数组转换为二叉搜索树

    题目:105. 从前序与中序遍历序列构造二叉树

    题目:103. 二叉树的锯齿形层序遍历

    题目:199. 二叉树的右视图

    也可以用dfs

    题目:113. 路径总和 II

    (非计划)题目:437. 路径总和 III

    使用前缀和

    题目:450. 删除二叉搜索树中的节点(经典题)

    题目:230. 二叉搜索树中第K小的元素

    题目:173. 二叉搜索树迭代器

    题目:236. 二叉树的最近公共祖先

    题目:297. 二叉树的序列化与反序列化

  6. 题目:997. 找到小镇的法官

    题目:1557. 可以到达所有点的最少点数目

    题目:841. 钥匙和房间

  7. 优先队列(堆)

    题目:215. 数组中的第K个最大元素

    题目:347. 前 K 个高频元素

    题目:451. 根据字符出现频率排序

    题目:973. 最接近原点的 K 个点

算法

  1. 二分

    题目:34. 在排序数组中查找元素的第一个和最后一个位置

    题目:33. 搜索旋转排序数组(这道题很麻烦,答案的方法很好)

    题目:74. 搜索二维矩阵(2次二分更普遍)

    题目:153. 寻找旋转排序数组中的最小值

    题目:162. 寻找峰值(辅助函数处理边界,条件简化)

  2. 双指针

    题目:82. 删除排序链表中的重复元素 II

    题解优雅,一次遍历

    题目:15. 三数之和

    K神的解法更好理解一些,二刷还是不会(OvO)

    题目:844. 比较含退格的字符串

    简单题,但是考察coding能力

    题目:986. 区间列表的交集

    题目:11. 盛最多水的容器

  3. 滑动窗口

    题目:438. 找到字符串中所有字母异位词(重点)

    这道题和之前的一个很像,可以使用diff来记录不同的个数,从而只需要判断进和出的情况

    题目:713. 乘积小于K的子数组

    题目:209. 长度最小的子数组

  4. dfs/bfs

    题目:200. 岛屿数量

    题目:547. 省份数量

    作为图的dfs/bfs的入门题来看待,是无向无环图

    题目:117. 填充每个节点的下一个右侧节点指针 II

    题目:572. 另一棵树的子树(这道简单题不简单)

    题目:1091. 二进制矩阵中的最短路径

    • dfs:判断路径/结果是否存在
    • bfs:寻找最短/最优路径

    这里给出一个题解,对dfs和bfs分析的比较好,以及为什么bfs的visited可以全局使用,https://leetcode-cn.com/problems/shortest-path-in-binary-matrix/solution/bfszui-duan-lu-jing-wen-ti-bfsdfsde-si-k-ngc5/

    题目:130. 被围绕的区域

    逆向思维

    题目:797. 所有可能的路径

    有向无环图,和上面的一起记忆

  5. 递归/回溯

    题目:78. 子集

    题目:90. 子集 II

    子集问题:

    1. 回溯法,增加条件为path的长度,长度满足后就增加一个有效答案,返回
    2. 二进制掩码

    含重复值:排序数组,去重处理(上一个没选择,且当前和上一个相同,则跳过)

    (非计划)题目:46. 全排列

    题目:47. 全排列 II

    排列问题:

    1. 回溯法,最直接最经典的解法,但是不易处理存在重复元素的情况
    2. 交换法,以k神为例

    含重复值:

    1. (对于回溯法)排序,同时利用字典和元素判断,这里判断比较复杂不易理解
    2. (对于交换法)只需要用到字典处理

    回溯法:

    具体地,我们只要在递归函数中设定一个规则,保证在填每一个空位的时候重复字符只会被填入一次。具体地,我们首先对原字符串排序,保证相同的字符都相邻,在递归函数中,我们限制每次填入的字符一定是这个字符所在重复字符集合中「从左往右第一个未被填入的字符」,即如下的判断条件:

    if j in vis or (j-1 not in vis and j > 0 and nums[j] == nums[j-1]):
    	continue
    

    这里还不能直接将元素哈希,只能将元素对应的下标哈希,再进行判断:

    class Solution:
        def permuteUnique(self, nums: List[int]) -> List[List[int]]:
            def backtrack(i):
                # 满足条件,这里不能用输入变量了
                if len(path) == len(nums):
                    res.append(path[:])
                    return
                
                # 要考虑所有的元素,而不是只考虑当前位置后面的元素,所以用for
                for j in range(len(nums)):
                    # 上一个选择和本次重复
                    # 这里为什么是或?
    
                    # j已经选择过 或者 j-1没选过但是j和j-1对应元素相等
                    if j in vis or (j-1 not in vis and j > 0 and nums[j] == nums[j-1]):
                        continue
                    
                    vis.add(j)
                    path.append(nums[j])
                    backtrack(j)
                    path.pop()
                    vis.remove(j)
            
            res = []
            path = []
            vis = set()
            nums.sort()
            backtrack(0)
            return res
    

    交换法:

    class Solution:
        def permuteUnique(self, nums: List[int]) -> List[List[int]]:
            # 交换法
            def backtrack(i):
                if i == len(nums)-1:
                    res.append(nums[:])
                    return
                dct = set()
                for j in range(i, len(nums)):
                    # 先去重
                    if nums[j] in dct:
                        continue
                    dct.add(nums[j])
                    nums[i], nums[j] = nums[j], nums[i]
                    backtrack(i+1)
                    nums[i], nums[j] = nums[j], nums[i]
            
            res = []
            backtrack(0)
            return res
    

    交换法只需要进行一次简单的字典判断,有几个区别:

    1. 字典初始化在回溯中,不是全局的概念;因为只需要判断本次回溯的位置处是否重复
    2. 字典元素为数组元素,不是下标索引
    3. 不需要引入额外的path,直接将原数组操作并添加至答案

    题目:39. 组合总和

    题目:40. 组合总和 II

    组合的题感觉更像子集一样,只不过不是全部子集,是有条件的子集

    1. 当前元素选/不选(适用于可重复使用同一元素),双分支
    2. 使用for循环验证后面的全部

    题目:17. 电话号码的字母组合

    题目:22. 括号生成(留意一下,回溯条件可以是多个)

    题目:79. 单词搜索(经典)

  6. 动态规划

    (非计划)题目:198. 打家劫舍

    题目:213. 打家劫舍 II

    2种情况下的最大值:如果偷窃了第一间房屋,则不能偷窃最后一间房屋,因此偷窃房屋的范围是第一间房屋到最后第二间房屋;如果偷窃了最后一间房屋,则不能偷窃第一间房屋,因此偷窃房屋的范围是第二间房屋到最后一间房屋。

    题目:55. 跳跃游戏

    典型的贪心,记住方法;和字节暑期实习笔试题很像(题目就叫“字节跳动”)

    题目:45. 跳跃游戏 II(继续Ⅰ的贪心策略)

    题目:62. 不同路径

    题目:5. 最长回文子串(放在这里有些奇怪,最优解马拉车success)

    题目:413. 等差数列划分

    和某道题很像,我记得那道题不能只考虑相邻3个元素

    题目:91. 解码方法

    当前情况等于2种情况相加,但是这2种情况都是要判断才可以

    题目:139. 单词拆分

    双循环类型dp,第二次循环判断之前某个状态是否满足,即在满足某个小情况(枚举的值那一段在字典中)下,才能求取当前值

    题目:300. 最长递增子序列

    和上面的题相似,第二次循环中判断每次的情况,即在满足某个小情况(末尾值大于枚举的值)下,才能求取当前值

    题目:673. 最长递增子序列的个数

    比较难,在上面的基础上,小循环和大循环都更新数量dp数组

    题目:1143. 最长公共子序列

    公共子序列问题,必须记忆!

    题目:583. 两个字符串的删除操作(问题转化)

    题目:72. 编辑距离(困难题,至今不会)

    题目:322. 零钱兑换

    依旧和139300一个情况,双循环,内循环的枚举要和外侧枚举值产生关系

    题目:343. 整数拆分

    割绳子问题,dp和贪心都要会

  7. 位运算

    题目:201. 数字范围按位与(最长公共前缀)

  8. 其他

    题目:384. 打乱数组

    题目:202. 快乐数

    题目:[149. 直线上最多的点数](

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值