代码随想录算法训练营第二十八天 | Leetcode随机抽题检测

Leetcode随机抽题检测--使用题库:Leetcode热题100

  • 1 两数之和
    • 未看解答自己编写的青春版
    • 重点
    • 题解的代码
    • 日后再次复习重新写
  • 49 字母异位词分组
    • 未看解答自己编写的青春版
    • 重点
    • 题解的代码
    • 日后再次复习重新写
  • 128 最长连续序列
    • 未看解答自己编写的青春版
    • 重点
      • 关于 left 和 right 的疑问?
      • 为什么只需要更新 num-left 和 num+right ?
    • 题解的代码
    • 日后再次复习重新写
  • 283 移动零
  • 11 盛最多水的容器
    • 未看解答自己编写的青春版
    • 重点
    • 题解的代码
    • 日后再次复习重新写
  • 15 三数之和
  • 42 接雨水
    • 未看解答自己编写的青春版
    • 重点
    • 自己理解后的代码,这部分代码都是按我自己的风格编写的,会和题解有一些出入,比如大部分题解中,位置 i 的左右最大高度,初始值就是height[i],考虑了自己,但我没有考虑自己,初始值是0
      • 要注意这种双指针法的编写逻辑,和上一题,"盛最多水的容器",相似。如何确定是移动 left 还是 right ? 看当前 mexleft 和 maxright 哪个小。因为我们在计算当前位置所能接的雨水时,首先要判断 min(maxleft , maxright) ,那么当前应该计算的雨水位置,就是小值的那一侧,假如maxleft小,那么不管后面的循环中,maxright再如何变化,在当前left处,min(maxleft , maxright) 的值不会再变化了(注意,当前left位置的maxleft,是由该位置之前的元素值决定的),所以,如果maxleft小,那么就计算left处的雨水,然后更新maxleft,然后移动left。right同理。
      • 本题要让0入栈,不让0入栈是不自然的,然后计算当前能承接水的体积的方式是:当前最外层遍历的元素是 i , 叫做right ,pop()出来的元素是 mid (这个值也是下标) , 当前单调栈stack内 (pop后的单调栈,单调栈的栈顶元素 stack[-1] ),此时的栈顶元素 stack[-1],叫做 left (这个值也是下标) 。计算以当前位置 mid 为底,所能承载的水的体积是:(min(nums[left] , nums[right]) - nums[mid]) * (right-left-1) 。
    • 两篇题解的地址
    • 日后再次复习重新写
  • 84 柱状图中最大的矩形
    • 未看解答自己编写的青春版
    • 重点
    • 题解的代码
    • 日后再次复习重新写
  • 3 无重复字符的最长子串
    • 未看解答自己编写的青春版
    • 重点
    • 题解的代码
    • 日后再次复习重新写
  • 438 找到字符串中所有字母异位词
    • 未看解答自己编写的青春版
    • 重点
    • 题解的代码
    • 日后再次复习重新写
  • 560 和为 K 的子数组
    • 未看解答自己编写的青春版
    • 重点
    • 题解的代码
    • 日后再次复习重新写
  • 239 滑动窗口最大值
    • 未看解答自己编写的青春版
    • 重点
    • 题解的代码
    • 日后再次复习重新写
  • 76 最小覆盖子串
    • 未看解答自己编写的青春版
    • 重点
    • 题解的代码
    • 日后再次复习重新写
  • 53 最大子数组和
    • 未看解答自己编写的青春版
    • 重点
    • 题解的代码
    • 日后再次复习重新写
  • 56 合并区间
    • 未看解答自己编写的青春版
    • 重点
    • 题解的代码
    • 日后再次复习重新写
  • 189 轮转数组
    • 未看解答自己编写的青春版
    • 重点
    • 题解的代码
    • 日后再次复习重新写
  • 238 除自身以外数组的乘积
    • 未看解答自己编写的青春版
    • 重点
    • 题解的代码
    • 日后再次复习重新写
  • 41 缺失的第一个正数
    • 未看解答自己编写的青春版
    • 重点
    • 题解的代码
    • 日后再次复习重新写
  • 73 矩阵置零
    • 未看解答自己编写的青春版
    • 重点
    • 题解的代码
    • 日后再次复习重新写
  • 54 螺旋矩阵
    • 未看解答自己编写的青春版
    • 重点
    • 题解的代码
    • 日后再次复习重新写
  • 48 旋转图像
    • 未看解答自己编写的青春版
    • 重点
    • 题解的代码
    • 日后再次复习重新写
  • 240 搜索二维矩阵 II
    • 未看解答自己编写的青春版
    • 重点
      • 还是这种思路厉害,时间复杂度是O(n),我的方法严格来说,还是O(n^2)
    • 题解的代码
    • 日后再次复习重新写

1 两数之和

未看解答自己编写的青春版

哈希哈希,觉得这题应该不用去看题解了。

class Solution:def twoSum(self, nums: List[int], target: int) -> List[int]:table = {}n = len(nums)for i in range(n):if len(table) == 0 :table[nums[i]] = table.get(nums[i],0) + ielse :temp = target - nums[i]if temp in table :return [i,table[temp]]else :table[nums[i]] = table.get(nums[i],0) + ireturn None

重点

题解的代码

日后再次复习重新写

49 字母异位词分组

未看解答自己编写的青春版

但是时间上仅打败5%,是哪里出了问题?好像也没法加速了啊

class Solution:def groupAnagrams(self, strs: List[str]) -> List[List[str]]:if len(strs) == 1 :return [strs]strs_sorted = [sorted(i) for i in strs]n = len(strs)used = [False]*nres = []for i in range(n):if used[i] == False :level = [strs[i]]used[i] = Truefor j in range(i+1,n):if used[j] == False :# 这句比较,引起了时间花费比字典直接索引大很多if strs_sorted[i]==strs_sorted[j] :level.append(strs[j])used[j] = Trueres.append(level)return res

重点

为什么用了字典,就比我的used数组快呢?

我目前认为是:if strs_sorted[i]==strs_sorted[j] :

这句比较,引起了时间花费比字典直接索引大很多

题解的代码

class Solution:def groupAnagrams(self, strs: List[str]) -> List[List[str]]:dic = {}for s in strs:keys = "".join(sorted(s))if keys not in dic:dic[keys] = [s]else:dic[keys].append(s)return list(dic.values())

