hello大家好!好久不见啦最近不是偷懒啊,事情确实太多了,又评奖学金又搞数学建模,还要一边写我的科研论文,这周事情才稍微少了一点。也刚刚好,开始上课了,课上也碰到了一些习题,之前写过,但内容上和我们之前讲过的有点联系,就想着再更新一篇博客,也是复习一下之前学过的内容。
OK,咱们话不多说,直接进入正题~
1、求两数之和
题目:
给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个 整数,并返回它们的数组下标。
你可以假设每种输入只会对应一个答案,并且你不能使用两次相同的元素。
你可以按任意顺序返回答案。
示例:
输入:nums = [2,7,11,15], target = 9 输出:[0,1] 解释:因为 nums[0] + nums[1] == 9 ,返回 [0, 1] 。
分析:
首先,这题让我们操作数组的数字以及其对应的下标,这就上说明可能要将两者关联起来,解题会更加的便捷,这也就不难想到使用哈希表或者二元数对的方式。但是这两者之间其实还存在着一点区别。
如果是二元数对,就是将两个数组的数结合,记录他们的数组下标的同时,判断他们是否满足题意,也就是我们的暴力枚举法。这个简单来说就是来固定前一个数,然后遍历数组的其他数,看看是否有满足条件的数。
这里放一张leetcode的大佬做的一张图片,相信大家可以很清晰直观的感受到
但是其实大家也发现了,这种“爆破法”(就是暴力破解法)的缺点是很明显的,他需要走过内外层多个循环,运气最差的时候,他的时间复杂度就是O(n^2)。所以我更推荐大家使用哈希表的解法,遍历一次数组即可。
使用哈希表解法的时候,其实我们就是将上述的思路转换,可以这样思考:固定数字的时候,我们要找另一个数字加起来等于26。而这个另一个数字我们其实可以通过计算得到:26-2=24我们希望有没有什么工具可以快速告诉我们,剩下的数字里有没有24这个数呢?
事实上,哈希表就是一个可以快速查找是否包含一个数字的容器。哈希表又分为Map和Set(这个前面有提到过,不清楚的小伙伴可以看我之前的文章),由于我们这道题最终要返回的是数字的下标,因此我们不仅要记出现过的数字,还要记出现过的数字的下标。使用Map,key存储出现过的数字,value存储出现过的数字的下标。
(这位大佬的图做的确实很漂亮,我就再借用一下,不献丑再自己画啦)
通过哈希表,我们就可以在一次循环的前提下,找到我们的目标数,这大大降低了我们的时间复杂度,使得代码更加简洁,看起来也更牛逼(bushi)
代码:
以下是枚举法和哈希法的代码与难疑点处详细注释,我是用python写的,大家可以参考一下~
#枚举法
class Solution:
def twoSum(self, nums: List[int], target: int) -> List[int]:
n = len(nums)
# 枚举每个元素
for i in range(n):
# 固定元素nums[i], 找到其之后能和它配对的元素nums[j]
for j in range(i + 1, n):
if nums[i] + nums[j] == target:
return [i, j]
return [-1, -1]
# 哈希法
class Solution:
def twoSum(self, nums: List[int], target: int) -> List[int]:
m = {} # 创立哈希表
for i, num in enumerate(nums):
# enumerate() 函数用于将一个可遍历的数据对象(如列表、元组或字符串)组合为一个索引序
# 列,同时列出数据和数据下标,一般用在 for 循环当中。
# i代表索引,num代表数值
if target - num in m:
# 如果当前数nums[i]的搭档存在,直接返回索引对
return [i, m[target - num]]
m[num] = i # 否则将当前数作为一个潜在目标存储到哈希表中
return [-1, -1] # 不存在满足条件的数对返回-1索引
2、无重复字符的最长子串
题目:
给定一个字符串 s
,请你找出其中不含有重复字符的 最长子串的长度。
示例:
输入: s = "abcabcbb" 输出: 3 解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。
分析:
这道题目,不长,但是也不是很好理解,网络上面有很多种解法,什么动态规划、暴力循环。我这次给大家分享一个我觉得非常巧妙的办法,双指针滑动窗口法。
就是通过两个指针一左一右,构造出一个可以滑动的窗口,通过指针与列表的对比,来实现窗口不断滑动,并且保证窗口内无重复数字并且最长。
具体过程大家可以看看这张图,可以结合我下面给的代码一起看,就能明白他的巧妙之处,通过指针与数组数值之间的转换比较,从而实现目标
代码:
def length_of_longest_substring(s: str) -> int:
# 创建一个大小为128的列表来存储字符的最近出现位置,128是因为ASCII字符的大小是128
# 我们初始化为列表的值0,因为我们还没有遇到任何字符
char_map = [0] * 128
# 初始化答案和滑动窗口的左边界
ans = 0
i = 0
# 开始遍历字符串,j是滑动窗口的右边界
for j in range(len(s)):
# 找到字符 s[j] 上次出现的位置(从1开始存储,因此要减去1)
# 如果上次出现的位置在当前窗口内(即 i <= 上次出现的位置),
# 则更新左边界 i 为上次出现的位置的下一位
i = max(i, char_map[ord(s[j])])
# 更新当前字符的出现位置为j+1,因为我们从1开始计数(即下一次这个字符可能出现在窗口之外)
char_map[ord(s[j])] = j + 1
# 更新答案:右边界 j - 左边界 i + 1 是当前窗口的长度
ans = max(ans, j - i + 1)
# 返回最长的子串长度
return ans
#列表长度设置到128,实际长度127,因为ASCII就到127。ord(s[j])
是 Python 内置函数 ord()
的调用,它的作用是将字符转换为对应的 ASCII 值。这就通过字符串的ASCII值的转换,把字符对应到列表的具体位置上,让列表的数值大小表示字符出现的次数。
#注意一点,j的值在一次循环中是不变的,加一的是列表中的值
3、一些小tip
本期文章算是对前面知识的一点复习(其实是感觉太久没更新了但又不知道写啥好。。。)
机器学习的内容等我写完论文再整理一下,应该会再接着更新,后面可能还会再加一点数据库的内容,毕竟我学的专业是大数据嘛
再说一点就是,leetcode上面哪些题目的代码前缀大家可以不用去管他,他其实可以简单理解为他给他们平台的测试的代码写了一个统一的接口,里面已经包括了题目所有需要传入的内容,所以我们只要在他的那个模块里面写我们自己的代码就可以了,所有你要用到的题目的值都已经给你写好传进来了,你要用的时候直接加自己的变量声明就可以啦~
好啦,本期文章就到这里啦,十月份我的博客更新应该就会和之前一样了,这段时间确实忙不过来了QAQ
祝大家学习、生活愉快!拜拜~~~