第 9 章 中位数和顺序统计量
顺序统计量
- 在一个由n个元素组成的集合中,第i个顺序统计量(order statistic)是该集合中第i小的元素
- 例如,在一个元素集合中,最小值是第1个顺序统计量,最大值是第n个顺序统计量(i=n)
一个中位数是它所属集合的中点元素
9.1 最小值和最大值
9.1.1 在一个有n个元素的集合中,需要做多少次比较才能确定其最小元素呢
例程: MINIMUM算法
结论: 为了确定最小值,至少要做
n-1
次比较
9.1.2 同时找到最小值和最大值
我们只需要最多
3 * Math.floor(n/2)
次比较就可以同时找到最小值和最大值具体方法如下
- 我们需要记录已知的最小值和最大值
- 我们将一对输入元素相互进行比较
- 我们将较小的与当前最小值比较,将较大的与当前最大值比较,这样,对每两个元素共需要3次比较
如何设定已知的最小值和最大值的初始值依赖于n是奇数还是偶数
- 如果n是奇数,我们就将最小值和最大值的初值都设为第一个元素的值,然后成对地处理余下地元素
- 如果n是偶数,就对前两个元素做一次比较,以决定最小值和最大值的初值,然后与n是奇数的情形一样,成对处理剩下的元素
代码实现: 同时求解最大值和最小值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
def minimum_and_maximum(arr): arr_len = len(arr) print(arr_len) if arr_len == 0: raise IndexError("param: arr is empty") if arr_len % 2: max_val, min_val = arr[0], arr[0] start = 1 else: max_val, min_val = (arr[0], arr[1]) if (arr[0] > arr[1]) else (arr[1], arr[0]) start = 2 for i in range(start, arr_len, 2): temp_max, temp_min = (arr[i], arr[i + 1]) if (arr[i] > arr[i + 1]) else (arr[i + 1], arr[i]) if temp_max > max_val: max_val = temp_max if temp_min < min_val: min_val = temp_min return max_val, min_val if __name__ == '__main__': arr = [-3, 19, -2, 4, 1, 3, 2, 16, 9, 10, 14, 8, 7, 17, 0, -1, 18] max_val, min_val = minimum_and_maximum(arr) print(max_val, min_val)
9.2 期望为线性时间的选择算法
选择算法: 用于选择序列中的第i个顺序统计量
伪代码
解释
- 第一行检查递归的基本情况
- 若不符合基本情况,第三行调用
RANDOMIZED-PARTITION
将数组A[p...r]
划分为两个子数组A[p..q-1]
和A[q+1..r]
,使得A[p..q-1]
中的每个元素都小于或等于A[q]
,而A[q+1..r]
中的元素都大于A[q]
- 然后检查
A[q]
是否是第i个统计量 - 若不是则继续递归的调用此算法
代码实现:
RANDOMIZED-SELECT
算法1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
import random def randomized_partition(arr, p, r): t = random.randint(p, r) arr[t], arr[r] = arr[r], arr[t] i = p - 1 for j in range(p, r): if arr[j] < arr[r]: i += 1 arr[i], arr[j] = arr[j], arr[i] arr[i + 1], arr[r] = arr[r], arr[i + 1] return i + 1 def randomized_select(arr, p, r, i): if p == r: return arr[p] q = randomized_partition(arr, p, r) k = q - p + 1 if k == i: return arr[q] elif k > i: return randomized_select(arr, p, q, i) else: return randomized_select(arr, q+1, r, i - k) if __name__ == '__main__': arr = [4, 1, 3, 2, 16, 9, 10, 14, 8, 7] o = randomized_select(arr, 0, len(arr) - 1, 1) print(o)
时间复杂度
- 最坏情况
- O(n2)
- 每次划分都极不走运地总是按余下地元素中最大地来进行划分
- 平均情况
- O(n)
- 又因为它是随机化的,所以不存在一个特定的会导致其最坏情况发生的输入数据
- 最坏情况
9.3 最坏情况为线性时间的选择算法
9.3.1 简介
SELECT
算法通过对输入数组的递归划分来找出所需元素,但是,在该算法中能够保证得到对数组的一个好的划分
9.3.2 算法步骤
将输入数组的n个元素划分为
floor(n / 5)
组,每组有5个元素,且至多只有一组由剩下的n % 5
个元素组成寻找这
ceil(n / 5)
组中每一组的中位数: 首先对每组元素进行插入排序, 然后确定每组有序元素的中位数对第二步中找到的
ceil(n / 5)
个中位数,递归调用SELECT
以找出其中位数x
通过
PARTITION
函数按照x
对输入数组进行划分, 假设x
是数组的第k
个顺序统计量如果
i=k
,则返回x
; 如果i<k
,则在低区递归调用SELECT
来找出第i
小的元素;如果i>k
,则在高区递归查找第i-k
小的元素
9.3.3 算法实现:Python
|
|
9.3.4 时间复杂度
最坏情况下的递归式
最坏情况下的时间复杂度
O(n)
, 带入主方法可以证明