日后再次复习重新写

128 最长连续序列

未看解答自己编写的青春版

没思路。错误的代码:

class Solution:def longestConsecutive(self, nums: List[int]) -> int:maxlength = 1table = {}for i in nums :if i not in table :table[i] = 1if i-1 in table :table[i] += table[i-1]table[i-1] = table[i]if i+1 in table :table[i] += table[i+1]table[i+1] = table[i]maxlength = max(maxlength,table[i])print(table)return maxlength

重点

在这里插入图片描述

关于 left 和 right 的疑问?

代码中 if num not in hash_dict就保证了 left 和 right 不会有交叠 进来的这个值没有在字典中出现过,意味着目前还没有任何区间包含这个值,如果 left 和 right 出现重叠了的,就意味着这个值之前已经在区间中了,矛盾了。

不可能有交叉,不可能一个没出现过的数,既属于left又属于right

为什么只需要更新 num-left 和 num+right ?

因为我们在判断时,只看当前数的前一个数和后一个数,所以在更新时,只需要更新边界,不在序列中的新数如果要使用已有的最大长度,一定会碰到边界,碰不到边界,那么值一定就是 1 。

题解的代码

利用 get 函数,可以少写很多判断的风格:

class Solution(object):def longestConsecutive(self, nums):hash_dict = dict()max_length = 0for num in nums:if num not in hash_dict:left = hash_dict.get(num - 1, 0)right = hash_dict.get(num + 1, 0)cur_length = 1 + left + rightif cur_length > max_length:max_length = cur_lengthhash_dict[num] = cur_lengthhash_dict[num - left] = cur_lengthhash_dict[num + right] = cur_lengthreturn max_length

判断 i-1 和 i+1 的风格:

class Solution {
public:int longestConsecutive(vector<int>& nums) {unordered_map<int,int> consecutive;int maxlong=0;for(int x:nums){if(consecutive.count(x)) continue;int nowlong=1;if(consecutive.count(x+1)&&consecutive.count(x-1)){nowlong+=consecutive[x+1]+consecutive[x-1];consecutive[x-consecutive[x-1]]=nowlong;consecutive[x+consecutive[x+1]]=nowlong;}else{if(consecutive.count(x-1)){nowlong+=consecutive[x-1];consecutive[x-consecutive[x-1]]=nowlong;}if(consecutive.count(x+1)){nowlong+=consecutive[x+1];consecutive[x+consecutive[x+1]]=nowlong;}}consecutive[x]=nowlong;maxlong=max(maxlong,nowlong);}return maxlong;}
};

看了解答后,自己又写了一遍的风格:

class Solution:def longestConsecutive(self, nums: List[int]) -> int:maxlength = 0table = {}for i in nums :if i not in table :if i-1 in table and i+1 in table :table[i] = table[i-1] + table[i+1] + 1table[i-table[i-1]] = table[i] table[i+table[i+1]] = table[i]                elif i-1 in table :table[i] = table[i-1] + 1table[i-table[i-1]] = table[i]elif i+1 in table :table[i] = table[i+1] + 1table[i+table[i+1]] = table[i]else :table[i] = 1maxlength = max(maxlength,table[i])return maxlength

日后再次复习重新写

283 移动零

双指针,过

11 盛最多水的容器

未看解答自己编写的青春版

没思路,知道是双指针,但是不清楚怎么移动。

看了解答后明白了,让高小的移动。

重点

对O(n)的算法写一下自己的理解,一开始两个指针一个指向开头一个指向结尾,此时容器的底是最大的,接下来随着指针向内移动,会造成容器的底变小,在这种情况下想要让容器盛水变多,就只有在容器的高上下功夫。 那我们该如何决策哪个指针移动呢?我们能够发现不管是左指针向右移动一位,还是右指针向左移动一位,容器的底都是一样的,都比原来减少了 1。这种情况下我们想要让指针移动后的容器面积增大,就要使移动后的容器的高尽量大,所以我们选择指针所指的高较小的那个指针进行移动,这样我们就保留了容器较高的那条边,放弃了较小的那条边,以获得有更高的边的机会。

class Solution:def maxArea(self, height: List[int]) -> int:maxv = 0n = len(height)left = 0right = n-1while left < right :value = min(height[left],height[right])*(right-left)maxv = max(maxv,value)if height[left] >= height[right] :right -= 1else :left += 1return maxv

题解的代码

日后再次复习重新写

15 三数之和

过。

42 接雨水

未看解答自己编写的青春版

不会。

重点

这道题是属于双指针法的题目,但是还是不清楚怎么用双指针,根本原因是对如何计算雨水,不清楚。

诸如类似计算雨水的题目,有两种计算方式,横向和竖向。
在这里插入图片描述

本题,我将会学习四种解法,暴力,DP,双指针,单调栈。其中,前三种方法属于一类,DP和双指针其实都是对暴力的优化,在本题上并没有体现出自身方法的优势。单调栈的使用,本题是一个很经典的题目。

需要注意的是,因为前三种方法,我们要做的都是依次遍历数组,所以使用的计算方式是:按列计算雨水,这也是符合直觉的。

单调栈因为涉及弹入弹出,是按行计算雨水。

自己理解后的代码,这部分代码都是按我自己的风格编写的,会和题解有一些出入,比如大部分题解中,位置 i 的左右最大高度,初始值就是height[i],考虑了自己,但我没有考虑自己,初始值是0

暴力法:超时

class Solution:def trap(self, height: List[int]) -> int:total = 0n = len(height)# 第一个和最后一个不可能接雨水for i in range(1,n-1):maxleft = 0maxright = 0j = i-1while j > -1 :maxleft = max(maxleft,height[j])j -= 1j = i+1while j < n :maxright = max(maxright,height[j])j += 1# 由于我前面编写的风格,要将结果和0做个判断,只保留非负数total += max(min(maxleft,maxright)-height[i],0)return total

DP:是不是可以提前保存好每个位置的左右最大值?其实就是对最大高度进行记忆化。

