更新中…
文章目錄
- 一、Sort
- 二、Tree
- 三、Graph
- 四、LinkList
- 五、Dynamic Programming
- 六、String
- 七、面試題
- 1. leetcode470 -- rand7生成rand10
- 2. leecode273 -- 非負整數轉換爲其對應的英文表示
- 3. leetcode207 -- 拓撲排序檢測有向無環圖
- 4. 最小刪減構造迴文串(最長公共子序列思想)(dp問題)
- 5. leetcode300 -- 最長上升子序列(dp問題)
- 6. 劍指Offer 14 -- 剪繩子最大乘積(dp問題)
- 7. 最多有多少不重疊的非空區間,使得每個區間內數字的 xor等於0(dp問題)
- 8. 給定一個正數n,求裂開的方法數(dp問題)
- 9. 消消樂問題(dp問題)
- 10. 企鵝合體問題(dp問題)
- 11. leetcode845 -- 最長山脈問題(dp問題)
- 12. 分隔數組以得到最大和 (dp問題)
- 13. leetcode221 -- 最大正方形(dp問題)
- 14. 最後剩一塊石頭重量(dp問題)
- 15. 最長等差序列(dp問題)
- 16. leetcoe115 -- 不同的子序列(dp問題)
- 17. leetcoe279 -- 完全平方數(dp問題)
- 21. 數據流求中位數(大根堆問題)
- 22. 判斷矩陣中是否存在指定路徑(遞歸)
- 23. sqrt
- 24. 約瑟夫環
- 25. 2sum 3sum
- 26. leetcode40 -- 和爲target的組合(遞歸)
- 27. leetcode372 -- 超級次方 (power的複雜度是log(n))
- 28. leetcode668 -- 給定高度m 、寬度n 的一張 m * n的乘法表,以及正整數k,你需要返回表中第k 小的數字(二分查找,逆向思維)
一、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.
在這裏插入代碼片
六、String
1. Permutation
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
2. Combinations
# -*- 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))
3. Josephus
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)
七、面試題
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 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
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. 企鵝合體問題(dp問題)
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
11. leetcode845 – 最長山脈問題(dp問題)
# -*- 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))
12. 分隔數組以得到最大和 (dp問題)
給出整數數組 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]
13. leetcode221 – 最大正方形(dp問題)
在一個由 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)
14. 最後剩一塊石頭重量(dp問題)
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])
15. 最長等差序列(dp問題)
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
16. leetcoe115 – 不同的子序列(dp問題)
"""
不同的子序列
給定一個字符串 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]
17. leetcoe279 – 完全平方數(dp問題)
解決方案:動態規劃,定義一個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))
21. 數據流求中位數(大根堆問題)
# -*- 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())
22. 判斷矩陣中是否存在指定路徑(遞歸)
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
23. sqrt
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
24. 約瑟夫環
"""
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))
25. 2sum 3sum
# -*- 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))
26. leetcode40 – 和爲target的組合(遞歸)
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
27. leetcode372 – 超級次方 (power的複雜度是log(n))
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))
28. leetcode668 – 給定高度m 、寬度n 的一張 m * n的乘法表,以及正整數k,你需要返回表中第k 小的數字(二分查找,逆向思維)
給定高度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