leetcode热题100在之前的分类总结中已经完成了一部分,现在完成剩下的一部分问题
https://leetcode-cn.com/problems/longest-palindromic-substring/
class Solution:
def longestPalindrome(self, s: str) -> str:
'''
暴力法,求得所有的字串,然后判断是否是回文串(超时)
'''
if not s: return ''
max_len = 0
res = ''
for i in range(len(s)):
for j in range(i, len(s)):
if s[i:j+1] == s[i:j+1][::-1]:
if max_len < j-i+1:
max_len = j-i+1
res = s[i:j+1]
return res
class Solution:
def longestPalindrome(self, s: str) -> str:
'''
暴力法存在大量的重叠子问题,例如当判断s[1:5]是否为回文串,判断了s[2:4]是否为回文串
采用动规的方式解决重叠子问题,以空间换取时间
状态:dp[i][j]表示从i到j是否为回文串
状态转移方程:dp[i][j] = dp[i+1][j-1] and s[i] == s[j]
边界条件:当仅有一个元素时:dp[i][i] = True
当仅有俩元素时,dp[i][j] = s[i] == s[j]
'''
if not s: return ''
l = len(s)
dp = [[False]*l for _ in range(l)]
# 仅有一个元素时的边界条件
for i in range(l):
dp[i][i] = True
max_len = 1
start = 0
for j in range(1, l):
for i in range(0, j):
if s[i] == s[j]:
# 仅有两个元素时的条件
if j - i < 3:
dp[i][j] = True
else:
dp[i][j] = dp[i+1][j-1]
else: dp[i][j] = False
## 记录最长的长度
if dp[i][j]:
cur_len = j - i + 1
if cur_len > max_len:
max_len = cur_len
start = i
return s[start:start + max_len]
https://leetcode-cn.com/problems/generate-parentheses/
class Solution:
def generateParenthesis(self, n: int) -> List[str]:
'''
暴力递归,超时
'''
def helper(s, path):
if not s:
if isvalid(path) and path not in self.res:
self.res.append(path)
return
for i in range(len(s)):
left = path.count('(')
right = path.count(')')
if right> left: continue
if left == right and s[i] == ')': continue
helper(s[:i]+s[i+1:], path + s[i])
def isvalid(s):
stack = []
for char in s:
if char == '(':
stack.append(char)
else:
if stack:
stack.pop()
else:
return False
if not stack: return True
if not s: return []
s = '(' *n + ')'*n
self.res = []
helper(s, '')
return self.res
class Solution:
def generateParenthesis(self, n: int) -> List[str]:
'''
上边的递归存在大量的重复解
'''
def helper(left, right, path):
if left == n and right == n:
self.res.append(path)
return
# 这种情况构不成有效括号
if left < right:
return
## 如果左括号已经使用的数目没有达到n,左括号还有一些没有使用
if left < n:
helper(left + 1, right, path +'(')
# 右括号还有一些没有使用
if right < n:
helper(left, right + 1, path + ')')
self.res = []
helper(0, 0, '')
return self.res
https://leetcode-cn.com/problems/search-in-rotated-sorted-array/
class Solution:
def search(self, nums: List[int], target: int) -> int:
'''
限制时间复杂度为logn,考虑到二分查找,对于二分查找主要的工作就是对于左右区间的拆分判断
对于[4,5,6,7,0,1,2], 对于给定的0, 左右两部分为【4,5,6】,【7,0,1,2,3】
两部分中肯定有一部分为有序的,
如果nums[l]nums[l],存在于左边部分
否则位于右边部分
如果右边有序,如果target>nums[mid] 位于右边部分
否则位于左边
'''
l, r = 0, len(nums)-1
while l <= r:
mid = l + (r-l)//2
# print('*',mid,/ l, r)
# 如果值刚好相等,就返回
if target == nums[mid]: return mid
# 如果左边序列有序
if nums[l] <= nums[mid]:
# target位于左半块
if nums[l]<=target<nums[mid]:
r = mid-1
# target位于右半块
else:
l = mid+1
# 左半块无序
else:
# target位于右半块(右半块有序)
if nums[mid]<target <=nums[r] :
l = mid + 1
else:
r = mid - 1
# print(mid, l, r)
return -1
https://leetcode-cn.com/problems/rotate-image/
class Solution:
def rotate(self, matrix: List[List[int]]) -> None:
"""
Do not return anything, modify matrix in-place instead.
旋转90度就是转置加上翻转
"""
if not matrix: return
n = len(matrix)
# 转置
for i in range(n):
for j in range(i+1, n):
if i ==j: continue
matrix[i][j], matrix[j][i] = matrix[j][i], matrix[i][j]
#翻转每一行
for i in range(n):
matrix[i] = matrix[i][::-1]
https://leetcode-cn.com/problems/jump-game/
class Solution:
def canJump(self, nums: List[int]) -> bool:
'''
宽度优先遍历,看是否可以达到最后(超时)
'''
from collections import deque
if not nums: return True
target = len(nums)-1
queue = deque()
queue.append((0,nums[0]))
while queue:
start_index, step = queue.pop()
if start_index == target: return True
# if step == 0 and start_index != target: continue
for i in range(1, step+1):
if i + start_index <= target:
# print(start_index+i, start_index, step)
queue.append((start_index+i, nums[start_index+i]))
if start_index+i == target: return True
return False
class Solution:
def canJump(self, nums: List[int]) -> bool:
'''
利用贪心算法
构建数组,记录从第i个位置起始可以到达的最远位置
'''
if not nums: return True
# 利用reach数组存储每个位置起始可以达到的最远位置
reach = [nums[0]]
for i in range(1, len(nums)):
# 如果最远可到达位置超过数组,那么说明可以到达最后一个位置
if reach[i-1] >= len(nums)-1: return True
# 如果第i个位置可以达到,则利用reach记录最大的位置
if i <= reach[i-1]:
reach.append(i + nums[i])
# 如果达不到则返回False
else:
return False
return True
https://leetcode-cn.com/problems/merge-intervals/
class Solution:
def merge(self, intervals: List[List[int]]) -> List[List[int]]:
'''
按照第一个元素排序
第一种情况,i+1的第一个数大于i的第二个数,这两个区间不重合,保存第i个区间
第二种情况,i+1的第一个数小于i的第二个数,然后这俩区间合并
'''
if not intervals: return []
if len(intervals) == 1: return intervals
intervals.sort(key=lambda x: x[0])
# print(intervals)
left = intervals[0][0]
right = intervals[0][1]
res = []
for i in range(len(intervals)-1):
if intervals[i+1][0] > right:
res.append([left, right])
left = intervals[i+1][0]
right = intervals[i+1][1]
else:
right = max(right, intervals[i+1][1])
res.append([left, right])
return res
https://leetcode-cn.com/problems/unique-binary-search-trees/
class Solution:
def numTrees(self, n: int) -> List[TreeNode]:
'''
采用递归的方法,直接求解(超时)
'''
if(n==0):
return 1
def build_Trees(left,right):
all_trees=[]
if (left>right):
return 1
num = 0
for i in range(left,right+1):
left_trees=build_Trees(left,i-1)
right_trees=build_Trees(i+1,right)
num += left_trees * right_trees
return num
res=build_Trees(1,n)
return res
class Solution:
def numTrees(self, n):
"""
假设n个节点存在
令G(n)的从1到n可以形成二叉排序树个数
令f(i)为以i为根的二叉搜索树的个数
即有:G(n) = f(1) + f(2) + ... + f(n)
n为根节点,当i为根节点时,其左子树节点个数为[1,2,3,...,i-1],右子树节点个数为[i+1,i+2,...n],所以当i为根节点时,其左子树节点个数为i-1个,右子树节点为n-i,即f(i) = G(i-1)*G(n-i),
上面两式可得:G(n) = G(0)*G(n-1)+G(1)*(n-2)+...+G(n-1)*G(0)
"""
G = [0]*(n+1)
G[0], G[1] = 1, 1
for i in range(2, n+1):
for j in range(1, i+1):
G[i] += G[j-1] * G[i-j]
return G[n]
https://leetcode-cn.com/problems/construct-binary-tree-from-preorder-and-inorder-traversal/
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
def buildTree(self, preorder: List[int], inorder: List[int]) -> TreeNode:
'''
前序遍历:根——左——右
中序遍历:左——根——右
1、利用前序遍历找到二叉树的根节点,然后在对应的中序遍历中找到这个节点
2、在中序遍历中,根节点将数组分为两部分,左右两部分分别为左右子树,得到左右子树的节点个数
3、得到左右子树的前序遍历与中序遍历结果,递归处理
'''
def helper(preorder_left, preorder_right, inorder_left, inorder_right):
'''
四个参数分别表示前序遍历的左右节点的索引,中序遍历的左右节点的索引
'''
if preorder_left > preorder_right: return
# 前序遍历的第一个节点就是根节点
preorder_root_index = preorder_left
# 中序遍历中的根节点的索引
inorder_root_index = hashmap[preorder[preorder_root_index]]
# 构建根节点
root = TreeNode(preorder[preorder_root_index])
# 左子树的节点数目
left_tree_size = inorder_root_index - inorder_left
# 递归构建左子树
root.left = helper(preorder_root_index+1, preorder_root_index+left_tree_size, inorder_left, inorder_root_index-1)
# 递归构建右子树
root.right = helper(preorder_root_index+left_tree_size+1, preorder_right, inorder_root_index+1, inorder_right)
return root
# 构建元素-索引字典(提高搜索根节点的效率)
hashmap = {j: i for i, j in enumerate(inorder)}
return helper(0, len(preorder)-1, 0, len(inorder)-1)
https://leetcode-cn.com/problems/flatten-binary-tree-to-linked-list/
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution:
def flatten(self, root: TreeNode) -> None:
"""
Do not return anything, modify root in-place instead.
展开的链表为其前序遍历的结果
1、先找到左子树最右边的节点,将右子树接到左子树最右边节点上
2、将左子树移动到原来右子树的位置
3、清空左子树,完成了第一个点
4、递归搜寻树的右子树
"""
def helper(root):
if not root: return
cur_node = root.left
if cur_node:
# 找到左子树的最右边节点
while cur_node.right:
cur_node = cur_node.right
#右子树接到左子树最右边节点上
cur_node.right = root.right
# 左子树接在原来右子树的位置
root.right = root.left
# 清空左子树
root.left = None
# 递归搜寻右子树
helper(root.right)
helper(root)
https://leetcode-cn.com/problems/linked-list-cycle-ii/
class Solution:
def detectCycle(self, head: ListNode) -> ListNode:
'''
哈希表
'''
hashmap = set()
if not head: return
while head:
if head not in hashmap:
hashmap.add(head)
else:
return head
head = head.next
return
class Solution:
def detectCycle(self, head: ListNode) -> ListNode:
'''
环形链表自然而然想到快慢指针
1、利用快慢指针寻找是否出现环,如果没有环,返回null
2、如果出现环,利用双指针找寻入口的节点
第一个指针指向头部,第二个指针指向快慢指针相遇的节点
二者再次相遇的节点就是入口节点
具体分析参考:https://leetcode-cn.com/problems/linked-list-cycle-ii/solution/linked-list-cycle-ii-kuai-man-zhi-zhen-shuang-zhi-/
'''
if not head: return
# s为慢指针,f为快指针
s, f =head, head
while f and f.next:
s, f = s.next, f.next.next
if f == s: break
# 没有环
if not f or not f.next: return
# s慢指针即为二者相遇的节点
while s != head:
s, head = s.next, head.next
return s
https://leetcode-cn.com/problems/lru-cache/
from collections import OrderedDict
class LRUCache:
'''
利用python自带的orderdict解决,实际就是双向链表加hashmap的结构
'''
def __init__(self, capacity: int):
self.maxsize = capacity
self.lrucache = OrderedDict()
def get(self, key: int) -> int:
if key in self.lrucache:
# 将现有 key 移动到有序字典的任一端。 如果 last 为真值(默认)则将元素移至末尾;如果 last 为假值则将元素移至开头
self.lrucache.move_to_end(key)
return self.lrucache.get(key,-1)
def put(self, key: int, value: int) -> None:
if key in self.lrucache:
del self.lrucache[key]
self.lrucache[key] = value
if len(self.lrucache)>self.maxsize:
# 如果 last 值为真,则按 LIFO 后进先出的顺序返回键值对,否则就按 FIFO 先进先出的顺序返回键值对。
self.lrucache.popitem(last = False)
'''
利用hashmap加上双向链表实现功能
'''
class Node:
def __init__(self,key,val):
self.key=key
self.val=val
self.next=None
self.prev=None
class LRUCache:
def __init__(self, capacity: int):
self.dic={}
self.head=Node(None,None)
self.tail=Node(None,None)
self.head.next=self.tail
self.tail.prev=self.head
self.capacity=capacity
def get(self, key: int) -> int:
if not key in self.dic:
return -1
node=self.dic[key]
self.delete(node)
self.insert(node)
return node.val
def insert(self,node):
node.next,node.prev=self.head.next,self.head
temp=self.head.next
self.head.next=node
temp.prev=node
def delete(self,node):
node.prev.next,node.next.prev=node.next,node.prev
def put(self, key: int, value: int) -> None:
if key in self.dic:
node=self.dic[key]
node.val=value
self.delete(node)
self.insert(node)
return
if len(self.dic)==self.capacity:
node=self.tail.prev
self.delete(node)
del self.dic[node.key]
node=Node(key,value)
self.dic[key]=node
self.insert(node)
https://leetcode-cn.com/problems/sort-list/
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def sortList(self, head: ListNode) -> ListNode:
'''
nlogn的时间复杂度,常数级空间,采用归并排序,采用递归(这样空间复杂度不为常数,因为采用了递归栈
'''
def helper(head):
if not head or not head.next: return head
## 寻找中间节点mid,对于偶数个节点,slow指向左半段的最后一个元素 对于奇数节点,slower位于最中间的元素
slow, fast = head, head.next
while fast and fast.next:
slow, fast = slow.next, fast.next.next
# 切分两块链表
mid, slow.next = slow.next, None
left = helper(head)
right = helper(mid)
# print(left, right)
# 融合左右两部分链表
# dummy node
h = res = ListNode(-1)
while left and right:
if left.val < right.val: h.next, left =left, left.next
else:
h.next, right = right, right.next
h = h.next
h.next = left if left else right
return res.next
return helper(head)
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def sortList(self, head: ListNode) -> ListNode:
'''
nlogn的时间复杂度,常数级空间,采用自底而上的归并排序
具体思路就是,先两个两个merge,然后四个四个merge,持续类推,直到排序完成
例: [3,54,24,9,35,43,27,11]
1、(3,54)(9,24)(35,43)(11,27)
2、(3,9,24,54)(11,27,35,43)
3、(3,9,11,24,27,35,43,54) 排序完成
'''
# 计算链表的长度
L = 0 #链表的长度
p = head
while p:
L += 1
p = p.next
# 构建虚拟头节点
dummy = ListNode(-1)
dummy.next = head
step = 0
while step <= L:
cur = dummy.next
tail = dummy
while cur:
left = cur
right = self.cut(left, step)
cur = self.cut(right, step)
tail.next = self.merge(left, right)
while tail.next:
tail = tail.next
# print(dummy.next)
step = step * 2 + 1
return dummy.next
def cut(self, head, step):
"""
从链表中截取步长为step的段
step的值为1,3,7 ... ,截取的步长为2,4,8 ...
返回截断后半部分的第一个节点
"""
p = head
while p and step:
p = p.next
step -= 1
if not p:
return None
next_ = p.next # 返回截断的后半部分
p.next = None # 截断
return next_
def merge(self, left, right):
'''
融合左右两段
'''
dummyHead = ListNode(0) # 虚拟头节点
p = dummyHead
while left and right:
if left.val < right.val:
p.next = left
p = left
left = left.next
else:
p.next = right
p = right
right = right.next
p.next = left if left else right
return dummyHead.next # 返回排序后的结果
https://leetcode-cn.com/problems/maximum-product-subarray/
class Solution:
def maxProduct(self, nums: List[int]) -> int:
'''
暴力法,遍历所有连续子数组,然后保留最大的乘积(超时)
'''
max_value = float('-inf')
for i in range(len(nums)):
product = nums[i]
max_value = max(max_value, product)
for j in range(i+1, len(nums)):
product *= nums[j]
max_value = max(max_value, product)
return max_value
class Solution:
def maxProduct(self, nums: List[int]) -> int:
'''(可以优化为只用两个变量)
动态规划,dp1[i]表示到第i个元素为止连续子数组的最大乘积,dp2[i]表示到第i个元素为止连续子数组的最小乘积
状态转移方程:
dp1[i] = max(dp1[i-1]*nums[i], dp2[i-1]*nums[i], nums[i])
dp2[i] = min(dp1[i-1]*nums[i], dp2[i-1]*nums[i], nums[i])
边界条件dp1[0] = nums[0]
'''
if len(nums)==1: return nums[0]
dp1 = [float('-inf')] * len(nums)
dp2 = [float('inf')] * len(nums)
res = float('-inf')
dp1[0] = nums[0]
dp2[0] = nums[0]
res = max(res, dp1[0], dp2[0])
for i in range(1, len(nums)):
dp1[i] = max(dp1[i-1]*nums[i], dp2[i-1]*nums[i], nums[i])
# print(dp1)
dp2[i] = min(dp1[i-1]*nums[i], dp2[i-1]*nums[i], nums[i])
res = max(res, dp1[i], dp2[i])
# print(dp1, dp2)
return res
前缀树:相关的内容还没有看过,随后用单独的章节来整理相关的问题,系统学习一下
https://leetcode-cn.com/problems/maximal-square/
class Solution:
def maximalSquare(self, matrix: List[List[str]]) -> int:
'''
动态规划,利用dp[i][j]表示到以matrix[i][j]为正方形右下角元素,最大正方形边长
状态转移:
dp[i][j] = min(dp[i-1][j], dp[i-1,j-1], dp[i][j-1]) + 1 if matrix[i][j] == '1'
边界条件:当边界值为1时dp[0][:] = 1, dp[:][0] = 1,否则二者为0
'''
if not matrix: return 0
m = len(matrix)
n = len(matrix[0])
dp = [[0]*n for _ in range(m)]
res = 0
for i in range(m):
if matrix[i][0] == '1':
dp[i][0] = 1
res = max(res, dp[i][0])
for j in range(n):
if matrix[0][j] == '1':
dp[0][j] = 1
res = max(res, dp[0][j])
# print(dp)
for i in range(1, m):
for j in range(1, n):
if matrix[i][j] == '0':
# print(i,j)
dp[i][j] = 0
continue
dp[i][j] = min(dp[i-1][j], dp[i-1][j-1], dp[i][j-1]) + 1
res = max(res, dp[i][j])
# print(dp)
return res**2
https://leetcode-cn.com/problems/product-of-array-except-self/
class Solution:
def productExceptSelf(self, nums: List[int]) -> List[int]:
'''
要在常数的时间复杂度完成,且不能使用除法
计算当前元素左边各个元素的乘积与当前元素右边各个元素的乘积二者相乘即可得到答案
'''
L = [1] * len(nums) # 左边各个元素的乘积
R = [1] * len(nums) # 右边各个元素的乘积
for i in range(1, len(nums)):
L[i] =L[i-1] * nums[i-1]
for i in range(len(nums)-2, -1, -1):
R[i] = R[i+1] * nums[i+1]
# print(L)
# print(R)
return [L[i]*R[i] for i in range(len(nums))]
github地址
更多文章请点击查看