class Solution:def trap(self, height: List[int]) -> int:total = 0n = len(height)dp = [[0]*2 for _ in range(n)]# dp[i][0] : 第i个位置的maxleft ; dp[i][1] : 第i个位置的maxrightfor i in range(1,n):dp[i][0] = max(dp[i-1][0],height[i-1])for i in range(n-2,-1,-1): dp[i][1] = max(dp[i+1][1],height[i+1])# 第一个和最后一个不可能接雨水for i in range(1,n-1):maxleft = dp[i][0]maxright = dp[i][1] # 由于我前面编写的风格,要将结果和0做个判断,只保留非负数total += max(min(maxleft,maxright)-height[i],0)return total

双指针:这里存在了一点冲突,我参考的两篇题解,在这里有一些分歧,卡哥的双指针法,其实就是完全手册里的二维DP,都是用两个一维数组,去储存每个位置的maxleft和maxright。
但是手册中的双指针思路如下:
在这里插入图片描述

要注意这种双指针法的编写逻辑,和上一题,“盛最多水的容器”,相似。如何确定是移动 left 还是 right ? 看当前 mexleft 和 maxright 哪个小。因为我们在计算当前位置所能接的雨水时,首先要判断 min(maxleft , maxright) ,那么当前应该计算的雨水位置,就是小值的那一侧,假如maxleft小,那么不管后面的循环中,maxright再如何变化,在当前left处,min(maxleft , maxright) 的值不会再变化了(注意,当前left位置的maxleft,是由该位置之前的元素值决定的),所以,如果maxleft小,那么就计算left处的雨水,然后更新maxleft,然后移动left。right同理。

class Solution:def trap(self, height: List[int]) -> int:total = 0n = len(height)maxleft = height[0]maxright = height[n-1]left = 1right = n-2while left <= right :if maxleft <= maxright :# 由于我前面编写的风格,要将结果和0做个判断,只保留非负数total += max(maxleft-height[left],0)# 注意逻辑,注意编写顺序,这里的更新要写在left更新之前# 因为在我的编写逻辑中,maxleft和maxright是不考虑当前值的# 如果先更新left了,maxleft就会漏掉当前left位置的值maxleft = max(maxleft,height[left])left += 1else :  # 由于我前面编写的风格,要将结果和0做个判断,只保留非负数total += max(maxright-height[right],0)# 注意逻辑,注意编写顺序,这里的更新要写在right更新之前maxright = max(maxright,height[right])right -= 1return total

单调栈:

本题要让0入栈,不让0入栈是不自然的,然后计算当前能承接水的体积的方式是:当前最外层遍历的元素是 i , 叫做right ,pop()出来的元素是 mid (这个值也是下标) , 当前单调栈stack内 (pop后的单调栈,单调栈的栈顶元素 stack[-1] ),此时的栈顶元素 stack[-1],叫做 left (这个值也是下标) 。计算以当前位置 mid 为底,所能承载的水的体积是:(min(nums[left] , nums[right]) - nums[mid]) * (right-left-1) 。

本题还有一个要注意的是:如果两个数值相等,怎么处理?照常入栈,顶替掉前一个!

class Solution:def trap(self, height: List[int]) -> int:total = 0n = len(height)# 栈里放的是索引stack = [0]for i in range(1,n):  while stack != [] and height[i] >= height[stack[-1]] :mid = stack.pop()if stack != []:left = stack[-1]total += (min(height[left],height[i])-height[mid])*(i-left-1)stack.append(i)return total

两篇题解的地址

接雨水问题的超完全手册

代码随想录

日后再次复习重新写

84 柱状图中最大的矩形

未看解答自己编写的青春版

重点

题解的代码

日后再次复习重新写

3 无重复字符的最长子串

未看解答自己编写的青春版

一开始想用字典的,但是发现不行,因为要求子序列,要求必须连续,就想到了用双向队列。

from collections import deque
class Solution:def lengthOfLongestSubstring(self, s: str) -> int:dq = deque()maxcount = 0count = 0for i in s :if i in dq :while dq[0] != i :dq.popleft()count -= 1dq.popleft()dq.append(i)else :dq.append(i)count += 1maxcount = max(count,maxcount)return maxcount

重点

注意:这道题是属于滑动窗口的题目。

用 set 要比我用 deque 来的更巧妙一些。因为本题所要求为去重的子串,而 set 自带去重特性。

class Solution:def lengthOfLongestSubstring(self, s: str) -> int:ans = left = 0window = set()  # 维护从下标 left 到下标 right 的字符for right, c in enumerate(s):while c in window:  # 加入 c 后,窗口内会有重复元素window.remove(s[left])left += 1  # 缩小窗口window.add(c)ans = max(ans, right - left + 1)  # 更新窗口长度最大值return ans

下面一版的代码解读:(下面这份代码不好理解,写的很简略,我觉得初学者还是先理解上面基于双向队列或者set的做法)

i是截至j,以j为最后一个元素的最长不重复子串的起始位置,即索引范围是[i,j]的子串是以索引j为最后一个元素的最长子串。 当索引从j-1增加到j时,原来的子串[i,j-1]新增了一个元素变为[i,j],需要判断j是否与[i,j-1]中元素有重复。所以if s[j] in st:是判断s[j]相同元素上次出现的位置,和i孰大孰小。如果i大,说明[i,j-1]中没有与s[j]相同的元素,起始位置仍取i;如果i小,则在[i,j-1]中有了与s[j]相同的元素,所以起始位置变为st[s[j]]+1,即[st[sj]+1,j]。而省略掉的else部分,由于s[j]是第一次出现所以前面必然没有重复的,仍然用i作为起始位置即可。 后面的ans=max(ans,j-i+1)中,括号中前者ans是前j-1个元素最长子串长度,j-i+1是以s[j]结尾的最长子串长度,两者(最长子串要么不包括j,要么包括j)取最大即可更新ans,遍历所有i后得到整个输入的最长子串长度。

class Solution:def lengthOfLongestSubstring(self, s):""":type s: str:rtype: int"""st = {}i, ans = 0, 0for j in range(len(s)):if s[j] in st:i = max(st[s[j]], i)ans = max(ans, j - i + 1)st[s[j]] = j + 1return ans;

题解的代码

日后再次复习重新写

集合方法复写:

class Solution:def lengthOfLongestSubstring(self, s: str) -> int:maxcount = 0window = set()left = 0for index,value in enumerate(s):while value in window :window.remove(s[left])left += 1window.add(value)maxcount = max(maxcount,index-left+1)return maxcount

