更新中…
时间复杂度O(n^2)
def bubble_sort(arr):
length = len(arr)
if length < 2:
return arr
for i in range(length):
for j in range(i, length):
if arr[j] < arr[i]:
arr[j], arr[i] = arr[i], arr[j]
return arr
时间复杂度O(nlogn), 最差O(n^2)(如何避免最差?最差是因为每次递归没能平均二分,那么需要选定合适的pivot,使得每次递归尽量二分,可以采取random方式)
空间复杂度O(nlogn)
quick_sort_lam = lambda array: array if len(array) <= 1 else \
quick_sort_lam([item for item in array[1:] if item <= array[0]]) \
+ [array[0]] + \
quick_sort_lam([item for item in array[1:] if item > array[0]])
def quick_sort(array, left, right):
if left >= right:
return
low = left
high = right
key = array[low]
while left < right:
while left < right and array[right] >= key:
right -= 1
array[left], array[right] = array[right], array[left]
while left < right and array[left] <= key:
left += 1
array[right], array[left] = array[left], array[right]
quick_sort(array, low, left - 1)
quick_sort(array, left + 1, high)
堆调整时间复杂度O(logn)
堆排序时间复杂度O(nlogn)
应用1: 大数据区topk
应用2: 数据流求中位数
"""
堆排序算法的步骤:
1. 把无序数组构建成二叉堆。
2. 循环删除堆顶元素,移到集合尾部,调节堆产生新的堆顶。
大根堆
"""
def downAdjustBig(array, parentIndex, length):
temp = array[parentIndex]
childIndex = 2 * parentIndex + 1
while (childIndex < length):
if childIndex + 1 < length and array[childIndex + 1] > array[childIndex]:
childIndex += 1
if temp >= array[childIndex]:
break
array[parentIndex] = array[childIndex]
parentIndex = childIndex
childIndex = 2 * childIndex + 1
array[parentIndex] = temp
def heapSort(array):
# 1.构建二叉堆
for i in range(int(len(array) / 2))[::-1]:
downAdjustBig(array, i, len(array))
print(array)
# 2.循环删除堆顶元素,移到集合尾部,调节堆产生新的堆顶
for i in range(len(array))[::-1]:
array[i], array[0] = array[0], array[i]
downAdjustBig(array, 0, i)
print(array)
广度 & 深度
# 广度优先遍历算法
def tree_level_traversal(root):
if root is None:
return
my_queue = collections.deque()
node = root
my_queue.append(node)
while my_queue:
node = my_queue.popleft()
print(node.val)
if node.left:
my_queue.append(node.left)
if node.right:
my_queue.append(node.right)
# 深度优先遍历算法
def tree_dfs_traversal(tree_node):
if tree_node:
print(tree_node.val)
if tree_node.left:
tree_dfs_traversal(tree_node.left)
if tree_node.right:
tree_dfs_traversal(tree_node.right)
二叉树 的 三种遍历方式(前中后) 的 非递归解法
"""
当前结点curr不为None时,每一次循环将当前结点curr入栈;
当前结点curr为None时,则出栈一个结点,且打印出栈结点的value值。
整个循环在stack和curr皆为None的时候结束。
"""
def inorderTraversal(root):
stack = []
res = []
curr = root
while stack or curr:
if curr:
stack.append(curr)
curr = curr.left
else:
curr = stack.pop()
res.append(curr.val)
curr = curr.right
return res
"""
由于前序遍历的顺序是中左右,所以我们每次先打印当前结点curr,并将右子结点push到栈中,然后将左子结点设为当前结点。
入栈和出栈条件(当前结点curr不为None时,每一次循环将当前结点curr入栈;
当前结点curr为None时,则出栈一个结点)以及循环结束条件
(整个循环在stack和curr皆为None的时候结束)与中序遍历一模一样。
"""
def preorderTraversal(root): ## 前序遍历
stack = []
res = []
curr = root
while stack or curr:
if curr:
res.append(curr.val)
stack.append(curr.right)
curr = curr.left
else:
curr = stack.pop()
return res
"""
代码的主体部分基本就是.right和.left交换了顺序,
且后序遍历在最后输出的时候进行了反向(因为要从 中右左 变为 左右中 )
"""
def postorderTraversal(root): ## 后序遍历
stack = []
res = []
curr = root
while stack or curr:
if curr:
res.append(curr.val)
stack.append(curr.left)
curr = curr.right
else:
curr = stack.pop()
return res[::-1]
# 前序 中序 构建树
def getTreeFromPreMid(pre, mid):
if len(pre) == 0:
return None
if len(pre) == 1:
return TreeNode(pre[0])
root = TreeNode(pre[0])
root_index = mid.index(pre[0])
root.left = getTreeFromPreMid(pre[1:root_index + 1], mid[:root_index])
root.right = getTreeFromPreMid(pre[root_index + 1:], mid[root_index + 1:])
return root
# 前序 中序 构建后序
def getAfterFromPreMid(pre, mid, res):
if len(pre) == 0:
return
if len(pre) == 1:
res.append(pre[0])
return
root = pre[0]
root_index = mid.index(root)
getAfterFromPreMid(pre[1:root_index + 1], mid[:root_index], res)
getAfterFromPreMid(pre[root_index + 1:], mid[root_index + 1:], res)
res.append(root)
return res
lowest common ancestor (LCA)
def lca(root, p, q):
if p is None or q is None:
return root
# dfs查找根节点到两个节点的路径
def dfs(node, visited, res):
if node is None:
return
path = visited + [node.val]
if node == p or node == q:
res.append(path)
dfs(node.left, path, res)
dfs(node.right, path, res)
res = []
visited = []
dfs(root, visited, res)
# 得到两条路径 --> res[0] res[1], 找最近的公共父节点
i = 0
for i in range(min(len(res[0]), len(res[1]))):
if res[0][i] == res[1][i]:
i += 1
else:
return res[0][i-1]
return res[0][i-1]
BST的LCA
"""
Given a binary search tree (BST), find the lowest common ancestor (LCA) of two given nodes in the BST.
_______6______
/ \
___2__ ___8__
/ \ / \
0 _4 7 9
/ \
3 5
For example, the lowest common ancestor (LCA) of nodes 2 and 8 is 6. Another example is LCA of nodes 2 and 4 is 2, since a node can be a descendant of itself according to the LCA definition.
"""
"""
注意这里是一个二叉搜索树,根结点的右子树上所有的点的值都比根结点大,左子树上所有点的值都比根结点的值小
因此分为四种情况,
1、如果两个节点一个值比根节点大,一个比根节点小,那么二者的公共节点肯定是根结点,
2、如果两个节点中有一个与根结点的值同样大,那么二者的公共节点同样是根结点
3、如果两个节点的值都比根结点小,那么二者的公共节点出现在根结点的左子树中,递归查询
4、如果两个节点的值都比根结点大,那么二者的公共节点出现在根结点的右子树中,递归查询
"""
def lowestCommonAncestor_BST(root, p, q):
"""
:type root: TreeNode
:type p: TreeNode
:type q: TreeNode
:rtype: TreeNode
"""
if (p.val - root.val) * (q.val - root.val) <= 0:
return root
if p.val < root.val and q.val < root.val:
return lowestCommonAncestor_BST(root.left, p, q)
if p.val > root.val and q.val > root.val:
return lowestCommonAncestor_BST(root.right, p, q)
给定 起止节点 求搜索路径问题 & Graph的遍历
# -*- coding:UTF-8 -*-
def find_all_paths_dfs(graph, start, end, path=[]):
"""
返回所有路径DFS
:param graph:
:param start:
:param end:
:param path:
:return:
"""
path = path + [start]
if start == end:
return [path]
if start not in graph:
return []
paths = []
for node in graph[start]:
if node not in path:
newpaths = find_all_paths_dfs(graph, node, end, path)
for newpath in newpaths:
paths.append(newpath)
return paths
def find_all_paths_bfs(graph, start, end):
"""
返回所有路径BFS
:param graph:
:param start:
:param end:
:return:
"""
paths = []
todo = [[start, [start]]]
while 0 < len(todo):
(node, path) = todo.pop(0)
for next_node in graph[node]:
if next_node in path:
continue
elif next_node == end:
paths.append(path + [next_node])
else:
todo.append([next_node, path + [next_node]])
return paths
def recursive_dfs(graph, start, path=[]):
"""
dfs遍历 递归形式
:param graph:
:param start:
:param path:
:return:
"""
path = path + [start]
for node in graph[start]:
if not node in path:
path = recursive_dfs(graph, node, path)
return path
def iterative_dfs(graph, start, path=[]):
'''
dfs遍历 非递归形式
:param graph:
:param start:
:param path:
:return:
'''
q = [start]
while q:
v = q.pop(0)
if v not in path:
path = path + [v]
q = graph[v] + q
return path
def iterative_bfs(graph, start, path=[]):
'''
bfs遍历 非递归形式
:param graph:
:param start:
:param path:
:return:
'''
q = [start]
while q:
v = q.pop(0)
if not v in path:
path = path + [v]
q = q + graph[v]
return path
if __name__ == '__main__':
'''
+---- A
| / \
| B--D--C
| \ | /
+---- E
'''
graph = {'A': ['B', 'C'],
'B': ['D', 'E'],
'C': ['D', 'E'],
'D': ['E'],
'E': ['A']}
print('recursive dfs: ', recursive_dfs(graph, 'A'))
print('iterative dfs: ', iterative_dfs(graph, 'A'))
print('iterative bfs: ', iterative_bfs(graph, 'A'))
print("##" * 20)
print('find_all_paths: ', find_all_paths_dfs(graph, 'A', 'E'))
print("##" * 20)
for path in find_all_paths_bfs(graph, 'A', 'E'):
print(path)
class ListNode(object):
def __init__(self, x):
self.val = x
self.next = None
class Solution(object):
def reverseList(self, head):
"""
:type head: ListNode
:rtype: ListNode
"""
if not head:
return None
p = head
q = head.next
while q:
head.next = q.next
q.next = p
p = q
q = head.next
return p
if __name__ == '__main__':
head = ListNode(0)
node1 = ListNode(1)
node2 = ListNode(2)
node3 = ListNode(3)
node4 = ListNode(4)
node5 = ListNode(5)
node6 = ListNode(6)
head.next = node1
node1.next = node2
node2.next = node3
node3.next = node4
node4.next = node5
node5.next = node6
p = Solution().reverseList(head)
while p:
print(p.val)
p = p.next
"""
第一步,找环中相汇点。分别用p1,p2指向链表头部,p1每次走一步,p2每次走二步,直到p1==p2找到在环中的相汇点。
第二步,找环的入口。接上步,当p1==p2时,p2所经过节点数为2x,p1所经过节点数为x,设环中有n个节点,p2比p1多走一圈有2x=n+x; n=x;
可以看出p1实际走了一个环的步数,再让p2指向链表头部,p1位置不变,p1,p2每次走一步直到p1==p2; 此时p1指向环的入口。
"""
def EntryNodeOfLoop(self, pHead):
# write code here
if pHead is None:
return None
pLeft = pHead
pRight = pHead
circle_cnt = 0
while pRight and pRight.next:
pLeft = pLeft.next
pRight = pRight.next.next
circle_cnt += 1
if pLeft.val == pRight.val:
pRight = pHead
left_cnt = 0
while pLeft != pRight:
pLeft = pLeft.next
pRight = pRight.next
left_cnt += 1
if pLeft == pRight:
return pLeft, circle_cnt, left_cnt
return None
动态规划:
- 自底向上
就是已经知道了所有递归边界,把所有可能的状态都算出来。
从初始已知的状态出发,向外拓展,最后到达目标状态。- 自顶向下(“记忆化搜索”)
从最终状态开始,找到可以到达当前状态的状态,如果该状态还没处理,就先处理该状态。
自顶向下通常使用递归实现- 总结
自顶向下,自底向上,只是动态规划实现的套路而已。复杂度并没有多大的变化。
事实上大多时候用自顶向下复杂度会更低,因为可以过滤掉更多无用的状态;
不过自底向上可以避免爆栈问题,而且实现往往实现更为简单。
def coins_min_combine(arr, target):
if target <= 0 or target > 1024:
raise ValueError
dp = [0 for i in range(target + 1)]
for i in range(1, target + 1):
c = sys.maxsize
for coin in arr:
if i - coin >= 0:
c = min(c, dp[i - coin] + 1)
dp[i] = c
return dp[-1]
print(coins_min_combine([1, 5, 11], 15))
def coin_ways(arr, target):
"""
给定一个正数数组arr,arr[i]表示第i种货币的面值,可以使用任意张。
给定一个正 target,返回组成aim的方法数有多少种?
动态规划优化状态依赖的技巧
:return:
"""
if len(arr) < 1 or target < 0:
return 0
return process(arr, 0, target)
def process(arr, index, target):
res = 0
if index == len(arr):
res = 1 if target == 0 else 0
else:
i = 0
while arr[index] * i <= target:
res += process(arr, index + 1, target - arr[index] * i)
i += 1
return res
def coin_ways_dp_compress(arr, target):
"""
给定一个正数数组arr,arr[i]表示第i种货币的面值,可以使用任意张。
给定一个正 target,返回组成aim的方法数有多少种?
动态规划优化状态依赖的技巧
:return:
"""
if len(arr) < 1 or target < 0:
return 0
dp = [0 for i in range(target + 1)]
j = 0
while arr[0] * j <= target:
dp[arr[0] * j] = 1
j += 1
for i in range(1, len(arr)):
for j in range(1, target + 1):
dp[j] += dp[j - arr[i]] if j - arr[i] >= 0 else 0
print(dp)
return dp[target]
# 编辑距离
def levenshtein_distance_dp(input_x, input_y):
xlen = len(input_x) + 1
ylen = len(input_y) + 1
# 此处需要多开辟一个元素存储最后一轮的计算结果
dp = [[0 for i in range(xlen)] for j in range(ylen)]
for i in range(xlen):
dp[i][0] = i
for j in range(ylen):
dp[0][j] = j
for i in range(1, xlen):
for j in range(1, ylen):
if input_x[i - 1] == input_y[j - 1]:
dp[i][j] = dp[i - 1][j - 1]
else:
dp[i][j] = 1 + min(dp[i - 1][j], dp[i][j - 1], dp[i - 1][j - 1])
return dp[xlen - 1][ylen - 1]
# 最长公共子串
def longest_common_substr_dp(str1, str2):
xlen = len(str1) + 1
ylen = len(str2) + 1
record = [[0 for i in range(ylen)] for j in range(xlen)]
maxNum = 0 # 最长匹配长度
p = 0 # 匹配的起始位
for i in range(1, xlen):
for j in range(1, ylen):
if str1[i - 1] == str2[j - 1]:
# 相同则累加
record[i][j] = record[i - 1][j - 1] + 1
if record[i][j] > maxNum:
# 获取最大匹配长度
maxNum = record[i][j]
# 记录最大匹配长度的终止位置
p = i
for i in record:
print(i)
return str1[p - maxNum:p], maxNum
# 最长公共子序列
def longest_common_sequence(input_x, input_y):
lcsequence_mat, flag = longest_common_sequence_dp(input_x, input_y)
i = len(input_x)
j = len(input_y)
lcs = []
get_lcs(input_x, input_y, i, j, flag, lcs)
print((lcsequence_mat[-1][-1], lcs))
def longest_common_sequence_dp(input_x, input_y):
xlen = len(input_x) + 1
ylen = len(input_y) + 1
dp = [([0] * ylen) for i in range(xlen)]
flag = [([0] * ylen) for i in range(xlen)]
for i in range(1, xlen):
for j in range(1, ylen):
if input_x[i - 1] == input_y[j - 1]: # 不在边界上,相等就加一
dp[i][j] = dp[i - 1][j - 1] + 1
flag[i][j] = 0
elif dp[i - 1][j] > dp[i][j - 1]: # 不相等
dp[i][j] = dp[i - 1][j]
flag[i][j] = 1
else:
dp[i][j] = dp[i][j - 1]
flag[i][j] = -1
for dp_line in dp:
print(dp_line)
return dp, flag
def get_lcs(input_x, input_y, i, j, flag, lcs):
if (i == 0 or j == 0):
return
if flag[i][j] == 0:
get_lcs(input_x, input_y, i - 1, j - 1, flag, lcs)
lcs.append(input_x[i - 1])
elif (flag[i][j] == 1):
get_lcs(input_x, input_y, i - 1, j, flag, lcs)
else:
get_lcs(input_x, input_y, i, j - 1, flag, lcs)
return lcs
在这里插入代码片
def perm_str(s=''):
if len(s) <= 1:
return [s]
str_list = []
for i in range(len(s)):
for j in perm_str(s[0:i] + s[i + 1:]):
str_list.append(s[i] + j)
return str_list
# -*- coding: utf-8 -*-
"""
@Time : 2019/8/22 2:35 PM
@Author : ddlee
@File : combinations.py
"""
import itertools
# itertools.combinations()
def combinations(iterable, r):
pool = tuple(iterable)
n = len(pool)
if r > n:
return
indices = list(range(r))
yield tuple(pool[i] for i in indices)
while True:
for i in reversed(range(r)):
if indices[i] != i + n - r:
break
else:
return
indices[i] += 1
for j in range(i + 1, r):
indices[j] = indices[j - 1] + 1
yield tuple(pool[i] for i in indices)
def combine(n, k):
def backtrack(first=1, curr=[]):
# if the combination is done
if len(curr) == k:
res.append(curr.copy())
for i in range(first, n + 1):
# add i into the current combination
curr.append(i)
# use next integers to complete the combination
backtrack(i + 1, curr)
# backtrack
curr.pop()
res = []
backtrack()
return res
if __name__ == '__main__':
# combinations('ABCD', 2) --> AB AC AD BC BD CD
# combinations(range(4), 3) --> 012 013 023 123
for i in combinations(range(1, 5), 2):
print(i)
print(combine(n=4, k=2))
def josephus_formula(n, m):
"""
:param n: 总人数
:param m: 每数到m去除
:return: 返回最后一个人的下标
"""
if n == 1:
return 0
else:
return (josephus_formula(n - 1, m) + m) % n
def josephus_mimic(n, k):
link = list(range(1, n + 1))
ind = 0
for loop_i in range(n - 1):
ind = (ind + k) % len(link)
ind -= 1
print('Kill:', link[ind])
del link[ind]
if ind == -1: # the last element of link
ind = 0
print('survice :', link[0])
def josephus(n, m):
"""
Args:
n: n people
m: count m del
Returns: last survive
"""
people = list(range(1, n + 1))
idx = 0
last = 0
for i in range(n):
cnt = 0
while cnt < m:
if people[idx] != -1:
cnt += 1
if cnt == m:
# print(people[idx])
last = people[idx]
people[idx] = -1
idx = (idx + 1) % n
return last
if __name__ == '__main__':
n = 3
m = 2
ans = josephus(n, m)
print(ans)
Given a function rand7 which generates a uniform random integer in the range 1 to 7, write a function rand10 which generates a uniform random integer in the range 1 to 10.
Do NOT use system’s Math.random().
推导:
给定1-a的随机数生成器,产生1-b的随机数生成器
# -*- coding: utf-8 -*-
"""
@Time : 2019/4/22 9:05 PM
@Author : ddlee
@File : 470rand10.py
"""
import random
class Solution:
def rand10(self, n):
"""
:rtype: int
"""
def rand7():
return random.randint(1, 7)
res = []
for i in range(n):
x = 7 * (rand7() - 1) + rand7()
while x > 40:
x = 7 * (rand7() - 1) + rand7()
rnd = x % 10 + 1
res.append(rnd)
return res
if __name__ == '__main__':
n = 1000000
res = Solution().rand10(n)
cnt = [0 for i in range(10)]
for i in res:
cnt[i-1] += 1
print(cnt)
# -*- coding:UTF-8 -*-
"""
将非负整数转换为其对应的英文表示。可以保证给定输入小于 231 - 1 。
示例 1:
输入: 123
输出: "One Hundred Twenty Three"
示例 2:
输入: 12345
输出: "Twelve Thousand Three Hundred Forty Five"
"""
class Solution(object):
def numberToWords(self, num):
"""
:type num: int
:rtype: str
"""
d1 = ['', 'One', 'Two', 'Three', 'Four', 'Five', 'Six', 'Seven', 'Eight', 'Nine', 'Ten', 'Eleven', 'Twelve',
'Thirteen', 'Fourteen', 'Fifteen', 'Sixteen', 'Seventeen', 'Eighteen', 'Nineteen', 'Twenty']
d2 = ['', 'Ten', 'Twenty', 'Thirty', 'Forty', 'Fifty', 'Sixty', 'Seventy', 'Eighty', 'Ninety']
if num == 0: return 'Zero'
if num <= 20: return d1[num]
if num < 100:
t, d = num // 10, num % 10
return d2[t] + ' ' + d1[d] if d > 0 else d2[t]
if num < 1000:
h = num // 100
if num % 100 == 0:
return d1[h] + ' Hundred'
return d1[h] + ' Hundred ' + self.numberToWords(num % 100)
if num < 10 ** 6:
th = num // 10 ** 3
if num % 10 ** 3 == 0:
return self.numberToWords(th) + ' Thousand'
return self.numberToWords(th) + ' Thousand ' + self.numberToWords(num % 10 ** 3)
if num < 10 ** 9:
mi = num // 10 ** 6
if num % 10 ** 6 == 0:
return self.numberToWords(mi) + ' Million'
return self.numberToWords(mi) + ' Million ' + self.numberToWords(num % 10 ** 6)
if num < 10 ** 12:
bi = num // 10 ** 9
if num % 10 ** 9 == 0:
return d1[num // 10 ** 9] + ' Billion'
return self.numberToWords(bi) + ' Billion ' + self.numberToWords(num % 10 ** 9)
if __name__ == '__main__':
num = 123456
print(Solution().numberToWords(num))
# -*- coding: utf-8 -*-
"""
@Time : 2019/4/23 3:11 PM
@Author : ddlee
@File : 207toposort.py
"""
import collections
"""
现在你总共有 n 门课需要选,记为 0 到 n-1。
在选修某些课程之前需要一些先修课程。 例如,想要学习课程 0 ,你需要先完成课程 1 ,我们用一个匹配来表示他们: [0,1]
给定课程总量以及它们的先决条件,判断是否可能完成所有课程的学习?
示例 1:
输入: 2, [[1,0],[0,1]]
输出: false
解释: 总共有 2 门课程。学习课程 1 之前,你需要先完成课程 0;并且学习课程 0 之前,你还应先完成课程 1。这是不可能的。
==> 判断是否为有向无环图
"""
class Solution:
def canFinish(self, numCourses, prerequisites):
"""
:type numCourses: int
:type prerequisites: List[List[int]]
:rtype: bool
"""
graph = collections.defaultdict(list)
indegrees = [0] * numCourses
for course, pre in prerequisites:
graph[pre].append(course)
indegrees[course] += 1
return self.topologicalSort(graph, indegrees) == numCourses
def topologicalSort(self, graph, indegrees):
count = 0
queue = []
for i in range(len(indegrees)):
if indegrees[i] == 0:
queue.append(i)
l = []
while queue:
course = queue.pop()
l.append(course)
count += 1
for i in graph[course]:
indegrees[i] -= 1
if indegrees[i] == 0:
queue.append(i)
print(l)
return count
if __name__ == '__main__':
print(Solution().canFinish(4, [[1, 0], [1, 2], [0, 3], [2, 3]]))
给定一个字符串s,你可以从中删除一些字符,使得剩下的串是一个回文串。如何删除才能使得回文串最长
def palindrome_seq(s):
length = len(s)
if length < 2:
return 0
rs = s[::-1]
dp = [[0 for i in range(length + 1)] for j in range(length + 1)]
for i in range(1, length + 1):
for j in range(1, length + 1):
if s[i - 1] == rs[j - 1]:
dp[i][j] = dp[i - 1][j - 1] + 1
else:
dp[i][j] = max(dp[i][j - 1], dp[i - 1][j])
for i in dp:
print(i)
return length - dp[length][length]
def lis2(s):
"""
最长上升子序列,一维存储
:param s:
:return:
"""
length = len(s)
if length < 2:
return length
# 以j结尾的最长上升子序列长度
dp = [1 for i in range(length + 1)]
dp[0] = 0
for i in range(1, length + 1):
for j in range(i, length + 1):
if s[i - 1] < s[j - 1]:
dp[j] = max(dp[i] + 1, dp[j])
return dp
def maxProductAfterCutting(n):
"""
剪绳子, 求乘积最大
:param n:
:return:
"""
if n < 2:
return 0
if n == 2:
return 1
max_list = [0, 1, 2]
for i in range(3, n + 1):
if i < n:
m = i
else:
m = 0
for j in range(1, i // 2 + 1):
tmp = max_list[j] * max_list[i - j]
m = max(m, tmp)
max_list.append(m)
print(max_list)
return max_list[n]
def most_eor(arr):
"""
给出n个数字 a_1,...,a_n,问最多有多少不重叠的非空区间,使得每个区间内数字的 xor都等于0。
:param arr:
:return:
"""
ans = 0
xor = 0
length = len(arr)
mosts = [0 for i in range(length)]
map = {}
map[0] = -1
for i in range(length):
xor ^= arr[i]
if xor in map:
pre = map[xor] # 找到那个开头位置
mosts[i] = 1 if pre == -1 else (mosts[pre] + 1) # 开头位置的最大值 + 1
if i > 0:
mosts[i] = max(mosts[i - 1], mosts[i]) # 只依赖前i-1 和 i 两种情况
map[xor] = i
ans = max(ans, mosts[i])
return ans
def _split_process(pre, rest):
if rest == 0:
return 1
if pre > rest:
return 0
ways = 0
for i in range(pre, rest + 1):
ways += _split_process(i, rest - i)
return ways
def split_ways(n):
if n < 1:
return 0
return _split_process(1, n)
def split_ways_dp(n):
"""
给定一个正数1,裂开的方法有一种,(1)
给定一个正数2,裂开的方法有两种,(1和1)、(2)
给定一个正数3,裂开的方法有三种,(1、1、1)、(1、2)、(3)
给定一个正数4,裂开的方法有五种,(1、1、1、1)、(1、1、2)、(1、3)、(2、2)、 (4)
给定一个正数n,求裂开的方法数。
动态规划优化状态依赖的技巧
:param n:
:return:
"""
if n < 1:
return 0
dp = [[0 for j in range(n + 1)] for i in range(n + 1)]
for i in range(1, n + 1):
dp[i][0] = 1
for i in range(1, n + 1):
dp[i][i] = 1
for pre in range(1, n)[::-1]:
for rest in range(pre + 1, n + 1):
dp[pre][rest] = dp[pre + 1][rest] + dp[pre][rest - pre]
for i in dp:
print(i)
return dp[1][n]
问题描述:
打气球,一下 能 连续打爆一串回文串,或者打爆一个
求打爆所有的最少需要几次?
# -*- coding:UTF-8 -*-
import sys
def archer(n, nums):
"""
动态规划
:param n:
:param nums:
:return:
"""
dp = [[sys.maxsize for j in range(n)] for i in range(n)]
for i in range(n):
for j in range(i, n):
if i == j:
dp[i][j] = 1
continue
if j - i == 1:
dp[i][j] = 1 if nums[i] == nums[j] else 2
break
for i in range(n):
for j in range(i, n):
for k in range(i, j):
dp[i][j] = min(dp[i][j], dp[i][k] + dp[k + 1][j])
if nums[i] == nums[j] and i + 1 < j - 1:
dp[i][j] = min((dp[i][j], dp[i + 1][j - 1]))
for i in dp:
print(i)
return dp[0][n - 1]
def dfs(l, r):
"""
动态规划
递归形式
:param l:
:param r:
:return:
"""
if dp[l][r] != -1:
return dp[l][r]
if l == r:
dp[l][r] = 1
return dp[l][r]
if r - l == 1:
dp[l][r] = 1 if nums[l] == nums[r] else 2
return dp[l][r]
ans = sys.maxsize
for k in range(l, r):
ans = min(ans, dfs(l, k) + dfs(k + 1, r))
if nums[l] == nums[r]:
ans = min(ans, dfs(l + 1, r - 1))
dp[l][r] = ans
return dp[l][r]
if __name__ == '__main__':
"""
问题描述:
打气球,一下 能 连续打爆一串回文串,或者打爆一个
求打爆所有的最少需要几次?
"""
n = 4
nums = [1, 4, 3, 1]
dp = [[-1 for j in range(n)] for i in range(n)]
print(dfs(0, n - 1))
for i in dp:
print(i)
print("--" * 10)
print(archer(n, nums))
def penguin_merge_near(arr):
"""
企鹅合体,只能合体一次,只能和左右其中的一个合体,合体即乘积,有可能为负数,求最大值
:param arr:
:return:
"""
length = len(arr)
if length < 1:
return 0
elif length == 1:
return arr[0]
elif length == 2:
return max(arr[0] + arr[1], arr[0] * arr[1])
dp = [0 for i in range(length + 1)]
dp[1] = arr[0]
dp[2] = max(arr[0] + arr[1], arr[0] * arr[1])
if length > 2:
for i in range(3, length + 1):
dp[i] = max(dp[i - 1] + arr[i - 1], dp[i - 2] + arr[i - 1] * arr[i - 2])
return dp[-1]
def penguin_merge(arr):
"""
企鹅合体,只能合体一次,合体即乘积,有可能为负数,求最大值
由于不指定顺序,因此可以先排序,-5, -3, 0, 1, 3, 5
:param arr:
:return:
"""
length = len(arr)
arr = sorted(arr)
if length < 1:
return 0
elif length == 1:
return arr[0]
elif length == 2:
return max(arr[0] + arr[1], arr[0] * arr[1])
ans = 0
if length > 2:
l = 0
r = length - 1
while r > 0:
if arr[r - 1] > 1:
ans += arr[r] * arr[r - 1]
r -= 2
else:
break
while l < r:
if arr[l + 1] <= 0:
ans += arr[l] * arr[l + 1]
l += 2
else:
break
while l <= r:
ans += arr[l]
l += 1
return ans
# -*- coding: utf-8 -*-
"""
@Time : 2019/5/11 3:06 PM
@Author : ddlee
@File : 845longest_mountain.py
"""
class Solution():
def longestMountain1(self, arr):
"""
求数组中的最长山脉
- B.length >= 3
- 存在 0 < i < B.length - 1 使得 B[0] < B[1] < ... B[i-1] < B[i] > B[i+1] > ... > B[B.length - 1]
输入:[2,1,4,7,3,2,5]
输出:5
解释:最长的 “山脉” 是 [1,4,7,3,2],长度为 5。
"""
# j结尾的递增序列
def lis(arr):
length = len(arr)
dp = [0 for i in range(length + 1)]
for i in range(1, length + 1):
for j in range(i, length + 1):
if arr[j - 1] > arr[i - 1]:
dp[j] = max(dp[i] + 1, dp[j])
return dp
# i开头的递减序列
def lds(arr):
length = len(arr)
dp = [0 for i in range(length + 1)]
for i in range(1, length + 1)[::-1]:
for j in range(i, length + 1)[::-1]:
if arr[j - 1] < arr[i - 1]:
dp[i] = max(dp[j] + 1, dp[i])
return dp
lis_dp = lis(arr)
print(lis_dp)
lds_dp = lds(arr)
print(lds_dp)
res = []
for i in range(len(lis_dp)):
if lis_dp[i] and lds_dp[i]:
res.append(lis_dp[i] + lds_dp[i] + 1)
else:
res.append(0)
return max(res) if res else 0
def longestMountain(self, arr):
"""
求数组中的最长山脉
- B.length >= 3
- 存在 0 < i < B.length - 1 使得 B[0] < B[1] < ... B[i-1] < B[i] > B[i+1] > ... > B[B.length - 1]
输入:[2,1,4,7,3,2,5]
输出:5
解释:最长的 “山脉” 是 [1,4,7,3,2],长度为 5。
"""
# i结尾的递增序列
def lis(arr):
length = len(arr)
dp = [0 for i in range(length)]
for i in range(1, length):
if arr[i] > arr[i - 1]:
dp[i] = max(dp[i - 1] + 1, dp[i])
return dp
# i开头的递减序列
def lds(arr):
length = len(arr)
dp = [0 for i in range(length)]
for i in range(length - 1)[::-1]:
if arr[i] > arr[i + 1]:
dp[i] = max(dp[i + 1] + 1, dp[i])
return dp
lis_dp = lis(arr)
# print(lis_dp)
lds_dp = lds(arr)
# print(lds_dp)
res = []
for i in range(len(lis_dp)):
if lis_dp[i] and lds_dp[i]:
res.append(lis_dp[i] + lds_dp[i] + 1)
else:
res.append(0)
return max(res) if res else 0
if __name__ == '__main__':
arr = [2, 1, 4, 7, 3, 2, 5]
arr = list(range(5))
arr = []
print(Solution().longestMountain(arr))
给出整数数组 A,将该数组分隔为长度最多为 K 的几个(连续)子数组。
分隔完成后,每个子数组的中的值都会变为该子数组中的最大值。
返回给定数组完成分隔后的最大和。
输入:A = [1,15,7,9,2,5,10], K = 3
输出:84
解释:A 变为 [15,15,15,9,10,10,10]
class Solution:
def maxSumAfterPartitioning2(self, A, K):
l = len(A)
dp = [0 for i in range(l+1)]
for i in range(l):
maxi = 0
rb = min(i + K, l) # right bound
for j in range(i, rb):
maxi = max(maxi, A[j])
dp[j + 1] = max(dp[j + 1], dp[i] + (j - i + 1) * maxi)
# print(dp)
return dp[l]
在一个由 0 和 1 组成的二维矩阵内,找到只包含 1 的最大正方形,并返回其面积。
示例:
输入:
1 0 1 0 0
1 0 1 1 1
1 1 1 1 1
1 0 0 1 0
输出: 4
class Solution:
def maximalSquare1(self, matrix):
if len(matrix) < 1:
return 0
matrix = [list(map(int, row)) for row in matrix]
m = len(matrix)
n = len(matrix[0])
dp = [[0 for j in range(n)] for i in range(m)]
for i in range(m):
dp[i][0] = matrix[i][0]
for j in range(n):
dp[0][j] = matrix[0][j]
for i in range(1, m):
for j in range(1, n):
if matrix[i][j] == 1:
if matrix[i - 1][j - 1] == 1:
for ind in range(1, int(math.sqrt(dp[i - 1][j - 1]) + 1)):
if matrix[i - ind][j] == 1 and matrix[i][j - ind] == 1:
dp[i][j] = int(pow(ind + 1, 2))
else:
dp[i][j] = int(pow(ind + 1 - 1, 2))
break
else:
dp[i][j] = 1
return max(map(max,dp))
"""
计算边长,比上一种方案好
"""
def maximalSquare(self, matrix):
if len(matrix) < 1:
return 0
matrix = [list(map(int, row)) for row in matrix]
m = len(matrix)
n = len(matrix[0])
dp = [[0 for j in range(n)] for i in range(m)]
for i in range(m):
dp[i][0] = matrix[i][0]
for j in range(n):
dp[0][j] = matrix[0][j]
for i in range(1, m):
for j in range(1, n):
if matrix[i][j] == 1:
dp[i][j] = min(dp[i-1][j], dp[i][j-1], dp[i-1][j-1]) + 1
else:
min(dp[i - 1][j], dp[i][j - 1], dp[i - 1][j - 1])
for i in dp:
print(i)
return pow(max(map(max,dp)), 2)
class Solution:
"""
1049. 最后一块石头的重量 II 显示英文描述
有一堆石头,每块石头的重量都是正整数。
每一回合,从中选出任意两块石头,然后将它们一起粉碎。假设石头的重量分别为 x 和 y,且 x <= y。那么粉碎的可能结果如下:
如果 x == y,那么两块石头都会被完全粉碎;
如果 x != y,那么重量为 x 的石头将会完全粉碎,而重量为 y 的石头新重量为 y-x。
最后,最多只会剩下一块石头。返回此石头最小的可能重量。如果没有石头剩下,就返回 0。
示例:
输入:[2,7,4,1,8,1]
输出:1
解释:
组合 2 和 4,得到 2,所以数组转化为 [2,7,1,8,1],
组合 7 和 8,得到 1,所以数组转化为 [2,1,1,1],
组合 2 和 1,得到 1,所以数组转化为 [1,1,1],
组合 1 和 1,得到 0,所以数组转化为 [1],这就是最优值。
"""
def lastStoneWeightII(self, stones):
'''动态规划 背包问题'''
l = len(stones)
if l < 1:
return 0
half = sum(stones) // 2 # 背包最大放的重量
dp = [[0 for j in range(half + 1)] for i in range(l + 1)]
for i in range(1, l + 1):
for j in range(1, half + 1):
if stones[i - 1] > j:
dp[i][j] = dp[i - 1][j]
else:
dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - stones[i - 1]] + stones[i - 1])
for i in dp:
print(i)
return sum(stones) - (2 * dp[-1][-1])
class Solution:
"""
输入:[3,6,9,12]
输出:4
解释:
整个数组是公差为 3 的等差数列。
"""
'''dp加缓存'''
def longestArithSeqLength(self, A) -> int:
if A == None or A == []:
return 0
n, res = len(A), 1
dp = [{0: 1} for _ in range(n)]
for j in range(n):
for i in range(j):
step = A[j] - A[i]
if step in dp[i]:
if step in dp[j]:
dp[j][step] = max(dp[i][step] + 1, dp[j][step])
else:
dp[j][step] = dp[i][step] + 1
else:
dp[j][step] = 2
res = max(res, max(dp[j].values()))
print(dp)
return res
'''暴力超时'''
def las(self, A):
if A == None or A == []:
return 0
l = len(A)
if l < 2:
return l
res = 2
for i in range(l):
for j in range(i + 1, l):
tmp = 2
gap = A[j] - A[i]
next = A[j] + gap
for k in range(j + 1, l):
if A[k] == next:
tmp += 1
next += gap
res = max(res, tmp)
return res
"""
不同的子序列
给定一个字符串 S 和一个字符串 T,计算在 S 的子序列中 T 出现的个数。
一个字符串的一个子序列是指,通过删除一些(也可以不删除)字符且不干扰剩余字符相对位置所组成的新字符串。
(例如,"ACE" 是 "ABCDE" 的一个子序列,而 "AEC" 不是)
示例 1:
输入: S = "rabbbit", T = "rabbit"
输出: 3
解释:
如下图所示, 有 3 种可以从 S 中得到 "rabbit" 的方案。
(上箭头符号 ^ 表示选取的字母)
rabbbit
^^^^ ^^
rabbbit
^^ ^^^^
rabbbit
^^^ ^^^
"""
class Solution:
def numDistinct(self, s: str, t: str) -> int:
ls = len(s) + 1
lt = len(t) + 1
dp = [[0 for j in range(lt)] for i in range(ls)]
if not t:
return 1
for i in range(ls):
dp[i][0] = 1
dp[i][1] = s[:i].count(t[0])
for i in range(1, ls):
for j in range(1, lt):
if s[i - 1] == t[j - 1]:
dp[i][j] = dp[i - 1][j - 1] + dp[i - 1][j]
# 相等的情况:s用相等这一位的方案 + s不用相等这一位的方案
else:
dp[i][j] = dp[i - 1][j]
for i in dp:
print(i)
return dp[-1][-1]
解决方案:动态规划,定义一个dp数组,长度为N+1,初始化为系统最大值,0的位置初始化为0
从1开始刷新整个数组:
1的平方是1,所以1……N中填1……N,更新dp
2的平方是4,所以从4的位置开始更新,dp[i] = min(dp[i], dp[i-j^2]+1)
……
'''
279. 完全平方数
给定正整数 n,找到若干个完全平方数(比如 1, 4, 9, 16, ...)使得它们的和等于 n。你需要让组成和的完全平方数的个数最少。
示例 1:
输入: n = 12
输出: 3
解释: 12 = 4 + 4 + 4.
示例 2:
输入: n = 13
输出: 2
解释: 13 = 4 + 9.
'''
class Solution:
def numSquares(self, n: int) -> int:
if n < 1:
return 0
dp = [sys.maxsize for i in range(n + 1)]
dp[0] = 0
rnd = int(pow(n, 0.5)) + 1
for i in range(1, rnd):
for j in range(pow(i, 2), n + 1):
dp[j] = min(dp[j], dp[j - pow(i, 2)] + 1)
# print(dp)
return dp[-1]
def numSquares1(self, n):
"""
:type n: int
:rtype: int
"""
def help(N, size):
if (N == 0): return True;
if (size == 0): return False;
k = int(pow(N, 0.5));
for i in range(k, 0, -1):
if (help(N - i * i, size - 1)):
return True
return False
f = False
ans = 0;
while (f == 0):
ans = ans + 1;
f = help(n, ans)
return ans
if __name__ == '__main__':
print(Solution().numSquares2(6175))
# -*- coding:UTF-8 -*-
"""
想法:
构建两个堆,大根堆,小根堆,保证大根堆的最大值 比 小根堆的最小值 小,保证两个堆相差不超过2
这样中位数就可以通过计算堆顶元素获得,时间复杂度O(1)
堆调整的复杂度O(logn)
"""
class MedianFinder(object):
def __init__(self):
self.arr = []
self.max_heap = []
self.min_heap = []
def add_num(self, num):
self.max_heap.append(num)
self.down_ajust_max(self.max_heap)
self.min_heap.append(self.max_heap.pop(0))
self.down_ajust_min(self.min_heap)
if len(self.max_heap) < len(self.min_heap):
self.max_heap.append(self.min_heap.pop(0))
# self.down_ajust_min(self.min_heap)
self.down_ajust_max(self.max_heap)
def down_ajust_max(self, arr):
length = len(arr)
if length < 2:
return arr
parent_idx = 0
tmp = arr[parent_idx]
child_idx = 2 * parent_idx + 1
while child_idx < length:
if child_idx + 1 < length and arr[child_idx + 1] > arr[child_idx]:
child_idx += 1
if tmp > arr[child_idx]:
break
arr[parent_idx] = arr[child_idx]
parent_idx = child_idx
child_idx = 2 * child_idx + 1
arr[parent_idx] = tmp
def down_ajust_min(self, arr):
length = len(arr)
if length < 2:
return arr
parent_idx = 0
tmp = arr[parent_idx]
child_idx = 2 * parent_idx + 1
while child_idx < length:
if child_idx + 1 < length and arr[child_idx + 1] < arr[child_idx]:
child_idx += 1
if tmp < arr[child_idx]:
break
arr[parent_idx] = arr[child_idx]
parent_idx = child_idx
child_idx = 2 * child_idx + 1
arr[parent_idx] = tmp
def find_median(self):
if len(self.max_heap) == len(self.min_heap):
return (self.max_heap[0] + self.min_heap[0]) / 2
else:
return self.max_heap[0]
if __name__ == '__main__':
mf = MedianFinder()
mf.add_num(5)
mf.add_num(4)
mf.add_num(9)
mf.add_num(7)
mf.add_num(3)
print(mf.min_heap)
print(mf.max_heap)
print(mf.find_median())
class Solution:
def hasPath(self, matrix, rows, cols, path):
# write code here
for i in range(rows):
for j in range(cols):
if matrix[i * cols + j] == path[0]:
if self.find(list(matrix), rows, cols, path[1:], i, j):
return True
return False
def find(self, matrix, rows, cols, path, i, j):
if not path:
return True
matrix[i * cols + j] = '0'
if j + 1 < cols and matrix[i * cols + j + 1] == path[0]:
return self.find(matrix, rows, cols, path[1:], i, j + 1)
elif j - 1 >= 0 and matrix[i * cols + j - 1] == path[0]:
return self.find(matrix, rows, cols, path[1:], i, j - 1)
elif i + 1 < rows and matrix[(i + 1) * cols + j] == path[0]:
return self.find(matrix, rows, cols, path[1:], i + 1, j)
elif i - 1 >= 0 and matrix[(i - 1) * cols + j] == path[0]:
return self.find(matrix, rows, cols, path[1:], i - 1, j)
else:
return False
def sqrt(x):
if x < 2:
return x
left, right = 0, x
while left <= right:
mid = left + (right - left) // 2
if mid * mid < x:
# left=mid
left = mid + 1
lstmid = mid # 关键步骤
elif mid * mid > x:
# right=x
right = mid - 1
else:
return mid
return lstmid
"""
Josephuse环
n个人,编号0 到 n-1,每走step步移除一个人,最后剩的编号是?
method-1: 用环去模拟
method-2:分析规律 计算(有公式)
"""
def last_remain(length, step):
if length < 1 or step < 1:
return -1
nums = [i for i in range(length)]
step_cnt = 0
remain_cnt = length
i = -1
while remain_cnt > 0:
i += 1
if i >= length:
i = 0
if nums[i] == -1:
continue
step_cnt += 1
if step_cnt == step:
nums[i] = -1
step_cnt = 0
remain_cnt -= 1
return i
# 公式版本
def last_remain_2(length, step):
if length < 1 or step < 1:
return -1
last = 0
for i in range(2, length + 1):
last = (last + step) % i
return last
if __name__ == '__main__':
for i in range(1,50):
print(last_remain(i, 3), last_remain_2(i, 3))
# -*- coding:UTF-8 -*-
def sum_2(arr, target):
hash_table = {}
res = []
for i in range(len(arr)):
if target - arr[i] in hash_table:
res.append([hash_table[target - arr[i]], i])
hash_table[arr[i]] = i
return res
def sum_3(arr, target):
arr.sort()
len1 = len(arr)
res = []
if len1 < 3:
print(res)
for i in range(len1 - 1):
left, right = i + 1, len1 - 1 # 以下思路与2sum中的快速排序思想一样
while left < right:
sum = arr[i] + arr[left] + arr[right]
if sum == target and [arr[i], arr[left], arr[right]] not in res:
res.append([arr[i], arr[left], arr[right]])
left += 1
right -= 1
elif sum < target:
left += 1
else:
right -= 1
print(res)
def sum_3_2(arr, target): # 3sum问题 解 2
res = []
for i, value1 in enumerate(arr):
for j, value2 in enumerate(arr[i + 1:]):
if (target - value1 - value2) in arr[i + 2:]:
minV = min(value1, value2, target - value1 - value2)
maxV = max(value1, value2, target - value1 - value2)
midV = target - minV - maxV
res.append((minV, midV, maxV))
print(list(set(res)))
if __name__ == '__main__':
nums = [1, 4, 3, 2, 6, 5]
target = 6
print(sum_2(nums, target))
sum_3(nums, target)
sum_3_2(nums, target)
print(continuous_pos_sum(15))
class Solution:
"""
leetcode 40
给定一个数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。
candidates 中的每个数字在每个组合中只能使用一次。
"""
def combinationSum2(self, candidates, target):
# 下面有一个目标值小于某一元素就break,所以先排序
candidates.sort()
# 储存返回的二维列表
res, length = [], len(candidates)
# 递归,目标值,起始位置,当前组合
def dfs(target, start, vlist):
# 目标值为0,表明当前递归完成,把当前递归结果加入res并返回
if target == 0:
return res.append(vlist)
# 从开始下标循环
for i in range(start, length):
# candidates有序,只要当前大于目标后面都大于,直接break
if target < candidates[i]:
break
# 这个判断保证不重复例如1,1,2,5,6,7,10,第二个1就会被跳过
if i > start and candidates[i] == candidates[i - 1]:
continue
# 否则目标值减当前值,i+1为新的起始位置(不用重复数字),把当前值加入当前组合
else:
dfs(target - candidates[i], i + 1, vlist + [candidates[i]])
dfs(target, 0, [])
return res
"""
leetcode 39
给定一个数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。
candidates 中的每个数字在每个组合中次数不限。
"""
def combinationSum(self, candidates, target):
# 下面有一个目标值小于某一元素就break,所以先排序
candidates.sort()
# 储存返回的二维列表
res, length = [], len(candidates)
# 递归,目标值,起始位置,当前组合
def dfs(target, start, vlist):
# 目标值为0,表明当前递归完成,把当前递归结果加入res并返回
if target == 0:
return res.append(vlist)
# 从开始下标循环
for i in range(start, length):
# candidates有序,只要当前大于目标后面都大于,直接break
if target < candidates[i]: break
# 这个判断保证不重复例如1,1,2,5,6,7,10,第二个1就会被跳过
if i > start and candidates[i] == candidates[i - 1]:
continue
# 否则目标值减当前值,i为用重复数字,把当前值加入当前组合
else:
dfs(target - candidates[i], i, vlist + [candidates[i]])
dfs(target, 0, [])
return res
class Solution:
def myPow(self, x: float, n: int) -> float:
# return pow(x, n)
i = n
if i < 0:
i = -i
res = 1
while i != 0:
if i % 2 != 0:
res *= x
x *= x
i = i // 2
return res if n > 0 else 1 / res
def superPow(self, a, b):
"""
你的任务是计算 ab 对 1337 取模,a 是一个正整数,b 是一个非常大的正整数且会以数组形式给出。
示例 1:
输入: a = 2, b = [3]
输出: 8
示例 2:
输入: a = 2, b = [1,0]
输出: 1024
"""
def modn(a, b, c):
res = 1
while b != 0:
if (b & 1):
res *= a
res %= c
a *= a
a %= c
b >>= 1
return res
power = 0
for i in b:
power = power * 10 + i
a = a % 1337
res = modn(a, power, 1337)
return res % 1337
if __name__ == '__main__':
a = 2
b = [3, 0, 0]
print(Solution().superPow(a, b))
给定高度m 、宽度n 的一张 m * n的乘法表,以及正整数k,你需要返回表中第k 小的数字。
例 1:
输入: m = 3, n = 3, k = 5
输出: 3
解释:
乘法表:
1 2 3
2 4 6
3 6 9
第5小的数字是 3 (1, 2, 2, 3, 3).
class Solution:
def findKthNumber(self, m: int, n: int, k: int) -> int:
def cnt_before_x(x):
cnt = 0
for i in range(1, m + 1):
cnt += min(x // i, n)
return cnt
def binary_search(left, right, k):
while left < right:
mid = (left + right) >> 1
cnt = cnt_before_x(mid)
if cnt >= k:
right = mid
else:
left = mid + 1
return left
left = 1
right = m * n + 1
kth = binary_search(left, right, k)
return kth