递归做法是入门题
class Solution:
def maxDepth(self, root):
if root is None:
return 0
else:
left_height = self.maxDepth(root.left)
right_height = self.maxDepth(root.right)
return max(left_height, right_height) + 1
迭代算是一个简单题
class Solution:
def maxDepth(self, root: TreeNode) -> int:
stack = []
if root is not None:
stack.append((1, root))
res = 0
while stack:
current_depth, root = stack.pop()
if root:
res = max(res, current_depth)
stack.append((current_depth+1,root.left))
stack.append((current_depth+1,root.right))
return res
把检索二叉树高度的过程加一个差距判断,并且可以用自底向上的方法优化达到提前返回的目的。
class Solution:
def isBalanced(self, root: TreeNode) -> bool:
def helper(root):
if not root:
return 0
l = helper(root.left)
if l == -1:
return -1
r = helper(root.right)
if r== -1:
return -1
return max(l,r) + 1 if abs(l-r) < 2 else -1
return helper(root) != -1
运用辅助递归函数,根节点和左右字数都是只含有0时删除这个子树。
class Solution:
def pruneTree(self, root: TreeNode) -> TreeNode:
def helper(root):
if not root:
return False
l = helper(root.left)
r = helper(root.right)
if not l:
root.left = None
if not r:
root.right = None
return root.val == 1 or l or r
#删除操作放到树的上一层,用root.left删除。
return root if helper(root) else None
二分思想,递归到合并两个链表的子问题。
class Solution:
def mergeKLists(self, lists: List[ListNode]) -> ListNode:
if not lists:return
n = len(lists)
return self.merge(lists, 0, n-1)
#二分法
def merge(self,lists, left, right):
if left == right:
return lists[left]
mid = left + (right - left) // 2
l1 = self.merge(lists, left, mid)
l2 = self.merge(lists, mid+1, right)
return self.mergeTwoLists(l1, l2)
#合并2个链表
def mergeTwoLists(self,l1, l2):
if not l1:return l2
if not l2:return l1
ans = p = ListNode()
while l1 and l2:
if l1.val < l2.val:
p.next = l1
l1 = l1.next
else:
p.next = l2
l2 = l2.next
p = p.next
if l1:
p.next = l1
if l2:
p.next = l2
return ans.next
没啥好说的,hash表解决
class Solution:
def twoSum(self, nums: List[int], target: int) -> List[int]:
hash_num = {}
for i in range(len(nums)):
x = hash_num.get(target - nums[i],-1)
if x != -1:
return [x,i]
hash_num[nums[i]] = i
既然不能反转,就用空间换时间,用栈把数据值反转,然后循环相加。
class Solution:
def addTwoNumbers(self, l1: ListNode, l2: ListNode) -> ListNode:
#空间换时间,省去2次遍历
s1, s2 = [], []
while l1:
s1.append(l1.val)
l1 = l1.next
while l2:
s2.append(l2.val)
l2 = l2.next
carry = 0
ans = None
#循环相加
while s1 or s2 or carry:
a = 0 if not s1 else s1.pop()
b = 0 if not s2 else s2.pop()
tmp = (a+b+carry)%10
carry = (a+b+carry)//10
curnode = ListNode(tmp)
curnode.next = ans
ans = curnode
return ans
相当于上一道题的简化,直接循环相加即可。
class Solution:
def addTwoNumbers(self, l1: ListNode, l2: ListNode) -> ListNode:
carry = 0
head = cur = ListNode()
while l1 or l2:
x1 = l1.val if l1 else 0
x2 = l2.val if l2 else 0
cur.next = ListNode((x1+x2+carry)%10)
carry = (x1+x2+carry)//10
cur = cur.next
if l1:
l1 = l1.next
if l2:
l2 = l2.next
if carry:
cur.next = ListNode(carry)
return head.next
dp问题,每个位置只能由右边和上边得到,可以轻松的写出转移方程。
class Solution:
def uniquePaths(self, m: int, n: int) -> int:
dp = [1] * n
#初始化
#右和上
for i in range(1, m):
for j in range(1, n):
dp[j] += dp[j-1]
return dp[-1]
比上面一道题加了几个分析判断条件。
class Solution:
def uniquePathsWithObstacles(self, obstacleGrid: List[List[int]]) -> int:
m = len(obstacleGrid)
n = len(obstacleGrid[0])
#一开始就堵了
if obstacleGrid[0][0] == 1:
return 0
dp = [0] * n
#第一行一直往右走,一旦堵了就全堵了
for i in range(n):
if obstacleGrid[0][i] == 0:
dp[i] = 1
else:
break
for i in range(1,m):
#第一列只能往下走,要看上一行第一个走不走得通
dp[0] = dp[0] if obstacleGrid[i][0] == 0 else 0
for j in range(1,n):
#本身堵了就是0,没有就看上和左的路径了
dp[j] = dp[j-1] + dp[j] if obstacleGrid[i][j] == 0 else 0
return dp[-1]
相当于把两个数组都分为左右两部分,左右部分的元素个数需要相等。而边界值就是需要的中位数。
class Solution:
def findMedianSortedArrays(self, nums1: List[int], nums2: List[int]) -> float:
m = len(nums1)
n = len(nums2)
# m更小 不会减出0
if m > n:
m,n = n,m
nums1,nums2 = nums2,nums1
mid = (m+n+1)//2
l,r = 0,m
while l <= r:
#i,j是nums1和nums2中左边元素的数量(不是下标)
i = (l+r)//2
j = mid - i
#nums1大了
if i > 0 and nums1[i-1] >nums2[j]:
r = i - 1
#num1小了
elif i < m and nums1[i] < nums2[j-1]:
l = i + 1
#满足条件,开始寻找左右极值
else:
#左最大
if i == 0:
l_max = nums2[j-1]
elif j ==0:
l_max = nums1[i-1]
else:
l_max = max(nums1[i-1],nums2[j-1])
if (m+n)%2:
return l_max
#右最小
if i== m:
r_min= nums2[j]
elif j ==n:
r_min = nums1[i]
else:
r_min = min(nums1[i],nums2[j])
return (l_max + r_min)/2.0
模拟仿真操作,我们直觉在顺时针打印时,是按照一行一列输出并逐渐往内延申的,我们也可以按照这种思路编写程序。
class Solution:
def spiralOrder(self, matrix: List[List[int]]) -> List[int]:
if not matrix:
return []
#当前未遍历到的矩阵边界
l, r, t, b, res = 0, len(matrix[0]) - 1, 0, len(matrix) - 1, []
while True:
#顶上一行
for i in range(l, r + 1):
res.append(matrix[t][i]) # left to right
t += 1
if t > b: break
#右边一列
for i in range(t, b + 1):
res.append(matrix[i][r]) # top to bottom
r -= 1
if l > r: break
#底下一行
for i in range(r, l - 1, -1):
res.append(matrix[b][i]) # right to left
b -= 1
if t > b: break
#左边一列
for i in range(b, t - 1, -1):
res.append(matrix[i][l]) # bottom to top
l += 1
if l > r: break
return res
每个数仅与前两个数相关,所以只需要2个变量储存历史信息就是完成dp。
class Solution:
def fib(self, N: int) -> int:
d0 = 0
d1 = 1
for i in range(N):
d0,d1= d1,d0+d1
return d0
利用队列,每次处理一层的节点数据。
class Solution:
def levelOrder(self, root: TreeNode) -> List[List[int]]:
out = []
s = collections.deque()
if not root:
return out
s.append(root)
while s:
n = len(s)
temp = []
#一层节点
for i in range(n):
x = s.popleft()
temp.append(x.val)
if x.left:
s.append(x.left)
if x.right:
s.append(x.right)
out.append(temp)
return out
O(1)的要求只能是hash表作为索引+链表存数据的组合了,利用虚拟头尾节点简化边界条件。
class DLinkedNode:
def __init__(self, key=0, value=0):
self.key = key
self.value = value
self.prev = None
self.next = None
class LRUCache:
def __init__(self, capacity: int):
self.capacity = capacity
self.size = 0
self.hashmap = {}
self.head = DLinkedNode()
self.tail = DLinkedNode()
self.head.next = self.tail
self.tail.prev = self.head
def get(self, key: int) -> int:
if key not in self.hashmap:
return -1
#找到节点挪到开头
node = self.hashmap[key]
self.move2head(node)
return node.value
def put(self, key: int, value: int) -> None:
if key not in self.hashmap:
#插入新节点
node = DLinkedNode(key, value)
self.hashmap[key] = node
#添加到开头
node.prev = self.head
node.next = self.head.next
self.head.next.prev = node
self.head.next = node
#容量上限
self.size += 1
if self.size > self.capacity:
removed = self.tail.prev
removed.next.prev = removed.prev
removed.prev.next = removed.next
self.hashmap.pop(removed.key)
self.size -= 1
else:
#更新
node = self.hashmap[key]
node.value = value
self.move2head(node)
#把节点移动到链表头
def move2head(self,node):
node.next.prev = node.prev
node.prev.next = node.next
node.prev = self.head
node.next = self.head.next
self.head.next.prev = node
self.head.next = node
类似用链表处理加法的方式。
class Solution:
def addStrings(self, num1: str, num2: str) -> str:
res = ""
i, j, carry = len(num1) - 1, len(num2) - 1, 0
while i >= 0 or j >= 0 or carry:
#加数1
n1 = int(num1[i]) if i >= 0 else 0
#加数2
n2 = int(num2[j]) if j >= 0 else 0
tmp = n1 + n2 + carry
carry = tmp // 10
res = str(tmp % 10) + res
i, j = i - 1, j - 1
return res
层次遍历,存储最后每层最后一个节点。
class Solution:
def rightSideView(self, root: TreeNode) -> List[int]:
if not root:
return []
#队列结构方便遍历
queue = collections.deque()
queue.append(root)
res = []
while queue:
n = len(queue)
for i in range(n-1):
node = queue.popleft()
if node.left:
queue.append(node.left)
if node.right:
queue.append(node.right)
#存储当前层最后的节点
else:
node = queue.popleft()
res.append(node.val)
if node.left:
queue.append(node.left)
if node.right:
queue.append(node.right)
return res
利用dfs遍历树节点,记录路径利用回溯法求解。
class Solution:
def pathSum(self, root: TreeNode, sum: int) -> List[List[int]]:
res = []
if not root: return []
def helper(root,sum,tmp):
if not root:
return
if not root.left and not root.right and sum - root.val == 0 :
tmp += [root.val]
res.append(tmp)
return
helper(root.left,sum - root.val,tmp + [root.val])
helper(root.right,sum - root.val,tmp + [root.val])
helper(root, sum, [])
return res
滑动窗口解决
class Solution:
def lengthOfLongestSubstring(self, s: str) -> int:
if not s:
return 0
l = r = out = 0
#窗口元素
pattern = set()
while r < len(s):
if s[r] not in pattern:
#更新最大值
pattern.add(s[r])
out = max(out,r-l+1)
r += 1
else:
pattern.remove(s[l])
l += 1
return out
模拟题意得操作
class Solution:
#反转一小段链表
def reverse(self, head, tail):
prev = tail.next
p = head
while prev != tail:
p.next,prev,p = prev,p,p.next
return tail, head
def reverseKGroup(self, head: ListNode, k: int) -> ListNode:
#哨兵节点
new_head = ListNode(0)
new_head.next = head
pre = new_head
while head:
tail = pre
#定位到k长度
for i in range(k):
tail = tail.next
if not tail:
return new_head.next
nxt = tail.next
head, tail = self.reverse(head, tail)
#反转后的一小段连上总链表
pre.next = head
tail.next = nxt
pre = tail
head = tail.next
return new_head.next
利用循环进位加法
class Solution:
def addTwoNumbers(self, l1: ListNode, l2: ListNode) -> ListNode:
carry = 0
head = cur = ListNode()
while l1 or l2 or carry:
x1 = l1.val if l1 else 0
x2 = l2.val if l2 else 0
cur.next = ListNode((x1+x2+carry)%10)
carry = (x1+x2+carry)//10
cur = cur.next
if l1:
l1 = l1.next
if l2:
l2 = l2.next
return head.next
根据定义,出现空值之后必须都是空值。
class Solution:
def isCompleteTree(self, root: TreeNode) -> bool:
if not root:
return False
q = collections.deque()
q.append(root)
#记录空节点是否出现过
flag = False
while q:
n = len(q)
for i in range(n):
node = q.popleft()
if node:
if flag:
#前面出现空值了,错误
return False
else:
q.append(node.left)
q.append(node.right)
else:
flag = True
return True
贪心思想,当当前的部分和小于0时重新开始计数。
class Solution:
def maxSubArray(self, nums: List[int]) -> int:
if not nums:
return 0
out = float('-inf')
cur = 0
for num in nums:
if cur < 1:
cur = num
else:
cur += num
out = max(cur,out)
return out
除了用最大堆完成nlogn的方法之外,还可以利用hash表,存储每个元素出现次数再遍历次数。
class Solution:
def topKFrequent(self, nums: List[int], k: int) -> List[int]:
n_f = {}
f_n = {}
for i in nums:
n_f[i] = n_f.get(i,0) + 1
for n,f in n_f.items():
temp = f_n.get(f,[])
temp.append(n)
f_n[f] = temp
arr = []
#按照次数从小到大遍历
for x in range(len(nums),0,-1):
if x in f_n:
for i in f_n[x]:
arr.append(i)
return arr[:k]
按照定义,把要比较的节点成组放入,成组比较。
import queue
class Solution:
def isSymmetric(self, root: TreeNode) -> bool:
q = queue.Queue()
q.put((root, root))
while q.qsize():
left, right = q.get()
if not left and not right:
continue
#一者为空,不符合
if not left or not right:
return False
if left.val != right.val:
return False
q.put((left.left, right.right))
q.put((left.right, right.left))
return True
当我们用双指针从头尾向中间遍历时,如果是段式回文,前后缀就会相等。
class Solution:
def longestDecomposition(self, text: str) -> int:
n = len(text)
i, j = 0, n - 1
str1, str2, ans = '', '', 0
while i < j:
str1 = str1 + text[i]
str2 = text[j] + str2
#分段回文
if str1 == str2:
ans += 2
str1, str2 = '', ''
i += 1
j -= 1
if n % 2 == 1 or str1 != '':
ans += 1
return ans
通过栈结构,匹配括号。
class Solution:
def isValid(self, s: str) -> bool:
mapping = {')':'(',']':'[','}':'{'}
stack = []
for x in s:
if x not in mapping.keys():
stack.append(x)
else:
if not stack:
return False
if stack.pop() != mapping[x]:
return False
#多余括号
if stack:
return False
return True
一个指针遍历数组,一个存储插入位置。
prvot = 0
for i in range(len(nums)):
if nums[i]!=0:
nums[i],nums[prvot] = nums[prvot],nums[i]
prvot += 1
利用快慢指针遍历
class Solution:
def middleNode(self, head: ListNode) -> ListNode:
if not head:
return
p1 = head
p2 = head.next
while p2 and p2.next:
p1 = p1.next
p2 = p2.next.next
if p2:
return p1.next
else:
return p1
使用双向链表+hash表模拟操作,工程实现比较复杂,推荐看官方题解。
遍历链表,每次处理一个点,利用python多元赋值
class Solution:
def reverseList(self, head: ListNode) -> ListNode:
p1 = None
p2 = head
while p2:
p2.next,p2,p1 = p1,p2.next,p2
return p1
递归处理
class Solution:
def isSameTree(self, p: TreeNode, q: TreeNode) -> bool:
if not p and not q:
return True
if not p or not q:
return False
if p.val != q.val:
return False
return self.isSameTree(p.left,q.left) and self.isSameTree(p.right,q.right)
可以用普通的二分法,也可以用牛顿法求解,令f(x) = 0一阶泰勒展开求出迭代公式。
class Solution:
def mySqrt(self, x: int) -> int:
if x == 0:
return 0
C, x0 = float(x), float(x)
while True:
xi = 0.5*(C/x0+x0)
if abs(x0 - xi) < 1e-7:
break
x0 = xi
return int(xi)
树的问题一般都可以用递归方式解决,这道题可以拆分为过左右子树和不过的两种考虑。
class Solution:
def __init__(self):
#存储变量
self.max_sum = float('-inf')
def maxPathSum(self, root: TreeNode) -> int:
self.helper(root)
return self.max_sum
def helper(self,root):
if not root:
return 0
left_gain = max(self.helper(root.left), 0)
right_gain = max(self.helper(root.right), 0)
#过根节点左右子树的最大路径
node_in_path = left_gain + right_gain + root.val
self.max_sum = max(self.max_sum,node_in_path)
#向上返回的话只能过一个子树
return root.val + max(left_gain , right_gain)
常规的二分法搜索
class Solution:
def minArray(self, numbers: List[int]) -> int:
l,r = 0,len(numbers)-1
while l < r:
mid = (l+r)//2
if numbers[mid] > numbers[r]:
l = mid + 1
elif numbers[mid] < numbers[r]:
r = mid
else:
r -= 1
return numbers[l]
两个链表长度可能不一致,但是A+B和B+A是等长的,所以连在一起遍历如果相交两个指针肯定会重合在入口节点。
class Solution:
def getIntersectionNode(self, headA: ListNode, headB: ListNode) -> ListNode:
p1 = headA
p2 = headB
while p1!=p2:
p1 = p1.next if p1 else headB
p2 = p2.next if p2 else headA
return p1
先把数组排序,然后固定第一个元素再去寻找其他元素。
class Solution:
def threeSum(self, nums: List[int]) -> List[List[int]]:
n=len(nums)
res=[]
if(not nums or n<3):
return []
nums.sort()
for i in range(n):
#排了序,第一个元素为整数组成不了3元组
if nums[i] > 0:
return res
#重复元素只要第一个
if(i>0 and nums[i]==nums[i-1]):
continue
l = i + 1
r = n - 1
while l<r:
#双指针检索合适的元素
if nums[i] + nums[l] +nums[r] == 0:
res.append( [nums[i] , nums[l] , nums[r]])
while(l<r and nums[l]==nums[l+1]):
l += 1
while(l<r and nums[r]==nums[r-1]):
r -= 1
l -= 1
r -= 1
elif(nums[i]+nums[l]+nums[r]>0):
r-=1
else:
l+=1
return res
先把链表分成两块,后半反转之后把两个链表交替结合即可。
class Solution:
def reverseList(self, head):
pre = None
cur = head
while cur:
cur.next, pre, cur = pre, cur, cur.next
return pre
def reorderList(self, head: ListNode) -> None:
if not head:
return head
slow, fast = head, head
while fast and fast.next:
slow, fast = slow.next, fast.next.next
#p为链表的中间位置
p = slow.next
slow.next = None
p = self.reverseList(p)
m = head
while p:
m.next, p.next, m, p = p, m.next, m.next, p.next
详细考虑一下越界情况。
class Solution:
def myAtoi(self, str: str) -> int:
str = str.strip() # 删除首尾空格
if not str:
return 0 # 字符串为空则直接返回
int_max, int_min = 2 ** 31 - 1, -2 ** 31
boudry = 2 ** 31 // 10
res, i, sign = 0, 1, 1
if str[0] == '-': sign = -1 # 保存负号
elif str[0] != '+': i = 0 # 若无符号位,则需从 i = 0 开始数字拼接
for c in str[i:]:
if not '0' <= c <= '9' :
break
if res > boudry or(res == boudry and c > '7'):
#注意,超过边界的数末尾是大于7的,等于7的本身就是边界值,正常输出
return int_max if sign == 1 else int_min
res = 10 * res + ord(c) - ord('0')
return sign*res
使用回溯法
class Solution:
def splitIntoFibonacci(self, S: str) -> List[int]:
n = len(S)
res = []
def backtrack(index, temp):
#index开始,前面数字的分段结果为temp
if index == n:
if self._is_fibonacci(temp):
res.append(temp)
return
for i in range(index, n):
new_value = S[index:i + 1]
if int(new_value) > 2 ** 31 - 1:
break
if not (new_value.startswith('0') and len(new_value) > 1):
if len(temp) >= 2 and int(new_value) == (temp[-1] + temp[-2]):
backtrack(i + 1, temp + [int(new_value)])
elif len(temp) < 2:
backtrack(i + 1, temp + [int(new_value)])
backtrack(0, [])
return res[0] if res else []
#判断是否满足条件
def _is_fibonacci(self, temp):
if len(temp) < 3:
return False
for i in range(2, len(temp)):
if temp[i] != (temp[i - 1] + temp[i - 2]):
return False
return True
利用索引位置把数字归位,第一个对不上的就是缺失的整数
class Solution:
def firstMissingPositive(self, nums: List[int]) -> int:
n = len(nums)
for i in range(n):
while 1 <= nums[i] <=n and nums[nums[i]-1]!= nums[i]:
nums[nums[i] - 1], nums[i] = nums[i], nums[nums[i] - 1]
for i in range(n):
if nums[i] != i+1:
return i+1
return n+1
常规二分查找
class Solution:
def search(self, nums: List[int], target: int) -> int:
l = 0
r = len(nums)-1
while l <= r:
mid = (l+r)//2
if nums[mid] == target:
return mid
elif nums[mid] > target:
r = mid - 1
else:
l = mid + 1
return -1
利用hash表,从每个区间的开始元素开始判断长度。
class Solution:
def longestConsecutive(self, nums: List[int]) -> int:
longest_streak = 0
num_set = set(nums)
for num in num_set:
#找到区间的开始元素
if num - 1 not in num_set:
current_num = num
current_streak = 1
while current_num + 1 in num_set:
current_num += 1
current_streak += 1
longest_streak = max(longest_streak, current_streak)
return longest_streak
递归判断子树是否满足条件,来进行删除(置空操作)。
class Solution:
def pruneTree(self, root: TreeNode) -> TreeNode:
def helper(root):
if not root:
return False
l = helper(root.left)
r = helper(root.right)
if not l:
root.left = None
if not r:
root.right = None
return root.val == 1 or l or r
return root if helper(root) else None
使用单调栈,一直维护当前遍历的树结构的根节点,然后判断值是否符合二叉搜素树定义。
class Solution:
def verifyPostorder(self, postorder: List[int]) -> bool:
root = float('inf')
stack = []
for val in reversed(postorder):
if val > root:
return False
while stack and val < stack[-1]:
root = stack.pop()
stack.append(val)
return True
分治成多个合并两个链表的问题解决
class Solution:
def mergeKLists(self, lists: List[ListNode]) -> ListNode:
if not lists:return
n = len(lists)
return self.merge(lists, 0, n-1)
#拆分子问题分治
def merge(self,lists, left, right):
if left == right:
return lists[left]
mid = left + (right - left) // 2
l1 = self.merge(lists, left, mid)
l2 = self.merge(lists, mid+1, right)
return self.mergeTwoLists(l1, l2)
#两个链表合并
def mergeTwoLists(self,l1, l2):
if not l1:return l2
if not l2:return l1
ans = p = ListNode()
while l1 and l2:
if l1.val < l2.val:
p.next = l1
l1 = l1.next
else:
p.next = l2
l2 = l2.next
p = p.next
if l1:
p.next = l1
if l2:
p.next = l2
return ans.next
模拟逐位加法进位操作,但是可以用空间换时间
class Solution:
def addTwoNumbers(self, l1: ListNode, l2: ListNode) -> ListNode:
#空间换时间,省去多次遍历单向链表
s1, s2 = [], []
while l1:
s1.append(l1.val)
l1 = l1.next
while l2:
s2.append(l2.val)
l2 = l2.next
carry = 0
ans = None
while s1 or s2 or carry:
a = 0 if not s1 else s1.pop()
b = 0 if not s2 else s2.pop()
tmp = (a+b+carry)%10
carry = (a+b+carry)//10
curnode = ListNode(tmp)
curnode.next = ans
ans = curnode
return ans
利用滚动数组处理即可,常规的动态规划问题
class Solution:
def uniquePaths(self, m: int, n: int) -> int:
dp = [1] * n
#初始化
#右和上
for i in range(1, m):
for j in range(1, n):
dp[j] += dp[j-1]
return dp[-1]
和上一道题一样利用滚动数组,要注意的点就是初始化条件不同。
class Solution:
def uniquePathsWithObstacles(self, obstacleGrid: List[List[int]]) -> int:
m = len(obstacleGrid)
n = len(obstacleGrid[0])
#一开始就堵了
if obstacleGrid[0][0] == 1:
return 0
dp = [0] * n
#第一行一直往右走,一旦堵了就全堵了
for i in range(n):
if obstacleGrid[0][i] == 0:
dp[i] = 1
else:
break
for i in range(1,m):
#第一列只能往下走,要看上一行第一个走不走得通
dp[0] = dp[0] if obstacleGrid[i][0] == 0 else 0
for j in range(1,n):
#本身堵了就是0,没有就看上和左的路径了
dp[j] = dp[j-1] + dp[j] if obstacleGrid[i][j] == 0 else 0
return dp[-1]
利用中位数的性质,两个数组左边部分之和应该是总元素个数的一半。找到满足这样条件的两个数组的切分位置即可。
class Solution:
def findMedianSortedArrays(self, nums1: List[int], nums2: List[int]) -> float:
m = len(nums1)
n = len(nums2)
# m更小 不会减出0
if m > n:
m,n = n,m
nums1,nums2 = nums2,nums1
mid = (m+n+1)//2
l,r = 0,m
while l <= r:
#i,j是nums1和nums2中左边元素的数量(不是下标)
i = (l+r)//2
j = mid - i
#nums1大了
if i > 0 and nums1[i-1] >nums2[j]:
r = i - 1
#num1小了
elif i < m and nums1[i] < nums2[j-1]:
l = i + 1
#满足条件,开始寻找左右极值
else:
#左最大
if i == 0:
l_max = nums2[j-1]
elif j ==0:
l_max = nums1[i-1]
else:
l_max = max(nums1[i-1],nums2[j-1])
if (m+n)%2:
return l_max
#右最小
if i== m:
r_min= nums2[j]
elif j ==n:
r_min = nums1[i]
else:
r_min = min(nums1[i],nums2[j])
return (l_max + r_min)/2.0
仿真模拟操作
class Solution:
def spiralOrder(self, matrix: List[List[int]]) -> List[int]:
if not matrix:
return []
#当前未遍历到的矩阵边界
l, r, t, b, res = 0, len(matrix[0]) - 1, 0, len(matrix) - 1, []
while True:
#顶上一行
for i in range(l, r + 1):
res.append(matrix[t][i]) # left to right
t += 1
if t > b: break
#右边一列
for i in range(t, b + 1):
res.append(matrix[i][r]) # top to bottom
r -= 1
if l > r: break
#底下一行
for i in range(r, l - 1, -1):
res.append(matrix[b][i]) # right to left
b -= 1
if t > b: break
#左边一列
for i in range(b, t - 1, -1):
res.append(matrix[i][l]) # bottom to top
l += 1
if l > r: break
return res
常规的动态规划问题
class Solution:
def fib(self, N: int) -> int:
d0 = 0
d1 = 1
for i in range(N):
d0,d1= d1,d0+d1
return d0
在正常的层次遍历基础上,根据层数做判断结果是从左到右还是从右到左保存。
class Solution:
def zigzagLevelOrder(self, root: TreeNode) -> List[List[int]]:
ans = list()
if root:
#遍历用queue
queue = collections.deque()
queue.append(root)
start_left = True
level = collections.deque()
while queue:
length = len(queue)
for i in range(length):
node = queue.popleft()
#当前层结果保存方向
if not start_left:
level.appendleft(node.val)
else:
level.append(node.val)
if node.left:
queue.append(node.left)
if node.right:
queue.append(node.right)
start_left = not start_left
ans.append(list(level))
level.clear()
return ans
利用快慢指针,查看其是否相遇
class Solution:
def hasCycle(self, head: ListNode) -> bool:
p1 = p2 = head
while p2 != None and p2.next != None:
p1 = p1.next
p2 = p2.next.next
if p1 == p2:
break
else:
return False
return True
两个链表拼起来遍历,有公共节点则会重合。
class Solution:
def getIntersectionNode(self, headA: ListNode, headB: ListNode) -> ListNode:
p1 = headA
p2 = headB
while p1 != p2:
p1 = p1.next if p1 else headB
p2 = p2.next if p2 else headA
return p1
排序后检查最大最小牌差值是多少即可。
class Solution:
def isStraight(self, nums: List[int]) -> bool:
joker = 0
nums.sort() # 数组排序
for i in range(4):
if nums[i] == 0: joker += 1 # 统计大小王数量
elif nums[i] == nums[i + 1]: return False # 若有重复,提前返回 false
return nums[4] - nums[joker] < 5 # 最大牌 - 最小牌 < 5 则可构成顺子
用前序找到树的根切分中序中左右子树节点,再递归构建。
class Solution:
def buildTree(self, preorder, inorder):
if len(inorder) == 0:
return None
# 前序遍历第一个值为根节点
root = TreeNode(preorder[0])
mid = inorder.index(preorder[0])
root.left = self.buildTree(preorder[1:mid+1], inorder[:mid])
root.right = self.buildTree(preorder[mid+1:], inorder[mid+1:])
return root
利用快排里的快速划分寻找
class Solution:
def findKthLargest(self, nums: List[int], k: int) -> int:
def partition(l,r):
pivot = (l+r)//2
p = nums[pivot]
nums[r],nums[pivot] = nums[pivot],nums[r]
store = l
for i in range(l,r):
if nums[i] < p:
nums[store],nums[i] = nums[i],nums[store]
store += 1
nums[r],nums[store] = nums[store],nums[r]
return store
def select(l,r,k):
#k为第k小
if l == r:
return nums[l]
pivot = partition(l, r
if k == pivot:
return nums[k]
elif k < pivot:
return select(l, pivot - 1, k)
else:
return select(pivot + 1, r, k)
return select(0, len(nums) - 1, len(nums) - k)
递归解决,要注意的就是某些只有一个儿子的节点递归时会出现某一边深度为0需要判断。
class Solution:
def minDepth(self, root):
if not root:
return 0
l = self.minDepth(root.left)
r = self.minDepth(root.right)
#判断是不是只有一个儿子的节点
if not all([l,r]) and any([l,r]):
return max(l,r) + 1
else:
return min(l,r) + 1
利用BFS带一个时间标记完成。
class Solution(object):
def orangesRotting(self, grid):
R, C = len(grid), len(grid[0])
#队列保存腐烂位置和时间
queue = collections.deque()
for r, row in enumerate(grid):
for c, val in enumerate(row):
if val == 2:
queue.append((r, c, 0))
def neighbors(r, c):
for nr, nc in ((r-1,c),(r,c-1),(r+1,c),(r,c+1)):
if 0 <= nr < R and 0 <= nc < C:
yield nr, nc
d = 0
while queue:
r, c, d = queue.popleft()
for nr, nc in neighbors(r, c):
if grid[nr][nc] == 1:
grid[nr][nc] = 2
#时间为源头腐烂时间+1
queue.append((nr, nc, d+1))
if any(1 in row for row in grid):
return -1
return d
利用二叉搜索树的性质,右子树节点都比他大,计数时减去,直到计数器为0返回根节点。
class Solution:
def kthLargest(self, root: TreeNode, k: int) -> int:
def dfs(root):
if not root:
return
dfs(root.right)
if self.k == 0:
return
self.k -= 1
if self.k == 0:
self.res = root.val
dfs(root.left)
self.k = k
dfs(root)
return self.res
常规的二分搜索变形
class Solution:
def search(self, nums: List[int], target: int) -> int:
if not nums:
return -1
l = 0
r = len(nums) - 1
while l <= r:
mid = (l+r)//2
if nums[mid] == target:
return mid
#前半有序
if nums[l] <= nums[mid]:
if nums[l] <= target < nums[mid]:
r = mid - 1
else:
l = mid + 1
else:
if nums[mid] < target <= nums[r]:
l = mid + 1
else:
r = mid - 1
return -1
因为是有序的数组,平方和大的数只会出现在两侧,所以需要从两侧向中间遍历。
class Solution:
def sortedSquares(self, A: List[int]) -> List[int]:
l = 0
r = len(A) - 1
res = []
while l <= r:
x1 = A[l] ** 2
x2 = A[r] ** 2
if x1 > x2:
res.append(x1)
l += 1
else:
res.append(x2)
r -= 1
return res[::-1]
双指针从两头向中间遍历。
class Solution:
def reverseString(self, s: List[str]) -> None:
l = 0
r = len(s) - 1
while l < r:
s[l],s[r] = s[r],s[l]
l += 1
r -= 1
反转后半链表进行遍历对比
class Solution:
def isPalindrome(self, head: ListNode) -> bool:
if head is None:
return True
#寻找中间位置
first_position = self.end_of_first_half(head)
second_position = self.reverse_list(first_position.next)
# 遍历搜索
result = True
#注意我们的分割方式下,多余的一个节点会在前半段,所以判断后半段是否遍历完
while result and second_position is not None:
if first_position.val != second_position.val:
result = False
first_position = first_position.next
second_position = second_position.next
return result
def end_of_first_half(self, head):
fast = head
slow = head
while fast.next and fast.next.next:
fast = fast.next.next
slow = slow.next
return slow
def reverse_list(self, head):
previous = None
current = head
while current:
next_node = current.next
current.next = previous
previous = current
current = next_node
return previous
这里面的题比较偏门。。大家看看就行
def find_kth_number(n, k):
cur = 1
k-=1
while k>0:
step = 0
#当前搜索边界
first = cur
last = cur + 1
while first <=n:
#cur开头的1位数,2位数……
step += min(n + 1, last) - first
first *= 10
last *= 10
if step<=k:
#头位数字+1
cur += 1
k-=step
else:
#头位数字定下来了,寻找下一位数
cur *= 10
k-=1
return cur
n,m = list(map(int,input().split()))
print(find_kth_number(n, m))
排序后使用暴力搜索
n=int(input())
d=list(map(int,input().split()))
d.sort()
def increas_num(d,n):
num = 0
i = 0
while i < n:
#这一道题需要加2个
if i == n - 1 or d[i + 1] - d[i] > 20:
num += 2
i += 1
#这一道题后面可以做第三题
elif d[i+1] - d[i] > 10:
num += 1
i += 2
else:
#i后面一道满足,再后面一道不满足
if i + 1 == n - 1 or d[i+2] - d[i+1] > 10:
num += 1
i += 2
else:
#连续3道满足
i += 3
return num
print(increas_num(d,n))
n = int(input())
h = list(map(int,input().split()))
e = 0
for i in reversed(range(n)):
e = (h[i] + e + 1)//2
print(e)
因为硬币之间是可以替代的,所以直接用贪心思想就可以。
n = 1024 - int(input())
res = 0
while n:
if n >= 64:
res += n//64
n = n%64
elif n >= 16:
res += n//16
n = n %16
elif n >= 4:
res += n//4
n = n %4
else:
res += n
n = 0
print(res)
link.
可以当作图论最小环问题求解。
n = int(input())
m = [[]*n for _ in range(n)]
for i in range(n):
m[i] = list(map(int,input().split()))
V = 1 << (n-1) #从左至右每一位二进制代表第i个城市是否被访问 如1000代表,第一个城市被访问,而其他城市没有
dp = [[float("inf")] * V for i in range(n)] # dp[i][j]:从节点i只经过集合j所有点再回到0点所需要的最小开销
for i in range(n):
dp[i][0] = m[i][0]
for j in range(1,V):
#为了顺利分解子问题,第一重循环从J开始,相当于每轮考虑一个点的集合,后续逐次增加点
for i in range(n):
for k in range(1,n): #能不能先到k城市
if (j >> (k-1) & 1) == 1: #可以途径k
dp[i][j] = min(dp[i][j], m[i][k] + dp[k][j ^ (1 << (k-1))])
#从0出发,经过所有点,再回到0的费用
print(dp[0][(1 << (n-1)) - 1])
link.
利用字典记录出现次数即可
n = int(input())
while n > 0:
m = int(input())
res = 1
d = {}
for i in range(m):
l = list(input().split())
k = int(l[0])
tmp_d = {}
for j in range(k):
index = (l[2 * j + 1],l[2 * j + 2])
if index in d:
tmp_d[index] = d[index] + 1
res = max(res, tmp_d[index])
else:
tmp_d[index] = 1
d = tmp_d
print(res)
n -= 1