438 找到字符串中所有字母异位词

未看解答自己编写的青春版

排序方法,时间复杂度过高。

class Solution:def findAnagrams(self, s: str, p: str) -> List[int]:n = len(s)m = len(p)res = []target = sorted(p)for i in range(0,n-m+1):temp = s[i:i+m]if sorted(temp) == target :res.append(i)return res

用字典记录:注意特殊情况的处理( m > n),注意初始化,不要放在循环里,放在外面初始化。注意收获结果的判断逻辑的放置位置,要放在循环逻辑的最后,所以在初始化后就应该立刻进行一次判断。

class Solution:def findAnagrams(self, s: str, p: str) -> List[int]:table = {}m = len(p)n = len(s)if m > n :return []left = 0right = 0res = []for i in p :table[i] = table.get(i,0)+1temp = {}while right < m :temp[s[right]] = temp.get(s[right],0)+1right += 1if temp == table :res.append(left)for i in range(right,n) :temp[s[left]]-=1if temp[s[left]] == 0 :del temp[s[left]]left += 1temp[s[right]] = temp.get(s[right],0)+1right += 1if temp == table :res.append(left)#print(temp)return res

根本不需要,上面,这么复杂的,初始化,以及 left right 走来走去,精简版:

class Solution:def findAnagrams(self, s: str, p: str) -> List[int]:table = {}m = len(p)n = len(s)if m > n :return []res = []temp = {}for i in range(m) :table[p[i]] = table.get(p[i],0)+1temp[s[i]] = temp.get(s[i],0)+1     if temp == table :res.append(0)for i in range(m,n) :temp[s[i-m]]-=1if temp[s[i-m]] == 0 :del temp[s[i-m]]temp[s[i]] = temp.get(s[i],0)+1 if temp == table :res.append(i-m+1)#print(temp)return res

用哈希,注意到题目说明:两个字符串均只包含小写字母。

class Solution:def findAnagrams(self, s: str, p: str) -> List[int]:if len(s) < len(p):return []Num = []n = len(p)A = [0] * 26for i in range(n):A[ord(p[i]) - ord('a')] += 1A[ord(s[i]) - ord('a')] -= 1if A == [0] * 26:Num.append(0)for i in range(n, len(s)):A[ord(s[i]) - ord('a')] -= 1A[ord(s[i - n]) - ord('a')] += 1if A == [0] * 26:Num.append(i + 1 - n)return Num

数组哈希方法,复写:

class Solution:def findAnagrams(self, s: str, p: str) -> List[int]:m = len(p)n = len(s)if m > n :return []res = []A = [0]*26for i in range(m) :A[ord(p[i])-ord('a')] += 1A[ord(s[i])-ord('a')] -= 1if A == [0]*26 :res.append(0)for i in range(m,n) :A[ord(s[i])-ord('a')] -= 1A[ord(s[i-m])-ord('a')] += 1if A == [0]*26 :res.append(i-m+1)#print(temp)return res

重点

注意:这道题是属于滑动窗口的题目。

题解的代码

日后再次复习重新写

560 和为 K 的子数组

未看解答自己编写的青春版

不会。

重点

为什么这题不可以用双指针/滑动窗口:因为nums[i]可以小于0,也就是说右指针i向后移1位不能保证区间会增大,左指针j向后移1位也不能保证区间和会减小。给定j,i的位置没有二段性,vice versa。

我也想到了不可以用双指针。

暴力:超时
在这里插入图片描述

class Solution:def subarraySum(self, nums: List[int], k: int) -> int:count = 0n = len(nums)for i in range(n):sums = 0for j in range(i,n):sums += nums[j]if sums == k :count += 1return count

前缀和 + 哈希表优化:
在这里插入图片描述
在这里插入图片描述

这方法,如果不是见过,真的很难想到啊。

题解的代码

class Solution:def subarraySum(self, nums: List[int], k: int) -> int:table = {}table[0] = 1total = 0count = 0n = len(nums)for i in range(n):total += nums[i]if total-k in table :count += table[total-k]table[total] = table.get(total,0)+1return count

日后再次复习重新写

239 滑动窗口最大值

未看解答自己编写的青春版

单调队列,我觉得这题考察的就是,如何根据题意,模拟出答案更新的过程,然后发现其中有什么规律,自然而然地去设计一个这样的单调队列。只保留大的数。

以及弹出元素和加入元素的逻辑,判断条件。

from collections import deque
class DQ:def __init__(self):self.dq = deque()def push(self,val):if len(self.dq)==0 :self.dq.append(val)else :while self.dq and self.dq[-1] < val :self.dq.pop()self.dq.append(val)def top(self):return self.dq[0]def popleft(self):self.dq.popleft()class Solution:def maxSlidingWindow(self, nums: List[int], k: int) -> List[int]:dq = DQ()n = len(nums)for i in range(k):dq.push(nums[i])res = [dq.top()]for i in range(k,n):remove = i-kif nums[remove]==dq.top():dq.popleft()dq.push(nums[i])res.append(dq.top())return res

重点

当然我觉得我能顺利地解决这道题,还是之前已经学过了的缘故。
滑动窗口最大值

题解的代码

日后再次复习重新写

76 最小覆盖子串

未看解答自己编写的青春版

又没写出来,这道题的巧妙不仅仅在于,用一个字典或者哈希数组,去存储每个字符,而是用字符串 t 的长度,作为一个指示变量,来指导我们什么时候该上前推进了,而不是每次加入一个元素时,都要进行比较。

错误代码:只让 t 中有的元素,进入字典,这样做完全是自己给自己添麻烦,后续的移动也是一团糟。

