这类问题的特征就是重新组织数组元素
1排序
注意几种特殊的排序问题(主要就是key-indexed sort)
1)元素值范围和下标是对应的且连续且没有重复(即元素取值是[0,n))则直接桶排序in place、O(n) 就可以排好序。
2 )元素值范围是下标域的子集且连续,可以有重复(即元素取值是[0,k],连续,k<=n),是第一种情况的一般化。也可以桶排序思想+计数排序思想 in place, O(n)的排好序
思想是:用元素值的桶作为该值的计数器,即counting sort中 count 数组和 原数组合二为一,即是数据数组又是key indexed counting 数组。区分就计数器用负数,负数就是计数器,正数是key,0则表示这个位置上的数据已经count了所以清除了。
2 shuffle
思路一:不变式是[0, i-1]里的元素是均匀分布的,加进a[i]后,[0, i]里的元素也是均匀分布的,处理的方式是随机从[0, i]位置选一个,和a[i]上的数交换
思路二:不变式是原数组的每个元素在[0, i-1]中每个位置出现的概率是相同的,加进位置i后,原数组每个元素在i上出现的概率是相同的,处理的方式是等概率的从剩下的数[i, n)里选一个数放到位置i
3 partition (2 way, 3 way, k way)
2 way:
逻辑: [0,i]是partition 1,(i, j) 是 partition 2, [j, end]是 未处理部分。
当一个新元素a[j]进来,如果属于partition 1 就和a[++i]交换,这样,partition 1增长了1,partition 2 shift了一下,(头放到尾后面);如果属于partition 2 则自然append在partition 2后面,什么也不用做。
3 way:
逻辑:[0,i]是partition 1,(i, j)是partition 2,[j, k)是未处理部分,[k, end]是partition 3。
当一个新元素a[j]进来,如果属于partition 1 则 和 a[++i]交换,j++处理下一个;如果属于partition 2则自然append到partition 2,不用做什么,j++处理下一个;如果属于partition 3则和a[--k]交换,第3情况相当于a[j]上又来了一个新的数,所以j不前进,继续处理这个数。循环终止条件是j 和 k相遇。
k way:
桶排序思想和计数排序思想结合,即1. 2)问题
4 interleave (奇偶partition)
interleave就暗示了奇偶。问题的实质就是将一个partition放在偶数下标位,另一个partition放在奇数下标位。
先扫一遍以确定哪个partition在偶数下标位,哪个partition在奇数下标位 :个数多的那个partition在偶数下标位(先开始)。双指针,一个管偶数下标位,一个管奇数下标位,分别定位到不满足partition条件的位置,交换。步长都是2。
def rerange(self, A):
num, n =0, len(A)
for e in A:
if e<0: num += 1
i, j = (0, 1) if num >= n-num else (1, 0)
while i < n and j < n:
while i < n and A[i] < 0: i += 2
while j < n and A[j] > 0: j += 2
if i < n and j < n: A[i], A[j] = A[j], A[i]
return A
3 错排
输入是一个具体排列,求所有元素都不在自己起始位置上的排列总数。
递推公式:D(n) = (n-1) * (D(n-2) + D(n-1))