leetcode刷题记录(medium,1-20)
已经刷完了前200题的简单难度,现在开始尝试medium。需要记录一些好的方法和思路。对于很大的知识缺失我还是会记在专题上。
2.两数之和
主要需要考虑的问题是进位的问题。另外就是创建一个空节点的方法。
考虑的情况:1.两个节点其中一个空,2.两个节点都空,3.两个节点都不空
极端数值:[5]+[5], [1]+[9,9] 这种连续进位问题
# Definition for singly-linked list.
# class ListNode(object):
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution(object):
def addTwoNumbers(self, l1, l2):
"""
:type l1: ListNode
:type l2: ListNode
:rtype: ListNode
"""
# 创建一个数值为0的节点,但是不能在head==None是写head=Listnode(0)
head = ListNode(0)
re = head
while l1 or l2:
head.val += l1.val + l2.val
if head.val>9:
head.val = head.val%10
head.next = ListNode(1)
else:
head.next = ListNode(0)
# 注意这里应该是elif,否则会执行已经符合的if内的内容
if l1.next and l2.next:
l1 = l1.next
l2 = l2.next
head = head.next
elif not l1.next and l2.next:
# print(1)
l1.val = 0
l2 = l2.next
head = head.next
elif not l2.next and l1.next:
l2.val = 0
l1 = l1.next
head = head.next
elif not l1.next and not l2.next:
if head.next.val == 0:
head.next = None
return re
代码精巧之处:
elif
用在同样级别的判断上,否则会执行已经符合的if内的内容- 创建一个数值为0的节点
head = Listnode(0)
,但是不能在head==None
处写head=Listnode(0)
3.无重复最长字串
判断无重复首先还是想到字典。
但是要考虑清楚新读入的字符如果发生重复可能有什么情况:
1.上一次出现是在无重复的字串的首项之前,因此对字串无影响。
2.出现在子串中,需要修改子串的头部位置。
class Solution(object):
def lengthOfLongestSubstring(self, s):
"""
:type s: str
:rtype: int
"""
dic = {}
max_length = 0
star = -1
for i, x in enumerate(s):
if x in dic:
star = max(star, dic[x])
max_length = max(max_length, i-star)
dic[x] = i
return max_length
代码精巧之处:
star =-1
可以处理初始情况- 用
enumerate
同时返回数值和坐标
5.最长回文子串
首先是回文子串问题,需要有一个对于回文串的处理思路,考虑奇数和偶数,从中间向两侧伸展。
class Solution(object):
def longestPalindrome(self, s):
"""
:type s: str
:rtype: str
"""
maxlength = 0
p = 0
for i in range(len(s)):
cur = max(self.getlen(s, i, i), self.getlen(s, i, i+1))
if cur > maxlength:
maxlength = cur
p = i - (maxlength-1)//2
return s[p: p+maxlength]
def getlen(self, s, l, r):
length = 0
while l >= 0 and r < len(s) and s[l] == s[r]:
r += 1
l -= 1
length = r - l - 1
return length
代码精妙之处:
- 回文串的处理方法,子函数输入字符串和两个中心位置。
- 注意求解子串长度时候坐标计算,最好画图
11.盛水最多的容器
这个题目的思路是之前看过的题目给了一些启发。采用双指针的方法,左右逼近。移动小的那一侧的指针,直到两者相遇。
class Solution(object):
def maxArea(self, height):
"""
:type height: List[int]
:rtype: int
"""
n = len(height)
i = 0
j = len(height)-1
max_A = 0
while i < j:
left = height[i]
right = height[j]
if left>right:
j -= 1
max_A = max(max_A, right*(n-1))
else:
i += 1
max_A = max(max_A, left*(n-1))
n -= 1
return max_A
代码亮点:
- 双指针思路,对于这一类的问题比较有效
15.三数之和
真的挺难的,时间复杂度有要求的。要求限制在
O
(
N
2
)
O(N^2)
O(N2)。
首先想到的思路是公众号上的,搜索与剪枝。但是这种策略可能是我运用的不好,还是会超时。这个题目实际上有一个简化的趋势,就是要求三个数字加起来等于0就可以。因此可以考虑首先进行排序。然后确定一个数字x
,目标变为target = 0-x
,然后应对剩下的数组运用双指针的方法解决。
时间复杂度是: O ( n 2 ) O(n^2) O(n2),数组排序 O ( N l o g N ) O(NlogN) O(NlogN),遍历数组 O ( n ) O(n) O(n),双指针遍历 O ( n ) O(n) O(n),总体 O ( N l o g N ) + O ( n ) ∗ O ( n ) O(NlogN)+O(n)∗O(n) O(NlogN)+O(n)∗O(n)
极端条件:(0,0,0)和超大数组
class Solution(object):
def threeSum(self, nums):
if not nums or len(nums)<3:
return []
nums.sort()
target = 0
ans = []
k = 0
while k < len(nums)-2:
target = 0 - nums[k]
i,j = k+1,len(nums)-1
if target<0:
break
while i < j:
if nums[i]+nums[j]<target:
i += 1
# 可以剪枝吧?
#while i<j and nums[i] == nums[i-1]:
# i += 1
elif nums[i]+nums[j]>target:
j -= 1
#while i<j and nums[j] == nums[j+1]:
# j -= 1
elif nums[i]+nums[j] == target:
ans.append([nums[k], nums[i], nums[j]])
i += 1
while i<j and nums[i] == nums[i-1]:
i += 1
k += 1
while k<len(nums)-2 and nums[k] == nums[k-1]:
k += 1
return ans
'''# 按照公众号的思路写的,时间复杂度不行O(N^3)
def threeSum(self, nums):
"""
:type nums: List[int]
:rtype: List[List[int]]
"""# sorted c.sort()
if not nums:
return []
nums.sort()
target = 0
word = 0
self.ans = []
now = []
index = -1
self.dfs(nums, self.ans, now, index, word, target)
return self.ans
def dfs(self, nums, ans, now, index, word,target):
if target == 0 and word == 3:
self.ans.append(now[:])
return
if word == 3:
return
for i in range(index+1,len(nums)):
target += nums[i]
word += 1
now.append(nums[i])
if target<1 and (i==index+1 or (i>index+1 and nums[i] != nums[i-1])):
self.dfs(nums, self.ans, now, i, word, target)
if target>0:
word -= 1
target -=nums[i]
now.pop()
return
word -= 1
target -=nums[i]
now.pop()
'''
这个题目还是难度不小的。
代码亮点:
- 处理重复问题,如果
nums[i]==nums[i-1]
就跳过。 - 需要说一下排序问题,一般两种,
a = sorted(nums)
和nums.sort()
一种无返回值,一种有返回值。
17.电话号码中的字母组合
这题真是,我看的一脸蒙蔽,实在是没思路。看了解答觉着茅塞顿开。还是一道不错的递归回溯题目,真的很考察逻辑思维。
代码有两种,一种是python的语言方法,一种是纯递归回溯,第一种更简洁,第二中更精妙。
class Solution(object):
def letterCombinations(self, digits):
"""
:type digits: str
:rtype: List[str]
"""
KEY = {'2': ['a', 'b', 'c'],
'3': ['d', 'e', 'f'],
'4': ['g', 'h', 'i'],
'5': ['j', 'k', 'l'],
'6': ['m', 'n', 'o'],
'7': ['p', 'q', 'r', 's'],
'8': ['t', 'u', 'v'],
'9': ['w', 'x', 'y', 'z']}
if digits == '':
return []
ans = ['']
for num in digits:
ans = [pre+suf for pre in ans for suf in KEY[num]]
return ans
# 方法1
def letterCombinations(self, digits):
if not digits:
return []
dic = {'2': ['a', 'b', 'c'],
'3': ['d', 'e', 'f'],
'4': ['g', 'h', 'i'],
'5': ['j', 'k', 'l'],
'6': ['m', 'n', 'o'],
'7': ['p', 'q', 'r', 's'],
'8': ['t', 'u', 'v'],
'9': ['w', 'x', 'y', 'z']}
res=[]
def backtrack(n,tmp):
if n==len(digits):
res.append(tmp)
return
# 这实在是妙啊,我真的服了
for i in range(len(dic[digits[n]])):
backtrack(n+1,tmp+dic[digits[n]][i])
backtrack(0,"")
return res
#'''
这代码真是绝了,重点说一下第二个代码的for循环部分。
首先,循环是对于第n个数字对应的字母进行遍历。
其次,循环结构的主体是调用函数,每次递归都前进一个数字,递归结束就是数字见尾。
注意:每次都是递归完成,回到循环。以两个数字为例,从res的数组可以看出,先第一个数字第一个字母和第二个数字的所有字母一一组合,完成一个完整的for循环。然后第一个数字的第二个字母再和第二个数字所有字母组合。直到完成。
19.删除链表的倒数第N节点
我发现链表题目没有双指针不能解决的。只是这一遇到了一个我没遇到的双指针用法。
目前已经见过的双指针,判断有环,判断链表合并,等分链表,和目前这个倒数第x个节点
问题分析两个情况:
1. 删除首个节点。return slow.next
2. 删除非首个,slow.next = slow.next.next
class Solution:
def removeNthFromEnd(self, head: ListNode, n: int) -> ListNode:
if not head or not head.next:
return None
slow = head
fast = head
# 保证fast.next是None时候,slow.next指向要删除的
while n > 0:
fast = fast.next
n -= 1
# 考虑删除第一个
if not fast:
return slow.next
# 只要fast.next是None,slow的下一个指向需要被删除的
while fast.next:
fast = fast.next
slow = slow.next
slow.next = slow.next.next
return head
代码精妙之处:
- 双指针的使用,帮助了判断位置。
- 考虑清楚删除节点的两种情况。