class Solution:def minWindow(self, s: str, t: str) -> str:n = len(s)m = len(t)if n < m :return ''table = {}for i in t :table[i] = table.get(i,0)+1left = 0rightmin = infleftmin = 0while s[left] not in table :left += 1right = leftflag = Falsewhile right < n :if s[right] in table :table[s[right]] -= 1if self.isright(table):flag = Truebreakright += 1if not flag :return ''if right-left < rightmin-leftmin :rightmin,leftmin = right,lefttable[s[left]] += 1   left += 1while left < n and right < n :while s[left] not in table :left += 1if self.isright(table):if right-left < rightmin-leftmin :rightmin,leftmin = right,lefttable[s[left]] += 1   left += 1else :right += 1flag = Falsewhile right < n :if s[right] in table :table[s[right]] -= 1if self.isright(table):flag = Truebreakright += 1if flag :if right-left < rightmin-leftmin :rightmin,leftmin = right,lefttable[s[left]] += 1   left += 1else :breakreturn s[leftmin:rightmin+1]def isright(self,table):for i in table :if table[i] > 0 :return Falsereturn True

重点

暂时不想写了,这道题,后面再复写。看是看懂了。

题解的代码

from collections import defaultdict
class Solution:def minWindow(self, s: str, t: str) -> str:char_t = defaultdict(int)nt = len(t)ns = len(s)for i in range(nt):char_t[t[i]] += 1leftmin = 0rightmin = 2*nsleft = 0right = 0while right < ns :# 这里必须是大于0,因为对于不存在的元素,defaultdict的返回值也是0if char_t[s[right]] > 0 :char_t[s[right]] -= 1nt -= 1else :char_t[s[right]] -= 1 # 不是t的值也要记录if nt == 0 :# 这里必须是小于0,因为对于不存在的元素,defaultdict的返回值也是0while char_t[s[left]] < 0 :char_t[s[left]] += 1left += 1if rightmin - leftmin > right-left :rightmin , leftmin = right , left nt = 1# 要理解下面两行代码所代表的逻辑,如果进入判断nt==0,那么一定是在前面,新加入了一个# 属于t的字符,并且t中所有字符目前的值是0,而上面的while循环,只能寻找小于0的字符,# 当while循环终止时,找到的是最左边的为0的字符,那么此时,如果我们还想让left移动,# 那么就要在right的右边,找一个和该最左边字符相等的字符,所以要让char_t[s[left]] += 1# 同时也记得让left移动一格,这样在后面找到该字符时,向左移动的while循环是合法的char_t[s[left]] += 1left += 1right += 1if rightmin == 2* ns :return ''else :return s[leftmin:rightmin+1]

日后再次复习重新写

复写:

class Solution:def minWindow(self, s: str, t: str) -> str:table = {}m = len(t)n = len(s)for i in t:table[i] = table.get(i,0)+1left = 0right = 0rightmin = 2*nleftmin = 0while right < n :if table.get(s[right],0) > 0 :table[s[right]] -= 1m -= 1else :# 这里处理的是两种情况,一是s[right]不在t中# 二是s[right]是t中的值,但是前面已经出现过,将其值减为0或者更小了,# 这里再次出现,也要减一,举例:s='ABBBC' t='ABC',其中的后两个Btable[s[right]] = table.get(s[right],0)-1if m == 0 :while table[s[left]] < 0 :table[s[left]]+=1left+=1if rightmin-leftmin > right-left:rightmin,leftmin = right,leftm = 1table[s[left]]+=1left+=1right += 1if rightmin == 2*n :return ''else :return s[leftmin:rightmin+1]

53 最大子数组和

未看解答自己编写的青春版

这份代码其实有点牵强,单独写了,如果数组中全是负数的情况。

class Solution:def maxSubArray(self, nums):maxcount = max(nums)if maxcount < 0 :return maxcountcount = 0for i in nums:count += iif count < 0:count = 0maxcount = max(maxcount,count)return maxcount

重点

贪心 or DP 。

题解的代码

标准代码,不自主赋值,全部用数组中自己的值:

class Solution:def maxSubArray(self, nums: List[int]) -> int:result = nums[0]maxsum = nums[0]n = len(nums)for i in range(1,n):if maxsum <= 0 :    maxsum = nums[i]else :maxsum += nums[i]result = max(maxsum,result)return result

日后再次复习重新写

56 合并区间

未看解答自己编写的青春版

class Solution:def merge(self, intervals: List[List[int]]) -> List[List[int]]:intervals.sort()res = []n = len(intervals)temp = intervals[0]for i in range(1,n):if temp[1] >= intervals[i][0] :# 这里要注意,因为我只是先按照第一个坐标进行排序,所以前一个区间的右边界# 不一定比后一个区间的右边界小temp[1] = max(temp[1],intervals[i][1])else :res.append(temp)temp = intervals[i]# 这里注意,根据前面的逻辑,不管最后一层循环走的是if还是else,都没有将最后的结果放入结果集中res.append(temp)return res

重点

注意两个点就好了,都写在了代码的注释中。

题解的代码

日后再次复习重新写

189 轮转数组

未看解答自己编写的青春版

能想到两种方法,第一种是先反转整个列表,再分别反转两段。第二种是,一次移动一个,移动K次。

错误代码:

错误原因:像这种要求在原数组上修改的,那么就不能用原数组进行一些赋值操作,就必须在上面操作,赋值之后,虽然新变量名字和原数组一样,但是这也是两个变量了,所以,本题,首先不能用python自带的切片,因为倒序切片没法部分倒序,一处理就是整个序列倒序,但这是不行的,会涉及申请新空间问题 (temp = nums[0:k]) ; 那么就必须自主编写倒序函数,也不能编写为 self.reverse 的格式,因为这意味着必须要有返回值,而承接返回值就意味着赋值,就不是原数组修改了!

要学习下面这种在函数内定义函数的方式。

class Solution:def rotate(self, nums: List[int], k: int) -> None:"""Do not return anything, modify nums in-place instead."""n = len(nums)k = k % nnums = nums[::-1]nums = self.reverse(0,k-1,nums)nums = self.reverse(k,n-1,nums)def reverse(self,i,j,nums):while i < j :nums[i],nums[j] = nums[j],nums[i]i += 1j -= 1return nums

一次向右循环移动一个数字,也是只需要O(1),但是时间复杂度上是O(k*n),这里不写这种方法的代码了。

重点

还有一种方法,就是申请一个K的空间,先把后K个数存起来。

class Solution:def rotate(self, nums: List[int], k: int) -> None:"""Do not return anything, modify nums in-place instead."""n = len(nums)k = k % ntemp = nums[n-k:n]if n > k :for i in range(n-k-1,-1,-1):nums[i+k] = nums[i]nums[:k] = temp

