Leetcode相关记录笔记

此篇博客用于记录我刷Leetcode题目的相关方法(草稿写法,若要具体了解参考其他博客)


hot100


1.两数之和:采用字典法解法。

暴力破解,时间复杂度较高

O(n)算法, a = target - nums[x],求出a的索引即可,返回a,x索引,注意a=x索引时需continue

字典法:字典用于提升查找速度
1。首先遍历nums列表,目标值-遍历值不在字典中,记录该值和值的索引
2。发现目标值-遍历值在字典中,说明前面已经出现过,返回前一个值的索引和当前值的索引


2.两数相加

基本的链表操作


3.无重复的最大子串:采用滑动窗口解法。

  滑动窗口的应用场景有几个特点:

  • 需要输出或比较的结果在原数据结构中是连续排列的;
  • 每次窗口滑动时,只需观察窗口两端元素的变化,无论窗口多长,每次只操作两个头尾元素,当用到的窗口比较长时,可以显著减少操作次数;
  • 窗口内元素的整体性比较强,窗口滑动可以只通过操作头尾两个位置的变化实现,但对比结果时往往要用到窗口中所有元素。

4.寻找两个正序数组的中位数:采用最小K值法

  此题的最小k意味着第k个最小的数,对于一个数组,可能长度是奇数也有可能是偶数,那么 k = l e n g t h / / 2 k = length // 2 k=length//2或者 k = l e n g t h / / 2 + 1 k = length //2 +1 k=length//2+1,对于这题,假设AB两个数组,我们需要比较第 k / / 2 − 1 k//2 -1 k//21数,谁小就把到这为止的元素剔除,如何重新计算k值,假设剔除了两个数(该数一定比k小),那么k应该等于k-2,直到k=1时,输出两者min值。


5.最长回文子串:采用动态规划法中心扩散法

什么样的问题可以考虑使用动态规划解决呢?

  如果一个问题,可以把所有可能的答案穷举出来,并且穷举出来后,发现存在重叠子问题,就可以考虑使用动态规划。比如一些求最值的场景,如最长递增子序列、最小编辑距离、背包问题、凑零钱问题等等,都是动态规划的经典应用场景。

中心扩散法:对于一个字符串,中心可能是奇的(比如aba,中心为a),也有可能是偶的(比如abba,中心为bb)。我们可以遍历这个字符串,以奇为中心和以偶为中心分别扩展。

     left1, right1 = self.expandAroundCenter(s, i, i)            # 中心是奇数 比如aba b是中心
     left2, right2 = self.expandAroundCenter(s, i, i + 1)        # 中心是偶数 比如abba bb是中心

求出以该中心扩散的最大回文串索引即可。


10.正则表达式匹配:采用动态规划法。【困难题】

此题有必要记录下思路和算法过程:

给你一个字符串s和一个字符规律p,请你来实现一个支持 '.'和 ‘✳’ 的正则表达式匹配。
‘.’ 匹配任意单个字符
’ ✳‘匹配零个或多个前面的那一个元素
所谓匹配,是要涵盖整个字符串s的,而不是部分字符串。

解题思路:
在这里插入图片描述
AC代码[写法较拙劣,上述思路实现]:

class Solution:
    def isMatch(self, s: str, p: str) -> bool:
        m, n = len(s), len(p)
        dp = [[False] * (n + 1) for _ in range(m + 1)]  # 初始化dp数组,因为需要考虑两边空串情况,所以行数和列数分别加1
        # 含空字串处理
        dp[0][0] = True  # 空字串为True
        for i in range(1, n + 1):       # dp行与列
            if p[i-1] == '*':           # dp第一行,空串遇见'*',返回j-2状态
                dp[0][i] = dp[0][i-2]
            else:                       # 其余置false
                dp[0][i] = False
        for i in range(1, m + 1):       # 列置false
            dp[i][0] = False
        for i in range(1, m + 1):       # 遍历,dp变化参照上述规则
            for j in range(1, n + 1):
                if s[i - 1] == p[j - 1] or p[j - 1] == '.':
                    dp[i][j] = dp[i - 1][j - 1]
                elif p[j - 1] == '*':
                    if s[i - 1] == p[j - 2] or p[j - 2] == '.':  # 注意先判断相等的情况(细节)
                        dp[i][j] = (dp[i][j - 2] or dp[i][j - 1] or dp[i - 1][j])
                    else:
                        dp[i][j] = dp[i][j - 2]
        return dp[m][n]

