更新中…
一、Sort
1. Bubble Sort
时间复杂度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
2. Quick Sort
时间复杂度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)
3. Heap Sort
堆调整时间复杂度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)
二、Tree
1. Traversal
广度 & 深度
# 广度优先遍历算法
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]
2. 前序中序 --> 构建树/求后序
# 前序 中序 构建树
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
3. 公共祖先 lowest common ancestor (LCA)
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
1. 搜索路径问题 & 遍历
给定 起止节点 求搜索路径问题 & 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)
四、LinkList
1. 反转链表
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
2. Entry of Loop
"""
第一步,找环中相汇点。分别用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
五、Dynamic Programming
动态规划:
- 自底向上
就是已经知道了所有递归边界,把所有可能的状态都算出来。
从初始已知的状态出发,向外拓展,最后到达目标状态。- 自顶向下(“记忆化搜索”)
从最终状态开始,找到可以到达当前状态的状态,如果该状态还没处理,就先处理该状态。
自顶向下通常使用递归实现- 总结
自顶向下,自底向上,只是动态规划实现的套路而已。复杂度并没有多大的变化。
事实上大多时候用自顶向下复杂度会更低,因为可以过滤掉更多无用的状态;
不过自底向上可以避免爆栈问题,而且实现往往实现更为简单。
1. 凑硬币问题
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))
2. 硬币组合数量问题
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]
3. 编辑距离 / 最长公共字串 / 最长公共子序列
# 编辑距离
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
4.
在这里插入代码片
六、面试题
1. leetcode470 – rand7生成rand10
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().
# -*- 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)
2. leecode273 – 非负整数转换为其对应的英文表示
# -*- 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))
3. leetcode207 – 拓扑排序检测有向无环图
# -*- 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]]))
4. 最小删减构造回文串(最长公共子序列思想)(dp问题)
给定一个字符串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]
5. leetcode300 – 最长上升子序列(dp问题)
def lis(s):
"""
最长上升子序列,二维存储
:param s:
:return:
"""
length = len(s)
if length < 2:
return length
dp = [[0 for j in range(length + 1)] for i in range(length + 1)]
for i in range(1, length + 1):
for j in range(1, length + 1):
if s[i - 1] < s[j - 1] and i < j:
dp[i][j] = dp[i - 1][j - 1] + 1
else:
dp[i][j] = max(dp[i - 1][j], dp[i][j - 1])
for i in dp:
print(i)
return dp
def lis2(s):
"""
最长上升子序列,一维存储
:param s:
:return:
"""
length = len(s)
if length < 2:
return length
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
6. 剑指Offer 14 – 剪绳子最大乘积(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]
7. 最多有多少不重叠的非空区间,使得每个区间内数字的 xor等于0(dp问题)
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
8. 给定一个正数n,求裂开的方法数(dp问题)
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]
9. 消消乐问题(dp问题)
问题描述:
打气球,一下 能 连续打爆一串回文串,或者打爆一个
求打爆所有的最少需要几次?
# -*- 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))
10. 数据流求中位数(大根堆问题)
# -*- 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())