题解的代码

日后再次复习重新写

238 除自身以外数组的乘积

未看解答自己编写的青春版

时间复杂度和空间复杂度均是 O(n) 的方法。

class Solution:def productExceptSelf(self, nums: List[int]) -> List[int]:n = len(nums)res = [0]*nleft = [0]*nright = [0]*ntemp = 1for i in range(1,n):temp = temp * nums[i-1]left[i] = temptemp = 1for i in range(n-2,-1,-1):temp = temp*nums[i+1]right[i] = tempfor i in range(n):if i == 0 :res[i] = right[i]elif i == n-1 :res[i] = left[i]else :res[i] = left[i] * right[i]return res

进阶,空间复杂度如何优化到 O(1) ,同时保持时间复杂度还是 O(n) ?

这种方法想不到。

重点

空间复杂度 O(1) 方法:

左边走一遍,右边走一遍,其中规律,自己试一试就能发现,我一开始总想着双指针,才一直想不出来。

class Solution:def productExceptSelf(self, nums: List[int]) -> List[int]:n = len(nums)res = [1]*n        left = 1right = 1for i in range(n):res[i] = leftleft = left * nums[i]      for i in range(n-1,-1,-1):res[i] = res[i] * rightright = right * nums[i]               return res

题解的代码

日后再次复习重新写

41 缺失的第一个正数

未看解答自己编写的青春版

不会,hard题没有思路。

间复杂度为 O(n) 并且只使用常数级别额外空间的解决方案,想不出来。

重点

主要思路:

第一遍把所有负数换成INT_MAX。第二遍,将出现的数的绝对值对应的位置的数置为负数。第三遍,不是负数就输出位置。

两个要点:

1、取abs的操作很巧妙,这样就可以避免前面的操作,覆盖了后面的值。

2、抓住本题的循环不变量:只要该元素出现过了,那么该位置的就必须是负数,所以必须加abs

题解的代码

class Solution:def firstMissingPositive(self, nums: List[int]) -> int:n = len(nums)for i in range(n):if nums[i] <= 0 :nums[i] = inffor i in range(n) :# 这里取abs的操作也很巧妙,这样就可以避免前面的操作,覆盖了后面的值temp = abs(nums[i])if temp > 0 and temp < n+1 :# 这里的abs太关键了,防止的就是多次操作的情况# 抓住本题的循环不变量:只要该元素出现过了,那么该位置的就必须是负数,所以必须加absnums[temp-1] = -abs(nums[temp-1])for i in range(n) :if nums[i] > 0 :return i+1return n+1

日后再次复习重新写

73 矩阵置零

未看解答自己编写的青春版

O(1) 方法,用 inf 对“原本0”进行标记 :(感觉我这有些投机取巧)

class Solution:def setZeroes(self, matrix: List[List[int]]) -> None:"""Do not return anything, modify matrix in-place instead."""m = len(matrix)n = len(matrix[0])for i in range(m):for j in range(n):if matrix[i][j] == 0 :matrix[i][j] = inffor i in range(m):for j in range(n):if matrix[i][j] == inf :for k in range(n) :if matrix[i][k] != inf :matrix[i][k] = 0for k in range(m) :if matrix[k][j] != inf :matrix[k][j] = 0for i in range(m):for j in range(n):if matrix[i][j] == inf :matrix[i][j] = 0

O(mn)方法

必须用深拷贝,copy.deepcopy() ,我真是涨知识了。

import copy
class Solution:def setZeroes(self, matrix: List[List[int]]) -> None:"""Do not return anything, modify matrix in-place instead."""copy_matrix = copy.deepcopy(matrix)m = len(matrix)n = len(matrix[0])for i in range(m):for j in range(n):if matrix[i][j] == 0 and copy_matrix[i][j] == 0 :for k in range(n) :matrix[i][k] = 0for k in range(m) :matrix[k][j] = 0

O(m+n)方法:

class Solution:def setZeroes(self, matrix: List[List[int]]) -> None:"""Do not return anything, modify matrix in-place instead."""m = len(matrix)n = len(matrix[0])exist_m = [False]*mexist_n = [False]*nfor i in range(m):for j in range(n):if matrix[i][j] == 0 :exist_m[i] = Trueexist_n[j] = Truefor i in range(m):for j in range(n):if matrix[i][j] == 0 :# 注意这里,对行操作还是对列操作# 逻辑和前面给exist_m(n)是相反的if exist_n[j] :for k in range(m) :# 这是对一列进行操作matrix[k][j] = 0if exist_m[i] :for k in range(n) :# 这是对一行进行操作matrix[i][k] = 0

重点

本题要求用三种不同的空间复杂度的方法。
在这里插入图片描述
今天做了这道题真是涨知识了,原来在前面二叉树和回溯算法章节,我一直使用的,list.copy() 方法,一直都是浅拷贝,是共享内存的,卡哥一直用的 list[::-1] ,但是我经过试验,一维列表的.copy(),拷贝后数据是不随原数据的更改而更改的,但是多维列表会更改!

多维就要用深拷贝,copy.deepcopy() 。

随便找的一份讲解

题解的代码

看了一下评论的解答,为我愚蠢的矩阵赋值方式感到抓狂。

思路:
在这里插入图片描述
O(1) :

class Solution:def setZeroes(self, matrix: List[List[int]]) -> None:"""Do not return anything, modify matrix in-place instead."""m = len(matrix)n = len(matrix[0])row = Falsecol = False'''错误逻辑for i in range(0,m):for j in range(0,n):# 注意看,这段逻辑是错误的,后面的修改逻辑可能导致 col row 的值发生错误# 而让每个位置都判断左右两边,是没有道理的if matrix[i][0] == 0 :col = Trueif matrix[0][j] == 0 :row = Trueif matrix[i][j] == 0 :matrix[0][j] = 0matrix[i][0] = 0'''for i in range(0,m):for j in range(0,n):# 正确逻辑,先判断此位置是不是0if matrix[i][j] == 0 :if j == 0 :col = Trueif i == 0 :row = Truematrix[0][j] = 0matrix[i][0] = 0for i in range(1,m):for j in range(1,n):if matrix[0][j] == 0 or matrix[i][0] == 0 :matrix[i][j] = 0if row :for i in range(n):matrix[0][i] = 0if col :for i in range(m):matrix[i][0] = 0