11.盛最多水的容器:采用双指针。

O(n)算法,控制左右两个指针,指针往容器高度较低的移动


15.三数之和:采用循环+双指针。

先将数组排好序,开始遍历数组,设两个指针,left = i + 1,right = len(nums) - 1, 当left < right时候,进行三元组判断,三种情况,大于0,小于0,等于0.

大于0: right左移 。 小于0,left右移, 当等于0时,需要考虑去重。

三元组等于0 时候去重的核心代码:

while left < right and nums[left + 1] == nums[left]:  # 防止重复
      left = left + 1
while left < right and nums[right - 1] == nums[right]:  # 防止重复
      right = right - 1

17.电话号码的字符组合:采用回溯法。

此题考查组合,暴力求解不仅麻烦而且时间复杂度较高,采用回溯法(递归求解)

首先构造数据字典,回溯核心代码模板:

        def backtrack(temp):
            if len(path) == len(digits):
                res.append("".join(path[:]))
                return
            for i in KEY[temp[0]]:
                path.append(i)
                backtrack(temp[1:])
                path.pop()

Python中也可以考虑列表推导式进行求解:

for num in digits:
    ans = [pre + suf for pre in ans for suf in KEY[num]]

19.删除链表的倒数第N个节点:采用快慢指针。

链表基本操作,先讲常用方法:可以先求出链表长度,然后求正序目标节点,工作指针定位即可。

此题用快慢指针方法更好,因为求倒数第N个节点,定义两个指针,fast、slow。两个指针都先指向头节点,fast先移动N步,然后fast和slow再一起移动,当fast移动到空节点(也就是末尾后一个时),此时slow所处的位置就是目标节点的位置。


20.有效的括号:采用栈。

括号匹配题:采用栈的思想即可。遇见左括号直接入栈,右括号需要与出栈字符比较。


21.合并两个有序链表:采用递归/迭代法。

对一个问题,用递归求解的时候,要想清楚几点问题:

1、这个问题的子问题是什么?(在两个链表中找到最小值)

2、当前层要干什么事情?(找最小值)

3、递归出口。(其中一个链表为空)

此题采用迭代的话,直接工作指针遍历即可。


22.括号生成:采用DFS。

1.DFS + 剪枝 大大缩短时间。DFS三个参数

def dfs(str,left,right)
    """
    :param str: 当前字符串
    :param left: 左边剩余可用括号数
    :param right 右边剩余可用括号数
    :return:
    """

递归出口:left == 0 and right ==0添加到res中,当left > right时,进行剪枝操作


23.合并K个升序链表:采用优先级队列/分治法。

这道题,首先顺序合并也是可以的,遍历,一个一个合并即可[21题思路]。不过耗时会很长。

介绍下优先级队列解法,创建最小堆,因为用的Python,调用Python的heapq库快速遍历创建,然后创建完毕后,依次取顶部加入到新链表中即可。

再介绍下分治法:分而治之,链表两两合并。计算数组长度

l1 = merge(lists,left,mid)
l2 = merge(lists,mid+1,right)
mergeTwolists(l1,l2) #此函数就是两个链表的合并算法

31.下一个排列:此题需要一定思想,双排列法

本题题意:返回恰好比nums排列后大的数,如果nums排列后是最大的,就返回最小的数

