每天保证至少两小时
day1--9.12
55. 跳跃游戏
题解
class Solution: def canJump(self, nums: List[int]) -> bool: cover = 0 i = 0 if len(nums) <= 1: return True while i <= cover: # 局部最优:局部最大覆盖范围 cover = max(cover, i + nums[i]) if cover >= len(nums) - 1: return True i += 1 return False 总结:
题目理解,样例解析部分没有认真思考,导致对题目理解存在偏差。
程序每一步只能处理一件事,不要试图在第一步的时候,思考第二步。
要图形结合,要先思考明确,然后再动手编写代码,不要直接就上手。
代码语法等是最次的,重要的是编程的思维,抽象的、处理问题的思维模式,编写代码前,一定要先理清思路,如果思路没有理清,那就不要动手。
贪心的核心:由局部最优->全局最优
贪心无套路,对于贪心,多维度的分解问题,抽象各个子问题。
45. 跳跃游戏 II
解析:https://programmercarl.com/0045.%E8%B7%B3%E8%B7%83%E6%B8%B8%E6%88%8FII.html
题解
class Solution: def jump(self, nums: List[int]) -> int: # 局部最优:curCover、nextCover向前推进,在当前curCover下,得到最大的nextCover curCover = 0 #第一跳的位置 nextCover = 0 # 下一跳的覆盖范围 res = 0 i = 0 if len(nums) <= 1: return 0 while i < len(nums) - 1: nextCover = max(nextCover, nums[i] + i) # 最大覆盖范围 if i == curCover: res += 1 curCover = nextCover i += 1 return res总结
局部最优的处理,最大覆盖范围、跳跃次数增加的位置,为本题难点。
局部最优:两次跳跃,取两次跳跃的最远覆盖范围,决定跳跃位置。curCover、nextCover向前推进,在当前curCover下,得到最大的nextCover。在这两个指针的不断向前推进中,确保当前一步、下一步这两次跳跃的距离之和最远。
跳跃位置:当前curCover覆盖的范围内,只需要跳跃一次。无论第二次起跳的起点在哪里,反正这个起点始终在我curCover的覆盖范围内,只要在这个范围内,我跳跃一次就行。注意,题目未要求标明每次跳跃的起点,因此这里对这种细节可以进行模糊,增加代码编写难度。
程序流程:遍历curCover,在curCover内找到最大的nextCover。遍历完成curCover后,将nextCover赋值给curCover,即完成最优的一次跳跃,并增加跳跃次数。继续在curCover中寻找最优的nextCover,以此循环往复。
特殊值处理:其一,要注意数据为空等情况;其二,要注意最后一次跳跃的处理。即若最后一次跳跃的curCover能够到达数组最后一位,则当
i == curCover为ture,或者说i = curCover = len(nums)-1时,此时跳跃次数的增加是没有意义的。因为起跳动作在跳跃的起点位置,起跳res计数加一。最后一次跳跃时,在起点位置,跳数加一,跳到数组最后一位。既然到达数组最后一位,题目要求已符合,此时应不再执行跳跃动作,自然起跳计数不应该计数。综上所述:总的来说,贪心的问题核心还是在于对局部最优和全局最优的把握中,实际编程过程中,当前我最容易纠结在一些没必要的需求上,核心还是在于思考问题方式不够全面、发散,练习动手过少。
day2--9.13
1190. 反转每对括号间的子串
解析:
题解:
20. 有效的括号
解析:栈
题解:
class Solution: def isValid(self, s: str) -> bool: # 栈 stack = [] for i in range(len(s)): top = stack[-1] if stack else None if ')' == s[i] and top == '(': stack.pop() continue if ']' == s[i] and top == '[': stack.pop() continue if '}' == s[i] and top == '{': stack.pop() continue stack.append(s[i]) if len(stack) == 0: return True return False总结:
python栈的用法,list的用法,
1. 两数之和
解析:
题解: 解法一,暴力双循环
class Solution: def twoSum(self, nums: List[int], target: int) -> List[int]: res = [] for i in range(len(nums)): for j in range(i+1, len(nums)): if ( nums[i] + nums[j] ) == target: res.append(i) res.append(j) return res解法二,利用map哈希表
class Solution: def twoSum(self, nums: List[int], target: int) -> List[int]: res = [] # 哈希 map = {} for i, num in enumerate(nums): complement = target - num if complement in map.keys(): return [map[complement], i] map[num] = i return []总结
字典用法,字典遍历循环
使用
enumerate进行列表遍历
day3--9.29
14. 最长公共前缀
题解:
class Solution: def longestCommonPrefix(self, strs: List[str]) -> str: if len(strs) == 0: return "" firstPrefix = strs[0] for i in range(len(firstPrefix)): """纵向比较""" first_ch = firstPrefix[i] for j in range(1, len(strs)): """每个字符串进行比较""" if len(strs[j]) <= i or strs[j][i] != first_ch: return firstPrefix[:i] return firstPrefix 总结:
python中,list切片的用法使用
[:i]进行切片
1047. 删除字符串中的所有相邻重复项
解析:
题解:
class Solution: def removeDuplicates(self, s: str) -> str: """栈移除相邻相同的元素""" stack = [] size = len(s) if size == 0: return '' for i in range(size): """ 1.空栈,直接入栈 2.非空,top != s[i],直接入 3.非空,top == s[i], 不入栈,且pop """ if stack and stack[-1] == s[i]: stack.pop() continue stack.append(s[i]) return "".join(stack)总结:
本题主要考察栈的用法,重点明确python中栈的几种操作:(1)栈顶值top如何取;(2)弹栈;(3)压栈;(4)栈空判断;
此外,本题还学习到list拼接字符串的方式:字符串的join()方法,用于将序列中的元素以指定的字符连接生成一个新的字符串。
392. 判断子序列
解析:
题解:
class Solution: def isSubsequence(self, s: str, t: str) -> bool: """双指针,判别子序列""" if len(s) > len(t): return False s_point = 0 for i in range(len(t)): if s_point < len(s) and t[i] == s[s_point]: s_point += 1 if s_point >= len(s): return True return False总结:
此题使用快慢双指针很好解决
注意点:本题注意空序列可以是任意序列的子序列,包括空序列
206. 反转链表
解析:
题解:
# Definition for singly-linked list. # class ListNode: # def __init__(self, val=0, next=None): # self.val = val # self.next = next class Solution: def reverseList(self, head: Optional[ListNode]) -> Optional[ListNode]: if head is None: return None res = ListNode() cur = head while cur is not None: tmp = cur cur = cur.next tmp.next = res.next res.next = tmp return res.next 总结:
没什么难度~
day4---9.30
1190. 反转每对括号间的子串
解析:
题解:
class Solution: def reverseParentheses(self, s: str) -> str: """ 实现思路:栈+双端队列 1.s字符ch依次序进栈stack 2.遇“)”进入弹栈,按弹栈顺序组成临时字符串temp 3.若stack不为空,temp按索引顺序入栈 4.继续对s进行入栈操作,循环23 5.stack为空,返回temp """ if len(s) == 0: return "" stack = [] for i in range(len(s)): if len(stack) != 0 and s[i] == ')': """遇到)弹栈""" tmp = "" while len(stack) != 0 and stack[-1] != '(': """遇到(结束弹栈,弹栈值由tmp记录""" tmp += stack[-1] stack.pop() stack.pop() # 弹出'(' for j in range(len(tmp)): stack.append(tmp[j]) continue # 过滤) stack.append(s[i]) # 其余情况,直接入栈 if len(stack) != 0: """处理s首尾无括号的情况""" return "".join(stack) return ""总结:
本题处理过程中,主要问题在于:未充分考虑括号匹配的几种形式,如
(abc)、ab(c)、(ab)c、a(b)c此外,去掉括号后,栈里面的值需要按顺序输出
day5---10.01
739. 每日温度
题解:
class Solution: def dailyTemperatures(self, temperatures: List[int]) -> List[int]: """ 双循环,超时,使用单调栈 1.遍历temp数组, 2.tmp[i]<=stack.top(),索引i入栈 3.tmp[i] > stack.top()出栈 4.asn[stack.top()] = i - stack.top() 5.以此类推,直至tmp遍历完 """ res = [0] * len(temperatures) stack = [] for i, T in enumerate(temperatures): while len(stack) != 0 and T > temperatures[stack[-1]]: res[stack[-1]] = i - stack[-1] stack.pop() stack.append(i) return res总结:
思路:找元素某一方向比其大/小的元素,如本题
本题为第一次接触单调栈,若不用单调栈,此题使用暴力双循环会超时
单调栈时间复杂度为
O(n)
day6--10.02
781. 森林中的兔子
题解:
class Solution: def numRabbits(self, answers: List[int]) -> int: """ 本题思想,利用哈希先去重,然后计算,本题只能通过列表进行数学推到 1.建立哈希表,key=ans[i],value记录其出现次数 2.遍历ans,哈希值进行累加 3.分别计算每个ans至少需要多少兔子 4.注意向上取整 """ if len(answers) == 0: return None count = Counter(answers) ans = 0 for key, value in count.items(): ans += (value + key) // (key + 1) * (key + 1) return ans总结:
本题理解,主要在于向上取整。假设有x只兔子回答y,即回答y的次数为x次,在字典中key=y,value=x;则这些回答y的兔子,至少要分为
(y + x) // (y + 1)组。为什么是
(y+1):因为需要加上回答问题的兔子,这里y+1也是每组兔子的最小数量为什么是
(x + y) // (y+1):为了向上取整。这里核心在于(x + y)中加的y,之所以要加这个y就是为了最终结果进行向上取整。为什么是
(x+ y)而不是(x + y + 1):若采用(x + y + 1)则在进行整除时,若x = y+1则正好整除的情况下会多算1组。例如x = 3,y = 2,此时最少分一组即可,每组3个兔子,每个兔子都说有2个兔子与它颜色一样。为什么
(x + y) // (y + 1) * (y + 1)中整除了(y + 1)然后有进行乘法操作:计算机计算顺序和数学运算存在差别,这里分子分母不会进行约分的精确计算,通过整除操作计算出最少需要多少组即(x + y) // (y + 1)组,每组兔子的个数最少为y + 1,因此结果相乘之后得到总共需要多少只兔子。本题还需要多多理解,理解难点就是整除这一块。做题过程中有思考过整除、求余等,欲通过整除+余数计算,有许多特殊情况需要考虑,但是一旦转换为程序里面的整除问题,这些特殊情况就容易解决了。
日后处理相关问题,凡是涉及到整除、求模取余等相关操作,要及时与整除进行联系,没准能够事半功倍的解决问题。