O(m+n)方法:

class Solution:def setZeroes(self, matrix: List[List[int]]) -> None:"""Do not return anything, modify matrix in-place instead."""m = len(matrix)n = len(matrix[0])exist_m = [False]*mexist_n = [False]*nfor i in range(m):for j in range(n):if matrix[i][j] == 0 :exist_m[i] = Trueexist_n[j] = Truefor i in range(m):for j in range(n):if exist_m[i] == True or exist_n[j] == True :                matrix[i][j] = 0

O(mn)的方法好难想,真的不需要这么大的空间!不考虑这种方法了,直接学习O(1)的方法不好吗!

日后再次复习重新写

54 螺旋矩阵

未看解答自己编写的青春版

加一减一好像有些乱的版本,因为我给 right 和 down 的定义都是 m n 。所以:在索引时,要记得减一。在倒序时,因为第一个值是可以取到的,所以要记得减一。在倒序时,因为最后一个值是不能取到的,所以要对 left 和 up 减一。

class Solution:def spiralOrder(self, matrix: List[List[int]]) -> List[int]:m = len(matrix)n = len(matrix[0])left = 0right = nup = 0down = mtotal = m*nres = [0]*totalcount = 0while count < total :for i in range(left,right):res[count] = matrix[up][i]count += 1up += 1if up >= down :breakfor i in range(up,down):res[count] = matrix[i][right-1]count += 1right -= 1if right <= left :breakfor i in range(right-1,left-1,-1):res[count] = matrix[down-1][i]count += 1down -= 1if up >= down :breakfor i in range(down-1,up-1,-1):res[count] = matrix[i][left]count += 1left += 1if right <= left :breakreturn res

更改下标逻辑:给 right 和 down 的定义变为 m-1 n-1 。值得注意的是,下标逻辑改变,判断跳出的逻辑也要相应改变。

class Solution:def spiralOrder(self, matrix: List[List[int]]) -> List[int]:m = len(matrix)n = len(matrix[0])left = 0right = n-1up = 0down = m-1total = m*nres = [0]*totalcount = 0while count < total :for i in range(left,right+1):res[count] = matrix[up][i]count += 1up += 1if up > down :breakfor i in range(up,down+1):res[count] = matrix[i][right]count += 1right -= 1if right < left :breakfor i in range(right,left-1,-1):res[count] = matrix[down][i]count += 1down -= 1if up > down :breakfor i in range(down,up-1,-1):res[count] = matrix[i][left]count += 1left += 1if right < left :breakreturn res

重点

本题要注意的是,对各个指标的定义,会影响判断跳出的逻辑,这个要举例来验证了。但是从直觉上说,还是第二种方法好,不管是在边界处理上更为简洁,还是在跳出逻辑上更符合直觉。

相等时,应该会再有加入元素的操作的,不能跳出。

学习上面第二种的写法。

题解的代码

日后再次复习重新写

48 旋转图像

未看解答自己编写的青春版

没思路

重点

这道题太巧妙了!已经练到这种程度了,如果做题的时候发现没思路,就不要按照老思路一直想下去了,很有可能是思路错了!改变思路,从其他方向入手!

本题思路:先转置,再镜像对称。

或者,先上下翻转,再转置也行。

先转置,再镜像对称,的代码:

class Solution:def rotate(self, matrix: List[List[int]]) -> None:"""Do not return anything, modify matrix in-place instead."""m = len(matrix)n = len(matrix[0])for i in range(m):for j in range(i+1,n):matrix[i][j],matrix[j][i] = matrix[j][i],matrix[i][j]middle = n // 2for i in range(m):for j in range(middle):matrix[i][j],matrix[i][n-1-j] = matrix[i][n-1-j],matrix[i][j]

题解的代码

日后再次复习重新写

240 搜索二维矩阵 II

未看解答自己编写的青春版

倒序遍历,这道题正序遍历不行。

class Solution:def searchMatrix(self, matrix: List[List[int]], target: int) -> bool:m = len(matrix)n = len(matrix[0])row = m-1col = n-1while row > -1 and matrix[row][0] > target :row -= 1while col > -1 and matrix[0][col] > target :col -= 1for i in range(row+1):for j in range(col+1):if matrix[i][j] == target :return Truereturn False

重点

另一种思路:
在这里插入图片描述

class Solution:def searchMatrix(self, matrix, target):""":type matrix: List[List[int]]:type target: int:rtype: bool"""m = len(matrix)if m == 0:return Falsen = len(matrix[0])if n == 0:return Falsei = m - 1j = 0while i >= 0 and j < n:if matrix[i][j] == target:return Trueelif matrix[i][j] < target:j = j + 1else:i = i - 1return False

还是这种思路厉害,时间复杂度是O(n),我的方法严格来说,还是O(n^2)

题解的代码

日后再次复习重新写

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/17751.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C语言每日一题:12《数据结构》相交链表。

题目&#xff1a; 题目链接 思路一&#xff1a; 1.如果最后一个节点相同说明一定有交点。 2.使用两个循环获取一下长度&#xff0c;同时可以获取到尾节点。 3。注意初始化lenA和lenB为1&#xff0c;判断下一个节点是空是可以保留尾节点的。长度会少一个&#xff0c;尾节点没有…

根据前序和中序遍历序列构造二叉树 (递归+迭代两种方法实现)

给定两个整数数组 preorder 和 inorder &#xff0c;其中 preorder 是二叉树的先序遍历&#xff0c; inorder 是同一棵树的中序遍历&#xff0c;请构造二叉树并返回其根节点。 输入: preorder [3,9,20,15,7], inorder [9,3,15,20,7] 输出: [3,9,20,null,null,15,7]源代码如下…

【虚拟数字人】SadTalker简易部署教程

视频教程在这里&#xff1a; sadtalker数字人创建简易教程 项目基于SadTalkers实现视频唇形合成的Wav2lip。通过以视频文件方式进行语音驱动生成唇形&#xff0c;设置面部区域可配置的增强方式进行合成唇形&#xff08;人脸&#xff09;区域画面增强&#xff0c;提高生成唇形的…

修改conda环境缓存默认路径