example:[1, 5, 8, 7, 6, 2, 1] -> [1, 6, 1, 2, 5, 7, 8]

那么这题需要的一个思想就是:找数交换翻转

找数:从尾部开始找,找到第一个比前面的数比后面小的数,[1, 5, 8, 7, 6, 2, 1]

找到这个数后,然后再从尾部找到第一个比此数大的数,[1, 5, 8, 7, 6, 2, 1]

交换:[1, 6, 8, 7, 5, 2, 1]

翻转:[1, 6, 8, 7, 5, 2, 1] —>[1, 6, 1, 2, 5, 7, 8]


33.搜索旋转排序素组:采用二分法

评论中有一句话方便理解这种做法:将数组一分为二,其中一定有一个是有序的,另一个可能是有序,也能是部分有序。此时有序部分用二分法查找。无序部分再一分为二,其中一个一定有序,另一个可能有序,可能无序。就这样循环.

官方代码中,指出如果num[0] < =num[mid] 说明右边有序,否则左边有序,注意:nums无重复。

官方代码:

class Solution:
    def search(self, nums: List[int], target: int) -> int:
        if not nums:
            return -1
        l, r = 0, len(nums) - 1
        while l <= r:
            mid = (l + r) // 2
            if nums[mid] == target:
                return mid
            if nums[0] <= nums[mid]:  # 说明左边有序
                if nums[0] <= target < nums[mid]:  # target在里面,然后二分求解
                    r = mid - 1
                else:
                    l = mid + 1  # 目标值不在,l=mid+1,跳到右边
            else:  # 说明右边有序
                if nums[mid] < target <= nums[len(nums) - 1]:  # target在里面,然后二分求解
                    l = mid + 1
                else:
                    r = mid - 1  # 目标值不在,r=mid-1,跳到左边
        return -1

34.在排序数组中查找元素的第一个和最后一个位置:采用二分法

此题限制时间复杂读O(logn),对于这种非递减数组,首先就应该采用二分查找法。

题意:给你一个按照非递减顺序排列的整数数组 nums,和一个目标值 target。请你找出给定目标值在数组中的开始位置和结束位置。

对于开始位置,我们可以用二分去查找,通过条件nums[mid]>=target,可以找到第一个大于等于 target的索引值,对于结束位置,我们通过条件nums[mid]>target,可以找到第一个大于target的索引值,该索引值-1可能就是我们需要的结束位置了。

所以得到这两个值后,我们需要判断

if leftIndex == len(nums):  # 说明target值大于数组所有值
   return [-1, -1]
if rightIndex < leftIndex:  # 说明数组内无target值
   return [-1, -1]
return [leftIndex,rightIndex]

39.组合总和:采用回溯法

对于这类寻找所有可行解的题,我们都可以尝试用「搜索回溯」的方法来解决。

以candidates = [2,3,6,7],target = 7 为例。

考虑 candidates[0] = 2 我们只需要找到组合总和为 7- 2 =5的所有总和

考虑 candidates[1] = 3 我们只需要找到组合总和为 7- 3 =4的所有总和

这样我们可以画出一颗递归树

图片来源于:leetcode39.解析

可以看到红色叶子节点所在路径是我们需要的组合,但是由于[2,2,3]会和[3,2,2]重复,所以需要进行简单处理,只需遍历的时候设置下一轮搜索的七点即可。

当然此题也可以做剪枝处理,我们可以先对candidates数组进行排序处理,然后递归时候判断 target - candidates[i] < 0,则剪枝。


46.全排列:采用回溯法

寻找所有可行解,一般都用回溯法求解

回溯模板:

result = []
def backtrack(路径, 选择列表):
    if 满足结束条件:
        result.add(路径)
        return
    
    for 选择 in 选择列表:
        做选择
        backtrack(路径, 选择列表)
        撤销选择

图片来源leetcode46.解析

表示从左往右填到第first个位置,当前排列为output


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值