面试题03. 数组中重复的数字
#法一#使用Counter统计,找到重复即可
from collections importCounterclassSolution:def findRepeatNumber(self, nums: List[int]) ->int:
c=Counter(nums)return c.most_common()[0][0]
#法二#哈希法 时间O(n)空间O(n),如果字典中有记录,那么直接返回
classSolution:def findRepeatNumber(self, nums: List[int]) ->int:
repeat_dict={}for num innums:if num not inrepeat_dict:
repeat_dict[num]= 1
else:return num
#法三,评论区获得#原地哈希 时间O(n)空间O(1)
classSolution:def findRepeatNumber(self, nums) ->int:for i inrange(len(nums)):while i !=nums[i]:if nums[i] ==nums[nums[i]]:returnnums[i]
temp=nums[i]
nums[i], nums[temp]=nums[temp], nums[i]#注意这里不要写成nums[i], nums[nums[i]] = nums[nums[i]], nums[i] Python这么写会有问题
面试题04. 二维数组中的查找
#从右上角开始走,小于目标值行加1,大于目标值列减一,注意边界
classSolution:def findNumberIn2DArray(self, matrix: List[List[int]], target: int) ->bool:if not matrix or notmatrix[0]:returnFalse
column= len(matrix[0])-1row=0
rows= len(matrix) #包括只有一行的特殊情况
if target > matrix[rows-1][column]: #如果比矩阵最大值还大,那么直接返回
returnFalseif target < matrix[0][0]: #如果比矩阵最小值还小,直接返回
returnFalsewhile(row < rows and column >=0):if target == matrix[row][column]: #找到了~
returnTrueelif target > matrix[row][column]: #目标值更大,那么往下走
row += 1
elif target < matrix[row][column]: #目标值更小,那么往左走
column -=1
return False #没找到,返回False
面试题06. 从尾到头打印链表
#法一#递归-时间换空间,不占用额外空间
classSolution:def reversePrint(self, head: ListNode) ->List[int]:if nothead:return[]return self.reversePrint(head.next) + [head.val]
#法二#使用额外空间,空间换时间
classSolution(object):defreversePrint(self, head):""":type head: ListNode
:rtype: List[int]"""res=[]whilehead:
res.append(head.val)
head=head.nextreturn res[::-1]
面试题07. 重建二叉树
需注意边界idx
classSolution:def buildTree(self, preorder: List[int], inorder: List[int]) ->TreeNode:if len(inorder) ==0:returnNone#根节点,前序遍历第一个就是根
root =TreeNode(preorder[0])#获取根节点在 inorder 中的索引
idx =inorder.index(preorder[0])#左子树,边界条件,除去根节点
root.left = self.buildTree(preorder[1:idx+1], inorder[:idx])#右子树,边界条件,除去根节点
root.right = self.buildTree(preorder[idx+1:], inorder[idx+1:])return root
面试题09. 用两个栈实现队列
classCQueue:def __init__(self):
self.stack1=[]
self.stack2=[]def appendTail(self, value: int) ->None:
self.stack1.insert(0, value)returnNonedef deleteHead(self) ->int:if notself.stack1:return -1
returnself.stack1.pop()#Your CQueue object will be instantiated and called as such:#obj = CQueue()#obj.appendTail(value)#param_2 = obj.deleteHead()
面试题10- I. 斐波那契数列
from functools importlru_cacheclassSolution:
@lru_cache(None)#评论获得,不然递归超过最大深度
def fib(self, n: int) ->int:if n<2:returnnreturn (self.fib(n-1)+self.fib(n-2))%1000000007
#动态规划
classSolution:def fib(self, n: int) ->int:
dp={}
dp[0]= 0 #初始条件
dp[1] = 1 #初始条件
for i in range(2, n+1):
dp[i]= dp[i-1] + dp[i-2]return int(dp[n]%(1000000007))
面试题10- II. 青蛙跳台阶问题
dp[0]=1dp[1]=1
面试题11. 旋转数组的最小数字
#二分法#比较右侧值
classSolution:def minArray(self, numbers: List[int]) ->int:
left=0
right= len(numbers)-1
while left
mid= (left+right)//2
if numbers[mid]>numbers[right]:
left= mid+1
elif numbers[mid]
right= mid #注意left和right
else : right -= 1 #相等的情况
return numbers[left] #注意return
面试题12- 矩阵中的路径
#DFS+回溯,参考LeetCode题解
classSolution(object):defexist(self, board, word):""":type board: List[List[str]]
:type word: str
:rtype: bool"""
defdfs(i, j, k):if not 0<=i
if len(word)-1 == k: return True #比较到了最后一个字符
tmp, board[i][j] = board[i][j], '/' #走过的路径用'/'标记
res = dfs(i-1, j, k+1) or dfs(i+1,j, k+1) or dfs(i, j-1, k+1) or dfs(i, j+1, k+1) #分别往左、右、上、下走
board[i][j] = tmp #还原状态
returnresfor i in range(len(board)): #所有格子都走
for j inrange(len(board[0])):if dfs(i, j, 0): returnTruereturn False #不存在路径
面试题13. 机器人的运动范围
DFS, numsum为数位和,往右下方向走
最差情况 时间O(MN),遍历所有
最差情况,空间O(MN)矩阵存储单元格所有索引
DFS
classSolution:def movingCount(self, m: int, n: int, k: int) ->int:defnumsum(x):
s=0whilex:
s+=x%10x=x//10
returnsdefdfs(i,j,si,sj):if not 0<=ik or (i,j) in visited: return0
visited.add((i,j))return 1+dfs(i+1,j,si+numsum(si),sj)+dfs(i,j+1,si,sj+numsum(sj))
visited=set()return dfs(0,0,0,0)
BFS,时空如上
classSolution:def movingCount(self, m: int, n: int, k: int) ->int:
queue, visited,=[(0, 0, 0, 0)], set()whilequeue:
i, j, si, sj=queue.pop(0)if not 0 <= i < m or not 0 <= j < n or k < si + sj or (i, j) in visited: continuevisited.add((i,j))
queue.append((i+ 1, j, si + 1 if (i + 1) % 10 else si - 8, sj))
queue.append((i, j+ 1, si, sj + 1 if (j + 1) % 10 else sj - 8))return len(visited)
面试题14- I. 剪绳子
from collections importdefaultdictclassSolution:def cuttingRope(self, n: int) ->int:
dp=defaultdict(int)
dp[1]=1dp[2]=1
for i in range(3, n+1):for j inrange(i):
dp[i]= max(dp[i], (i-j)*j, j*dp[i-j])returndp[n]#dp[i]维持原状态不剪
#j*(i-j)从j剪一刀后不剪
#j*dp[i-j]从j剪一刀后再剪
动态规划优化,原地 时间O(N)空间O(1),LeetCode题解中获得。
我们发现任何大于 3 的数都可以拆分为数字 1,2,3 的和,且它们对 3 的余数总是 0,1,2,因此我们可以仅用 dp[0],dp[1],dp[2] 表示所有大于 3 的值,这样空间复杂度可降到 O(1)。
classSolution:defcuttingRope(self, n):
dp= [0, 1, 1]for i in range(3, n + 1):
dp[i% 3] = max(max(dp[(i - 1) % 3], i - 1),2 * max(dp[(i - 2) % 3], i - 2),3 * max(dp[(i - 3) % 3], i - 3))return dp[n % 3]
面试题16. 数值的整数次方
#法一#递归形式
classSolution:def myPow(self, x: float, n: int) ->float:if n ==0:return 1
if n < 0: #把负指数转为正,同时x取倒数
return 1 / self.myPow(x, -n)#如果是奇数
if n & 1:return x * self.myPow(x, n - 1)return self.myPow(x * x, n >> 1)
#法二#非递归
classSolution:def myPow(self, x: float, n: int) ->float:if n <0:
x= 1 /x#负数变成正数
n = -n
res= 1
whilen:if n & 1: #如果n为奇数
res *=x
x*=x
n>>= 1
return res
面试题18. 删除链表的节点
#假头 用了额外空间#Definition for singly-linked list.#class ListNode:#def __init__(self, x):#self.val = x#self.next = None
classSolution:def deleteNode(self, head: ListNode, val: int) ->ListNode:
prior= ListNode(-1)
prior.next=head
p=priorwhilehead:if head.val ==val:
prior.next=prior.next.next
head=head.next
prior=prior.nextreturn p.next
#双指针 不用额外空间 时间O(N)
classSolution:def deleteNode(self, head: ListNode, val: int) ->ListNode:if head.val == val: return head.next #判断链表头
pre, cur = head, head.next #双指针
while cur and cur.val !=val:
pre, cur=cur, cur.next
pre.next=cur.nextreturn head
面试题21. 调整数组顺序使奇数位于偶数前面
#评论区的一行大法
return sorted(nums,key=lambda x:1-x%2)
面试题22. 链表中倒数第k个节点
#双指针 快指针先跑k,然后一起跑,慢指针就到k了
classSolution:def getKthFromEnd(self, head: ListNode, k: int) ->ListNode:
fast=headfor i in range(k): #快指针跑到k
fast =fast.nextwhile fast: #快、慢指针一起跑
head =head.next
fast=fast.nextreturn head
面试题24. 反转链表
#法一,常规做法
classSolution:def reverseList(self, head: ListNode) ->ListNode:if not head: returnNone
new= ListNode(head.val) #处理结尾多出的NULL
cur =head.nextwhilecur:
temp=cur.next
cur.next=new
new=cur
cur=tempreturn new
#法二#先存为List,然后重新构造链表
classSolution(object):defreverseList(self, head):""":type head: ListNode
:rtype: ListNode"""res=[]
p= new =ListNode(None)while head: #值存入List
res.append(head.val)
head=head.nextwhile res: #重新构造链表,反向
new.next =ListNode(res.pop())
new=new.nextreturn p.next
面试题25. 合并两个排序的链表
#递归
def mergeTwoLists(self, l1: ListNode, l2: ListNode) ->ListNode:if not l1: #l1为空,直接把l2拼接在后
returnl2if not l2: #l2为空,直接把l1拼接在后
returnl1
pre= None #新链表
if l1.val < l2.val: #谁小把谁拼接在后
pre =l1
pre.next=self.mergeTwoLists(l1.next, l2)else:
pre=l2
pre.next=self.mergeTwoLists(l1, l2.next)return pre
面试题26. 树的子结构
classSolution:def isSubStructure(self, A: TreeNode, B: TreeNode) ->bool:if not A or not B: #题目中说空树不是子结构
returnFalseif A.val != B.val: #如过值不相等,那么A移动比较
return self.isSubStructure(A.left,B) orself.isSubStructure(A.right,B)else: #值相等,开始判断子结构
returnself.helper(A,B)def helper(self, a, b): #判断子结构
if not b: #a分支节点可以大于等于b,但一定不能小于
returnTrueif not a: #如果a为空,b不为空,那么不是子结构,所以一定要先判断b
returnFalseif a.val != b.val: #如果值不等也不是子结构
returnFalsereturn self.helper(a.left, b.left) and self.helper(a.right, b.right) #左和左比较,右和右比较
面试题27. 二叉树的镜像
#法一#递归
classSolution:def mirrorTree(self, root: TreeNode) ->TreeNode:if not root: returnNone
root.left, root.right=self.mirrorTree(root.right), self.mirrorTree(root.left)return root
#法二#迭代-涉及到树的都差不多写法
classSolution:def mirrorTree(self, root: TreeNode) ->TreeNode:if not root: return[]
stack=[root]whilestack:
nextStack=[]whilestack:
node=stack.pop(0)
node.left, node.right=node.right, node.leftifnode.left: nextStack.append(node.left)ifnode.right: nextStack.append(node.right)
stack=nextStackreturn root
面试题28. 对称的二叉树
#递归
classSolution:def isSymmetric(self, root: TreeNode) ->bool:if not root:return True #空树
returnself.helper(root, root)defhelper(self, root1, root2):if not root1 and not root2: #左树为空,右树为空
returnTrueif not root1 and root2: #左树为空,右树非空,不对称
returnFalseif root1 and not root2: #左树非空,右树为空,不对称
returnFalseif root1.val != root2.val: #值不等,也不对称
returnFalsereturn self.helper(root1.left, root2.right) and self.helper(root1.right, root2.left)
面试题29. 顺时针打印矩阵
将矩阵第一行的元素添加到res列表里,删除第一行(也就是matrix中的第一个列表),然后逆时针旋转(这里通过转置+倒序实现),新的matrix的第一行即为接下来要打印的元素。
#LeetCode题解思路,矩阵逆时针旋转
classSolution:def spiralOrder(self, matrix: List[List[int]]) ->List[int]:
res=[]whilematrix:
res+= matrix.pop(0) #弹出最前列表
matrix = list(zip(*matrix))[::-1]return res
面试题30. 包含min函数的栈
classMinStack:def __init__(self):"""initialize your data structure here."""self.stack=[]
self.minStack=[]def push(self, x: int) ->None:
self.stack.append(x)#正常栈,正常压入
if not self.minStack: self.minStack.append(x) #如果辅助栈为空,不比较直接压入
else:if x > self.minStack[-1]: #压入元素和栈顶元素比较,如果压入元素大,那么复制栈顶元素,如果压入元素小,将小元素压入
self.minStack.append(self.minStack[-1])else: self.minStack.append(x)def pop(self) -> None: #两个栈同时弹出
self.stack.pop()
self.minStack.pop()def top(self) ->int:return self.stack[-1]def min(self) ->int:return self.minStack[-1]
面试题31. 栈的压入、弹出序列
初始化栈 stack,j = 0;
遍历 pushed 中的元素 x;
当 j < popped.size() 且栈顶元素等于 popped[j]:
弹出栈顶元素;
j += 1;
如果栈为空,返回 True,否则返回 False。
贪心算法 时间O(N)空间O(N)
classSolution:def validateStackSequences(self, pushed: List[int], popped: List[int]) ->bool:
stack=[]
j=0for x inpushed:
stack.append(x)while stack and j
stack.pop()
j+=1
return not stack
面试题33. 二叉搜索树的后序遍历序列
classSolution:def verifyPostorder(self, postorder: List[int]) ->bool:if not postorder: returnTruedefhelper(nums):if len(nums) <= 1: returnTrue
root= nums[-1]for i inrange(len(nums)):if nums[i] > root: break #取得比根节点大的i值
for j in range(i, len(nums)-1):#除去根节点
if nums[j] < root: returnFalsereturn helper(nums[:i]) and helper(nums[i:-1]) #-1为除去根节点
return helper(postorder)
面试题34. 二叉树中和为某一值的路径
#Definition for a binary tree node.#class TreeNode:#def __init__(self, x):#self.val = x#self.left = None#self.right = None
importcopyclassSolution:def pathSum(self, root: TreeNode, sum_: int) ->List[List[int]]:
res=[]
temp=[]defhelper(root,sum_):if not root: return
if not root.left and not root.right: ##判断是否为叶节点
temp.append(root.val)if sum(temp) == sum_: ## 满足条件,则添加到最终结果中
res.append(copy.deepcopy(temp)) #此处为深拷贝,不然temp的操作会影响res
temp.pop()returntemp.append(root.val)#进栈
helper(root.left, sum_)
helper(root.right, sum_)
temp.pop()#出栈
helper(root, sum_)return res
面试题36. 二叉搜索树与双向链表
"""# Definition for a Node.
class Node:
def __init__(self, val, left=None, right=None):
self.val = val
self.left = left
self.right = right"""
classSolution:def treeToDoublyList(self, root: 'Node') -> 'Node':if not root: return
def change_ptr(root, node): #改造中序遍历
if not root: returnnode
node=change_ptr(root.left, node)
node.right=root
root.left=node
node=root
node=change_ptr(root.right, node)returnnode
visual_head= Node(0) #假结点,用于调用函数
tail =change_ptr(root,visual_head)
head=visual_head.right
head.left=tail
tail.right=headreturn head
面试题38. 字符串的排列
输入一个字符串,打印出该字符串中字符的所有排列。 你可以以任意顺序返回这个字符串数组,但里面不能有重复元素。
示例: 输入:s = "abc" 输出:["abc","acb","bac","bca","cab","cba"]
classSolution:def permutation(self, s: str) ->List[str]:
c, res=list(s), []defdfs(x):if x == len(c) - 1:
res.append(''.join(c)) #添加排列方案
returndic=set()for i inrange(x, len(c)):if c[i] in dic: continue #重复,因此剪枝
dic.add(c[i])
c[i], c[x]= c[x], c[i] #交换,固定此位为 c[i]
dfs(x + 1) #开启固定第 x + 1 位字符
c[i], c[x] = c[x], c[i] #恢复交换
dfs(0)return res
面试题39. 数组中出现次数超过一半的数字
数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。 你可以假设数组是非空的,并且给定的数组总是存在多数元素。
示例 1: 输入: [1, 2, 3, 2, 2, 2, 5, 4, 2] 输出: 2
法一:需要的数字出现次数多于一半 那么排序后必定在中间, 时间O(nlogn),空间O(1)
classSolution:def majorityElement(self, nums: List[int]) ->int:
nums=sorted(nums)return nums[len(nums)//2]
法二:字典统计:略
法三:摩尔投票法 时间O(n),空间O(1)
也可以理解成混战极限一换一,不同的两者一旦遇见就同归于尽,最后活下来的值都是相同的,即要求的结果
classSolution:def majorityElement(self, nums: List[int]) ->int:
vote=0for num innums:if vote == 0: x=num #注意这里没有else,保证vote=1
if x==num:
vote+=1
else:
vote-=1
return x
面试题41. 数据流中的中位数
如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。
例如, [2,3,4] 的中位数是 3 [2,3] 的中位数是 (2 + 3) / 2 = 2.5 设计一个支持以下两种操作的数据结构:
void addNum(int num) - 从数据流中添加一个整数到数据结构中。
double findMedian() - 返回目前所有元素的中位数。
示例 1:
输入: ["MedianFinder","addNum","addNum","findMedian","addNum","findMedian"] [[],[1],[2],[],[3],[]] 输出:[null,null,null,1.50000,null,2.00000] 示例 2:
输入: ["MedianFinder","addNum","findMedian","addNum","findMedian"] [[],[2],[],[3],[]] 输出:[null,null,2.00000,null,2.50000]
常规做法,因为排序时间nlogn 空间n
classMedianFinder:def __init__(self):"""initialize your data structure here."""self.store=[]def addNum(self, num: int) ->None:
self.store.append(num)def findMedian(self) ->float:
self.store.sort()
n=len(self.store)if n & 1 == 1: #n 是奇数
return self.store[n // 2]else:return (self.store[n // 2 - 1] + self.store[n // 2]) / 2
用堆,Python只有小顶堆,值取负来模拟大顶堆
classMedianFinder:def __init__(self):"""initialize your data structure here."""
#初始化大顶堆和小顶堆
self.max_heap =[]
self.min_heap=[]def addNum(self, num: int) ->None:if len(self.max_heap) == len(self.min_heap):#先加到大顶堆,再把大堆顶元素加到小顶堆
heapq.heappush(self.min_heap, -heapq.heappushpop(self.max_heap, -num))else: #先加到小顶堆,再把小堆顶元素加到大顶堆
heapq.heappush(self.max_heap, -heapq.heappushpop(self.min_heap, num))def findMedian(self) ->float:if len(self.min_heap) ==len(self.max_heap):return (-self.max_heap[0] + self.min_heap[0]) / 2
else:return self.min_heap[0]
面试题42. 连续子数组的最大和
输入一个整型数组,数组里有正数也有负数。数组中的一个或连续多个整数组成一个子数组。求所有子数组的和的最大值。
要求时间复杂度为O(n)。
示例1:
输入: nums = [-2,1,-3,4,-1,2,1,-5,4]
输出: 6
解释: 连续子数组 [4,-1,2,1] 的和最大,为 6。
动态规划解析:
状态定义: 设动态规划列表 dp ,dp[i] 代表以元素 nums[i] 为结尾的连续子数组最大和。
为何定义最大和 dp[i] 中必须包含元素 nums[i] :保证 dp[i] 递推到 dp[i+1] 的正确性;如果不包含 nums[i] ,递推时则不满足题目的 连续子数组 要求。
转移方程: 若 dp[i−1]≤0 ,说明dp[i−1] 对 dp[i] 产生负贡献,即 dp[i-1] + nums[i] 还不如 nums[i]本身大。
当 dp[i - 1] > 0 时:执行 dp[i] = dp[i-1] + nums[i] ;
当 dp[i − 1] ≤ 0 时:执行 dp[i] = nums[i] ;
初始状态: dp[0] = nums[0],即以 nums[0] 结尾的连续子数组最大和为 nums[0] 。
返回值: 返回 dp 列表中的最大值,代表全局最大值。
classSolution:def maxSubArray(self, nums: List[int]) ->int:for i in range(1, len(nums)):
nums[i]+= max(nums[i - 1], 0)return max(nums)