前言&#xff1a;conda环境占用的内存太大了&#xff0c;每次建立一个新的虚拟环境都要加5个G差不多。所以想要修改默认的路径 问题1&#xff1a;找不到.condarc文件夹 创建condarc文件命令 conda config --add channels r 修改conda环境缓存默认路径 打开.condarc 添加 en…

xinput1_4.dll丢失怎么办?这几个方法都能解决

xinput1_4.dll是一个动态链接库文件&#xff08;DLL&#xff09;&#xff0c;它是Microsoft DirectX的一部分&#xff0c;用于处理游戏控制器输入。当你的电脑提示xinput1_4.dll文件丢失时&#xff0c;意味着与这个文件相关的游戏或应用程序无法正常运行。 当你的电脑提示xinp…

iPhone 8 Plus透明屏应用范围详解

iPhone 8 Plus是苹果公司于2017年推出的一款智能手机&#xff0c;它采用了全新的玻璃机身设计&#xff0c;支持无线充电&#xff0c;并且搭载了更强大的A11仿生芯片。 而透明屏则是一种新型的屏幕技术&#xff0c;可以使手机屏幕呈现出透明的效果。 透明屏是一种将屏幕背后的元…

基于深度学习的裂纹图像分类研究(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

11、springboot项目启动时对容器中的bean进行延迟初始化

springboot项目启动时对容器中的bean进行延迟初始化 预初始化&#xff1a; Spring Boot在启动应用时&#xff0c;会启动Spring容器&#xff0c;当启动Spring容器时&#xff0c;Spring会自动初始化容器中所有的singleton Bean——这是默认行为 预初始化的好处&#xff1a; 1、项…

MATLAB编程实践12、13

生命游戏 游戏的宇宙是无限可扩展的二维矩形网格&#xff0c;群体是那些标注为存活的网格的集合。群体可以依照称为代的离散时间步距进化。在每一步中&#xff0c;每个网格的命运由它周围最近的8个网格邻居的活度决定&#xff0c;规则如下&#xff1a; 如果一个存活的网格有两个…

dubbo-helloworld示例

1、工程架构 2、创建模块 &#xff08;1&#xff09;创建父工程,引入公共依赖 pom.xml依赖 <dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></depende…

图像处理库(Opencv, Matplotlib, PIL)以及三者之间的转换

文章目录 1. Opencv2. Matplotlib3. PIL4. 三者的区别和相互转换5. Torchvision 中的相关转换库5.1 ToPILImage([mode])5.2 ToTensor5.3 PILToTensor 1. Opencv opencv的基本图像类型可以和numpy数组相互转化&#xff0c;因此可以直接调用torch.from_numpy(img) 将图像转换成t…

SQL-事务

set autocommit 0; select * from acount where name 嘉宝 && acount.money > 1000; update acount set money money - 1000 where name 嘉宝; update acount set money money 1000 where name 煎包; commit ; 脏读;当有两个事务使用同一数据库时&#xff0c…

SQL SERVER使用发布订阅同步数据库遇到的坑

可能遇到的各种坑 1.在执行 xp_cmdshell 的过程中出错。调用 ‘CreateProcess’ 失败&#xff0c;错误代码: ‘5’ 网上有各种解决办法&#xff0c;包括改本地安全策略&#xff0c;将sql server服务的网络权限改为本机系统&#xff0c;改cmd用户的读写权限&#xff0c;退出360…

基于WSL2、Ubuntu和VS Code的CUDA平台运行C语言程序

一、CUDA程序执行方法 执行步骤为&#xff1a; 安装Visual Studio Code。在Visual Studio Code中安装插件WSL与电脑的WSL2进行连接。点击左下角&#xff0c;然后再选择连接到WSL。 在WSL中创建以 .cu 为后缀的文件。 rootDESKTOP-HR6VO5J:~# mkdir CUDA /…

【NLP-新工具】语音转文本与OpenAI的用途

一、说明 OpenAI最近2022发布了一个名为Whisper的新语音识别模型。与DALLE-2和GPT-3不同&#xff0c;Whisper是一个免费的开源模型。它的主要功能就是将语音翻译成文本。本文将介绍如何使用这个重要应用库。 二、 Whisper概念 2.1 Whisper是啥&#xff1f; Whisper 是一种自动…

Linux实战:五子棋

一、五子棋原理 采用二维数组保存棋盘信息,棋盘上面的任何一个位置,里面可以放置三类信息。 空用户1的落子(黑子)用户2的落子(白子)下棋就是在二维数组中找对应的空位置,进行落子落完子之后下来就要考虑该落子位置是否有”五子连珠“,进而进行输赢判定,每一次走棋,多…

selenium如何打开浏览器,等待用户输入完成后,再运行

selenium如何打开浏览器&#xff0c;等待用户输入完成后&#xff0c;再运行 一、在脚本中&#xff0c;等待用户输入 在使用 Selenium 打开浏览器后等待用户输入完成&#xff0c;可以使用 Python 编写一个简单的脚本来实现。首先&#xff0c;确保你已经安装了 Selenium 和对应的…

数据结构与算法

时间复杂度和空间复杂度 时间复杂度大 O 表示法&#xff1a;表示代码执行时间随这数据规模增大的变化趋势。 空间复杂度大 O 表示法&#xff1a;表示代码占用的存储空间随数据规模增大的变化趋势。 数组 编程语言中一般会有数组这种数据类型。不过&#xff0c;它不仅是编程…

【JAVASE】类与对象

⭐ 作者&#xff1a;小胡_不糊涂 &#x1f331; 作者主页&#xff1a;小胡_不糊涂的个人主页 &#x1f4c0; 收录专栏&#xff1a;浅谈Java &#x1f496; 持续更文&#xff0c;关注博主少走弯路&#xff0c;谢谢大家支持 &#x1f496; 类与对象 1. 面向对象1.1 什么是面向对象…

Cordova+Vue2.0打包apk,保姆教程来袭!

1.环境准备&#xff08;全部都需要配置环境变量&#xff09; java version "1.8.0_341" 安卓sdk android-29 Gradle 4.10.1 node v16.16.0 cordova 10.0.0 (cordova-lib10.1.0)2.安卓环境变量 1. 确认已安装 Android SDK Build-Tools 和 Android SDK Platform-Tool…