目录
1. n个[0,n)的数,求每个数的出现次数(不能开辟额外空间)
2. 约瑟夫环问题(题解参考)
3. 图的BFS and DFS遍历(题解参考 and 题解参考2)
3. 圆环回原点问题(题解参考)
4. Leetcode164:最大间距
5. 有A和B两个有序数组(数组元素不重复),给出sum,请找到A和B中所有相加和为sum的序列对
6. 找出数组中的k数
7. 重写Equals
8. Leetcode50:Pow(x, n)
9. AUC计算
10. 二分查找和牛顿迭代求平方根
12. 列表构建二叉树
13. 保留有序链表中的重复元素,并且只保留一次
14. 快排(递归and非递归)||Leetcode75:颜色分类(荷兰国旗问题)||剑指 Offer 45. 把数组排成最小的数
17. 从n个元素中等概率地抽取m个元素(蓄水池抽样)(参考题解1)(参考题解2)
18. 归并排序(题解参考)
19. Leetcode148:排序链表(未调试成功)
20. 单例(题解参考1)(题解参考2)
21. threading实现多线程(题解参考)
22. 找出100以内的所有质数(题解参考)
23. 随机洗牌算法(题解参考)
24. 稀疏矩阵存储以及相乘
基本思路:
def numberAppearTimes(nums):
if not nums: return None
size = len(nums)
for i in range(size):
nums[nums[i] % size] += size
ans = []
for i in range(size):
ans.append((i, nums[i] // size))
return ans
a = [1, 2, 1, 4, 1]
print(numberAppearTimes(a))
题目:有n个人围成一圈,1..n,从1开始报数,每次踢出报到k的人。然后下一个继续从1开始报数,输出环中的最后一个人。
基本思路:1. 考虑到k=1的情况,直接输出最后一个人;2. 考虑计数大于len(people),需要对len(people)取模;
def josephus(n, k):
"""
n:总共有n个人围成一圈
k:每次踢出排名第k的人
"""
if k == 1:
print('survive:', n) # 特殊情况,最后一个人留下来
p = 0
people = list(range(1, n+1)) # 编号是从1开始的
while True:
if len(people) == 1:
break # 剩下最后一个人,跳出来
p = (p + (k-1)) % len(people) # 考虑到k大于len(people)的情况,也考虑到编号遍历时大于len(people)的情况
print('kill:', people[p])
del people([p]) # 找到该节点后删除
print('survive:', people[0])
基本思路:
from queue import Queue
# Definition for a node
class Node:
def __init__(self, val):
self.val = val
self.next = None
class Solution:
"""
根据邻接表创建有向图
"""
def __init__(self, adj_list):
self.adj_list = adj_list
self.peaks = []
self.visited = [False] * len(self.adj_list)
def bfs(self):
"""
广度优先遍历,时间复杂度O(n)
"""
queue = Queue()
self.visted = [False] * len(self.adj_list) # 存储顶点的访问状态
queue.put(0) # 默认从邻接表第0个邻接点开始,这里队列存的是index,以下基于index来
self.visted[0] = True
self.peaks.append(self.adj_list[0][0])
while not queue.empty():
index = queue.get() # 出队
for i in self.adj_list[index][1]: # 遍历每个顶点的连接点list
if not self.visited[i]: # 判断是否访问过
self.visited[i] = True
queue.put(i) # 将顶点所对应的index入队
self.peaks.append(adj_list[i][0]) # 打印顶点名称
return self.peaks
def dfs(self, node):
"""
递归调用深度优先遍历,时间复杂度O(n)
node:其实节点的index
"""
if self.visited[node] == True: return
self.visited[node] = True
self.peaks.append(self.adj_list[node][0])
for index in self.adj_list[node][1]:
if not self.visited[index]:
self.dfs(index)
return self.peaks
if __name__ == '__main__':
adj_list = [
("a",[4,2,1]),
("b",[2]),
("c",[4,3]),
("d",[4]),
("e",[]),
]
solution = Solution(adj_list)
# print(solution.bfs())
print(solution.dfs(0))
题目:一个环上有10个点,编号为0-9,从0点出发,每步可以顺时针到下一个点,也可以逆时针到上一个点,求:经过n步又回到0点有多少种不同的走法。
基本思路:再回到0点可以从右面回来,也可以从左面回来,即先到达旁边的一个点,看看有多少回来的方法;d(k, j) = d(k-1, j-1) + d(k-1, j+1);d(k, j)表示从点j走k步到达原点0的方法数,因此可以转化为他相邻的点经过k-1步回到原点的问题,这样将问题的规模缩小.由于是环的问题, j-1, j+1可能会超出 0到n-1的范围,因此,我们将递推式改成如下:d(k, j) = d(k-1, (j-1+n)%n) + d(k-1, (j+1)%n);
def getstep(n, k):
"""
n:环的元素个数,圆环从0点开始,最大值为n-1
k:回到原点走的步数
"""
if n == 0: return 0 # 没有元素;
if n == 1 and k == 0: return 1 # 1个元素,走0步,1种走法;
# 自己define max(n, k) <= 100
dp = [[0 for _ in range(100)] for _ in range(100)]
# dp[0][0] = 0;
for i in range(1, n):
dp[0][i] = 0 # 在圆环内任意一个非原点的点走k=0步到回到原点的走法为0
for i in range(1, k + 1):
for j in range(n):
dp[i][j] = dp[i-1][(j-1+n) % n] + dp[i-1][(j+1) % n] # 可以从右边回来,也可以从左边回来,所以j-1和j+1两种情况;同时考虑到j-1要满足0..n-1,额外+n再取模
return dp[k][0] # 从0开始,走k步最后又回到0点;
题目:给定一个无序的数组,找出数组在排序之后,相邻元素之间最大的差值。如果数组元素个数小于 2,则返回 0。(要求线性时间和空间复杂度)
示例:输入: [3,6,9,1] 输出: 3 解释: 排序后的数组是 [1,3,6,9], 其中相邻元素 (3,6) 和 (6,9) 之间都存在最大差值 3。
基本思路:1. 先排序,再遍历;(时间复杂度O(nlogn),不满足时间复杂度)
示例:A:[1,4,5] and B:[3,4,6] and sum=8 --> output 1,1,2,0 => because A[1]+B[1]=8 /A[2]+B[0]=8
基本思路:前后双指针 + hashmap
"""
方法1:前后双指针
时间复杂度:max(O(nlogn), O(mlogm)) if not sorted; O(m + n) if sorted
空间复杂度:O(1)
"""
def findSumIndex(nums1, nums2, target):
# if unsorted, need sort;
nums1.sort()
nums2.sort()
ans = []
if not nums1 or not nums2: return []
i, j = 0, len(nums2)
while i < len(nums1) and j >= 0:
if nums1[i] + nums[j] == target:
ans.append([i, j])
i += 1
j -= 1
elif nums1[i] + nums[j] > target:
j -= 1
else:
i += 1
return ans
"""
方法2:hashmap -- 将长度更短的一个nums构建hashmap, 遍历另一个nums查找
时间复杂度:max(O(n), O(m))
空间复杂度:min(O(n), O(m))
"""
题目:k数的定义:比位置位于这个数前面的数字都大,且比位置位于这个数后面的数字都小的数字(数组首尾的数字不用管)
示例:[4,1,3,2,7,9,8,10,12] --> [7, 10]
基本思路:遍历第一遍find符合比左边值都大的index, 遍历第二遍找到比右边值都小的index,同时结合第一遍遍历的结果,得到同时满足条件的index;
"""
方法1:
遍历第一遍find符合比左边值都大的index, 遍历第二遍找到比右边值都小的index,同时结合第一遍遍历的结果,得到同时满足条件的index;
时间复杂度:O(n)
空间复杂度:O(n) -- 辅助数组
"""
def knums(nums):
if not nums: return -1
size = len(nums)
mi = [False for _ in range(size)]
min_val = nums[0]
max_val = nums[size - 1]
ans = []
for i in range(1, size):
if nums[i] > min_val:
mi[i] = True
min_val = max(min_val, nums[i])
for j in range(size-1, -1, -1):
if nums[j] < max_val:
max_val = min(max_val, nums[j])
if mi[j] == True:
ans.append(nums[j])
return ans
题目:两个字符串,按照规则判断相等,规则是两个字符串相同字符出现的次数相同,遍判定相等。例(AAB 和 ABA 相等)
基本思路:1. 两个hashmap;2. 转换为数组,再sort;最后遍历比对;
题目:实现 pow(x, n) ,即计算 x 的 n 次幂函数。
示例:输入: 2.00000, 10 输出: 1024.00000
基本思路:1. 递归法;2.快速幂法;
class Solution(object):
"""
方法1:递归
时间复杂度:递归调用次数O(logn)
空间复杂度:递归调用栈所占空间O(logn)
"""
def myPow(self, x, n):
"""
:type x: float
:type n: int
:rtype: float
"""
if n == 0: return 1
if n < 0: # 需要考虑负数,如果不考虑,2^-2这个case,首先变为(2*2)^-1,下一层递归又变为(2*2)^-2
x, n = 1 / x, -n
if n % 2:
return x * self.myPow(x, n - 1) # 如果n为奇数,n-1即可,在下一轮递归中即为偶数,可二分
return self.myPow(x * x, n / 2)
"""
方法2:快速幂
时间复杂度:O(logn) -- 分而治之,n不断进行二分
空间复杂度:O(1)
"""
def myPow(self, x, n):
if n == 0: return 1
if n < 0:
x, n = 1 / x, -n
ans = 1
while n: # case1: x^5 = x^(2^2 + 2^0) -- 5(二进制:101)
if n & 1: # case2: x^10= x^(2^3 + 2^1) -- 5(二进制:1010)
ans *= x
x *= x # x^(2^0) --> x^(2^1) --> x^(2^2) --> x^(2^3)...
n >>= 1
return ans
基本思路:
1. 穷举所有样本对,正例样本比负例样本预测分数高+1,相等+0.5,否则+0;最后除以总对数;
2. 直接根据公式计算;
基本思路:
"""
要求输出整数,这里是舍弃小数部分后的整数
"""
class Solution(object):
def mySqrt(self, x):
"""
:type x: int
:rtype: int
"""
lo, hi = 0, x #// 2 + 1 这部分加和不加都没有影响
while (lo < hi):
mid = (lo + hi + 1) >> 1
if mid * mid > x:
hi = mid - 1
else:
lo = mid
return lo
"""
不能这样写的原因是8的sqrt()之后取的是2,而不是3; mid = 2的时候,会执行lo = mid + 1;
只能是从较大值往较小值出缩减,比如mid = 3的时候,mid * mid > x,那么最终结果一定是< mid,所以右边界hi = mid - 1,同理,当剩下最后两个值时,取的需要是右边界的数字,所以需要mid = (lo + hi + 1) >> 1
"""
lo, hi = 0, x # // 2 + 1 这部分加和不加都没有影响
while (lo < hi):
mid = (lo + hi) >> 1
if mid * mid < x:
lo = mid + 1
else:
hi = mid
return lo
"""
要求保留精度
方法1:二分法
时间复杂度:O(logn)
"""
from math import sqrt
def sqrt_binary(x):
lo, hi = 0.0, x # 这里一定要注意变为float形式,否则之后做运算的时候一直是int形式
while lo <= hi:
mid = lo + (hi - lo) / 2 # 这里也不能写成位运算的形式,只有int可以应用位运算
ans = mid
if abs(mid * mid - x) <= 1e-6:
break
elif mid * mid < x:
lo = mid
else:
hi = mid
return ans
"""
方法1:牛顿法
基本思路:相当于求解 f(x) = x^2 - A的根,这里的A是sqrt(A)需要求的.
迭代公式Xk+1 = Xk - f(x) /f'(x) = X - (x^2 - A)/(2 * x) = 0.5 * (x + A / x)
时间复杂度
"""
def sqrt_newton(x):
ans = x
while abs(ans * ans - x) > 1e-6:
ans = (ans + x / ans) / 2
return ans
基本思路:注意这一点--parentNum = len(li) // 2 - 1 # 这个需要再研究一下, (貌似完全二叉树才是这种情况,index在parentNum之后的都没有leaf node了)
def createTree(alist):
"""
alist: list
"""
li = []
for a in alist:
li.append(TreeNode(a))
parentNum = len(li) // 2 - 1 # 这个需要再研究一下, (貌似完全二叉树才是这种情况,index在parentNum之后的都没有leaf node了)
for i in range(parentNum + 1):
leftIndex = 2 * i + 1
rightIndex = 2 * i + 2
if leftIndex < len(li): li[i].left = li[leftIndex]
if rightIndex < len(li):li[i].right= li[rightIndex]
return li[0]
基本思路:见代码
def deleteduplicate(head):
if not head: return head
p = head
while p.next:
if p.val == p.next.val:
p.next = p.next.next
else:
p = p.next
return head
快排
基本思路:非递归采用栈的思想,先压右边界,再压左边界;
递归:平均时间复杂度O(nlogn);平均空间复杂度O(logn)--递归调用栈;
当pivot选择不合理,退化为链表时,最坏时间复杂度O(n * n) ;最坏空间复杂度O(n),每一层递归调用栈是一个元素;
非递归:其实就是用stack来模拟函数调用栈;时间复杂度为O(nlogn);
颜色分类
题目:给定一个包含红色、白色和蓝色,一共 n 个元素的数组,原地对它们进行排序,使得相同颜色的元素相邻,并按照红色、白色、蓝色顺序排列。此题中,我们使用整数 0、 1 和 2 分别表示红色、白色和蓝色。
示例:输入: [2,0,2,1,1,0] 输出: [0,0,1,1,2,2]
基本思路:快排的partition思想,分四个区域(0区域,1区域,未处理区域 and 2区域)
数组排成最小的数
题目:输入一个非负整数数组,把数组里所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个。
示例:输入: [3,30,34,5,9]
输出: "3033459"
基本思路:基本沿用快排框架,更改比较大小的规则即可;
最大数
题目:给定一组非负整数,重新排列它们的顺序使之组成一个最大的整数。
示例:输入: [3,30,34,5,9]
;输出: 9534330
基本思路:与上一题思路完全相同;
from random import randint
def partition(nums, lo, hi):
"""
nums: list for sort
lo: the start index
hi: the last index
"""
i, j = lo, hi + 1
ind = randint(lo, hi)
nums[lo], nums[ind] = nums[ind], nums[lo]
pivot = nums[lo]
while i + 1 < j:
if nums[i+1] <= s[i]:
nums[i+1], nums[i] = nums[i], nums[i+1]
i += 1
else:
nums[i+1], nums[j-1] = nums[j-1], nums[i+1]
j -= 1
return i
def quick_sort(nums, lo, hi ):
if lo < hi:
pivot = partition(nums, lo, hi)
quick_sort(nums, lo, pivot - 1)
quick_sort(nums, pivot + 1, hi)
return nums
def quick_sortStack(nums, lo, hi):
if hi <= lo: return
stack = []
stack.append(hi) # 右边界先入栈,左边界后入栈;出栈时,按照left, and then right出栈
stack.append(lo)
while stack:
lo = stack.pop()
hi = stack.pop()
pivot = partition(nums, lo, hi)
if lo < pivot - 1:
stack.append(pivot-1)
stack.append(lo)
if hi > pivot + 1:
stack.append(hi)
stack.append(pivot + 1)
return nums
######################荷兰国旗######################
class Solution(object):
def sortColors(self, nums):
"""
:type nums: List[int]
:rtype: None Do not return anything, modify nums in-place instead.
"""
size = len(nums)
if size < 2: return nums
k, i, j = -1, -1, size
while i + 1 < j:
if nums[i+1] == 2:
nums[i+1], nums[j-1] = nums[j-1], nums[i+1]
j -= 1
elif nums[i+1] == 1:
i += 1
else:
nums[i+1], nums[k+1] = nums[k+1], nums[i+1]
k += 1
i += 1
return nums
######################把数组排成最小的数######################
class Solution(object):
def minNumber(self, nums):
"""
:type nums: List[int]
:rtype: str
"""
from random import randint
nums = [str(i) for i in nums]
def partition(nums, lo, hi):
i, j = lo, hi + 1
ind = randint(lo, hi)
nums[lo], nums[ind] = nums[ind], nums[lo]
pivot = nums[lo]
while i + 1 < j:
if nums[i + 1] + pivot <= pivot + nums[i + 1]:
nums[i + 1], nums[i] = nums[i], nums[i + 1]
i += 1
else:
nums[i + 1], nums[j - 1] = nums[j - 1], nums[i + 1]
j -= 1
return i
def fastsort(nums, lo, hi):
if hi <= lo: return
pivot = partition(nums, lo, hi)
fastsort(nums, lo, pivot - 1)
fastsort(nums, pivot + 1, hi)
size = len(nums)
if size == 0: return ''
if size == 1: return str(nums[0])
fastsort(nums, 0, len(nums)-1)
return ''.join(nums)
######################把数组排成最大的数######################
class Solution(object):
def largestNumber(self, nums):
"""
:type nums: List[int]
:rtype: str
"""
from random import randint
nums = [str(i) for i in nums]
def partition(nums, lo, hi):
i, j = lo, hi + 1
ind = randint(lo, hi)
nums[lo], nums[ind] = nums[ind], nums[lo]
pivot = nums[lo]
while i + 1 < j:
if nums[i + 1] + pivot <= pivot + nums[i + 1]:
nums[i + 1], nums[i] = nums[i], nums[i + 1]
i += 1
else:
nums[i + 1], nums[j - 1] = nums[j - 1], nums[i + 1]
j -= 1
return i
def fastsort(nums, lo, hi):
if hi <= lo: return
pivot = partition(nums, lo, hi)
fastsort(nums, lo, pivot - 1)
fastsort(nums, pivot + 1, hi)
size = len(nums)
if size == 0: return ''
if size == 1: return str(nums[0])
fastsort(nums, 0, len(nums)-1)
for e in nums: ## 目的是为了处理[0, 0]这个bad case;
if e != '0':
return ''.join(nums[::-1])
return '0'
基本思路:
1. 等概率抽样
每次从n个元素中利用随机函数逐个抽取,如果发现该次抽取的元素在之前已经被成功抽取过,则重新抽取直至抽到新元素;
缺点:随着m的增大,越到后面重复的概率越高;cost比较大;
2. 蓄水池抽样
先将前m个元素放入蓄水池中,从m+1个元素开始抽取,同时以m / n的概率替换掉已经存在于蓄水池中的数字;(数学归纳法证明)
"""
方法1:等概率抽样
每次从n个元素中利用随机函数逐个抽取,如果发现该次抽取的元素在之前已经被成功抽取过,则重新抽取直至抽到新元素;
缺点:随着m的增大,越到后面重复的概率越高;cost比较大;
"""
"""
方法2:蓄水池抽样
先将前m个元素放入蓄水池中,从m+1个元素开始抽取,同时以m / n的概率替换掉已经存在于蓄水池中的数字;
"""
from random import randint
def sampling(n, m):
size = len(n)
for i in range(m, size): # 0..m-1总共m个数
j = randint(0, i) # randint(a, b) 包括a和b
if j < m:
n[i], n[j] = n[j], n[i]
return n[:m]
-->下面一题则是应用:此题是数组,下一题是链表
基本思路:时间复杂度O(nlogn) and 空间复杂度O(n)
def merge_sort(nums):
size = len(nums)
if size < 2: return nums
mid = size // 2
left = merge_sort(nums[:mid])
right = merge_sort(nums[mid:])
return merge(left, right)
def merge(left, right):
ans = []
i = j = 0
while i < len(left) and j < len(right):
if left[i] <= right[j]: # 这里一定要取等号,保持归并的稳定性
ans.append(left[i])
i += 1
else:
ans.append(right[j])
j += 1
ans += left[i:] # 如果right先遍历完所有元素,在最后append上left的剩余元素
ans += right[j:] # 如果left先遍历完所有元素,在最后append上right所有剩余元素
return ans
题目:在 O(n log n) 时间复杂度和常数级空间复杂度下,对链表进行排序。
示例:输入: 4->2->1->3 输出: 1->2->3->4
基本思路:归并排序
"""
方法:时间复杂度为O(nlogn) --> 归并排序
"""
class Solution(object):
def sortList(self, head):
"""
:type head: ListNode
:rtype: ListNode
"""
def merge2List(l1, l2):
if not l1: return l2
if not l2: return l1
if l1.val <= l2.val:
l1.next = merge2List(l1.next, l2)
return l1
else:
l2.next = merge2List(l1, l2.next)
return l2
# 快慢指针找到中点,切分两条链表
if not head:
return head
sl = fa = head
while fa and fa.next:
fa, sl = fa.next.next, sl.next
l1, l2 = head, sl.next
sl.next = None
left = self.sortList(l1)
right = self.sortList(l2)
return merge2List(left, right)
基本概念:单例模式(Singleton Pattern)是一种常用的软件设计模式,该模式的主要目的是确保某一个类只有一个实例存在。当你希望在整个系统中,某个类只能出现一个实例时,单例对象就能派上用场。
1. 使用模块
2. 使用装饰器
3. 使用类
4. 基于__new__方法实现(推荐使用,方便)--属于饿汉模式?:
原理:在python的类创建对象的过程中,先通过__new__方法实例化一个对象(PS:在没有自己定义该方法时会默认调用object.new),然后才执行__init__方法对实例化的对象进行各项初始化复制操作。
实现单例时,为了保证线程安全需要在内部加入锁。当实例化一个对象时,是先执行了类的__new__方法(我们没写时,默认调用object.__new__),实例化对象;然后再执行类的__init__方法,对这个对象进行初始化,所有可以基于这个,实现单例模式。
"""
方法1:实现1 -- 不考虑加锁
"""
class Singleton:
def __new__(cls, *args, **kwargs):
if not hasattr(cls, '_instance'): # hasattr()函数用于判断对象是否包含对应的属性
cls._instance = super().__new__(cls)
# cls._instance = super(Singleton, cls).__new__(cls)
return cls._instance
"""
方法1:实现2
"""
class Singleton:
_instance = None # 定义一个类属性做判断
def __new__(cls):
if cls._instance == None:
# 如果_instance为空证明是第一次创建实例
# 通过父类的__new__(cls)创建实例
cls._instance == object.__new__(cls)
return cls._instance
else:
# 返回上一个对象的引用
return cls._instance
"""
方法2:考虑加锁
"""
import threading
class Singleton(object):
_instance_lock = threading.Lock()
def __init__(self):
pass
def __new__(cls, *args, **kwargs):
if not hasattr(Singleton, "_instance"):
with Singleton._instance_lock: # 加锁防止多线程环境中两个线程同时判断到上一行代码为True,同时创建该类的实例
if not hasattr(Singleton, "_instance"):
# 调用object类的__new__方法
Singleton._instance = object.__new__(cls)
# Singleton._instance = super(Singleton, cls).__new__(cls, *args, **kwargs)
return Singleton._instance
obj1 = Singleton()
obj2 = Singleton()
print(obj1,obj2)
def task(arg):
obj = Singleton()
print(obj)
for i in range(10):
t = threading.Thread(target=task,args=[i,])
t.start()
"""
可以通过直接从threading.Thread继承创建一个新的子类,并实例化后调用start()方法启动新线程,即它调用了线程的run()方法:
--通过继承treading.Thread,并重写run函数
--join() 调用这个方法的时候,主进程会在这里停住,等待该线程进行完毕再继续往下执行;
Why join()? -- 线程执行时间过长,而且要在后续代码中使用线程运行的结果,这样可以使用join,防止主线程比子线程先结束,
或者后续代码在线程结束之前就运行了从而获取不到线程处理结果。
使用join会导致进程阻塞,这个阻塞并不是目的,只是表现。并不是为了阻塞线程而使用join.
"""
import threading
import time
exitFlag = 0
class myThread(threading.Thread):
def __init__(self, threadID, name, counter):
threading.Thread.__init__(self)
self.threadID = threadID
self.name = name
self.counter = counter
def run(self): # 重写run函数
print("开始线程:" + self.name)
print_time(self.name, self.counter, 5)
print("退出线程:" + self.name)
def print_time(threadName, delay, counter):
while counter:
if exitFlag:
threadName.exit()
time.sleep(delay)
print("%s: %s" % (threadName, time.ctime(time.time())))
counter -= 1
# 创建新线程
thread1 = myThread(1, "Thread-1", 1)
thread2 = myThread(2, "Thread-2", 2)
# 开启新线程
thread1.start()
thread2.start()
thread1.join()
thread2.join()
print("退出主线程")
基本思路:两个for循环,外层遍历2-100,2是最小的质数,内层从2开始到i结束(质数:除了1和本身,没有其他因子)
i=2
for i in range(2,100): # 取i从2开始,2 最小的质数
for j in range(2,i): #j在i的范围内取,range区间取不到i,看i是否有其他因数
if i%j==0:
break
if i%j!=0: #最好用if 如果用else不规范
print(i)
基本思路:
1. 有n!种组合,生成n!种组合之后,在里面任意选一种即可,也就是等概率地给出这n!结果中任意一个。但是复杂度太高。
2. 关键点:对于生成的排列,每一个元素都能独立地等概率地出现在每一个位置。或者说每一个位置能独立等概率地放置每个元素。
3. 公平性证明(eg. 仅有5个元素,最后一位元素被固定在最后一个位置的概率是1/5;倒数第二个元素被固定在倒数第二个位置的概率是4/5 * 1/4 = 1/5,其他同理;)
for i in range(len(nums)-1, -1, -1):
swap(nums[i], nums[randint(0, i)]) # randint(0, i) 生成[0, i]之间的随机整数
题解参考:(不完成正确)Python利用三元组完成稀疏矩阵相乘
import numpy as np
def sparseToTriple(matrix):
"""
matrix: sparse matrix
return: triple dict, rows, columns
"""
m, n = np.shape(matrix)
triple = {}
for i in range(m):
for j in range(n):
if matrix[i][j] != 0:
triple[(i, j)] = matrix[i][j]
return triple, [m, n]
def multiTriple(tripleA, shapeA, tripleB, shapeB):
"""
return: tripleA * tripleB, shape
"""
if shapeA[1] != shapeB[0]: return 'Error' # 无法相乘
multiMatrix = {}
for (i, j) in tripleA:
for (m, n) in tripleB:
if j == m: # matrixA.column == matrix.row时,可以相乘,对应的结果放在(matrixA.row, maxtrixB.column)
if not multiMatrix.get((i, n)):
multiMatrix[(i, n)] = tripleA[(i, j)] * tripleB[(m, n)]
else:
multiMatrix[(i, n)] += tripleA[(i, j)] * tripleB[(m, n)]
# 相乘的结果为0,也需要删除
for key in multiMatrix:
if multiMatrix[key] == 0:
multiMatrix.pop(key)
return multiMatrix, [shapeA[0], shapeB[1]]
def tripleToSparse(triple, shape):
outMatrix = zeros([shape[0], shape[1]])
for point in triple:
outMatrix[point[0]][point[1]] = triple[(point[0], point[1])]
return outMatrix
def matrixMultiple(matrixA, matrixB):
rowA, colA = np.shape(matrixA)
rowB, colB = np.shape(matrixB)
if colA != rowB:
print("The two matries doesn't match")
return -1
tripleA, shapeA = sparseToTriple(matrixA)
tripleB, shapeB = sparseToTriple(matrixB)
multipleTriples, shape = multiTriple(tripleA, shapeA, tripleB, shapeB)
outMatrix = tripleToSparse(multipleTriples, shape)
return outMatrix
基本思路:前后双指针;类似于归并排序,但是是从大到小;
def differentAbs(nums):
size = len(nums)
if size <= 1: return size
i, j = 0, size - 1
if abs(nums[j]) >= abs(nums[i]):
curNum = abs(nums[j])
j -= 1
else:
curNum = abs(nums[i])
i += 1
count = 1
while i < j:
if abs(nums[j]) >= abs(nums[i]):
if abs(nums[j]) != curNum:
curNum = abs(nums[j])
count += 1
j -= 1
else:
if abs(nums[i]) != curNum:
curNum = abs(nums[i])
count += 1
i += 1
print(curNum, i, j, count)
return count
基本思路:动态规划状态方程dp[i][j] = dp[i-1][j] + dp[i][j-1]
Follow up:空间and时间复杂度优化--只保留两行,空间复杂度优化为O(2*n)
def sumPath(m, n):
dp = [[0 for _ in range(n)] for _ in range(2)]
for i in range(n):
dp[0][i] = 1
j = 0
while j < m:
dp[1][0] = 1
for i in range(n):
dp[1][i] = dp[1][i-1] + dp[0][i]
dp[0] = dp[1]
return dp[-1][-1]
题目:手上一叠扑克牌,从扑克牌的最上方开始拿一张放到桌面上,再将下一张牌放到最底部,重复这个步骤直至手上没有扑克牌为止;<给定桌上的扑克牌的顺序,求最初手上的一堆牌的顺序>
基本思路:逆向思考;从桌子上的最后一张牌开始拿回手上,再将最底部的牌放至最顶部,重复这个步骤直到桌上的牌全部都被拿至手上。
def poker(nums):
if not nums: return None
size = len(nums)
if size == 1: return nums[0]
ans = []
while nums:
ans.append(nums.pop())
ans.append(ans.pop(0))
return ans