中位数:分为上中位数和下中位数,上中位数即长度除2向上取整,下中位数为长度除2向下取整。一般说中位数默认下中位数。

顺序统计量:即第几小的数。一个数组的第一顺序统计量即为最小的数,第n顺序统计量即为最大的数。

1.最小值和最大值

如果我们要找出一个序列中的最小值,我们需要进行n-1次比较,同样,如果我们要找出一个序列中的最大值,也需要进行n-1次比较。

那如果我们需要同时找出最小值和最大值呢,那就需要2n-2次比较。

但是如果我们不再是用一个数与已经记录的最大最小值进行比较,而是用两个数先比较,然后取较小的一个和最大值比较,较大的和最大值比较,这样三次比较就可以完成两个数的比较,较上面的方法节约了一次比较,因而我们只需要

次比较即可。

2.期望为线性时间的选择算法

我们知道,快速排序的每一趟的结果,标记元素都会落在它最终结果的位置上,因此我们可以利用这个性质,达到以线性时间来寻找序列的顺序统计量。

def quick_sort(self, init_list, i, left, right):
    """
    快速排序
    :param init_list: 排序列表
    :param i: 比较元素索引
    :return:
    """
    length = len(init_list)
    # 计算左右索引
    left_index = left
    right_index = right
    # 将比较元素放到最后,防止它是最大值时不进行交换
    temp = init_list[i]
    init_list[i] = init_list[right_index]
    init_list[right_index] = temp
    # 比较元素值
    flag = init_list[right_index]
    # 比较元素索引(因为交换元素位置会变)
    flag_index = right_index
    # 重复执行直至左右标记重叠
    while left_index != right_index:
        # 从左向右找比flag大的数交换
        for i in range(left_index, right_index):
            if flag < init_list[left_index]:
                temp = flag
                init_list[flag_index] = init_list[left_index]
                flag_index = i
                init_list[left_index] = temp
                break
            else:
                left_index += 1
        # 从右向左找比flag小的数交换
        for i in range(right_index, left_index, -1):
            if flag > init_list[right_index]:
                temp = flag
                init_list[flag_index] = init_list[right_index]
                flag_index = i
                init_list[right_index] = temp
                break
            else:
                right_index -= 1
    return init_list, left_index
def randomized_select(self, init_list, order_statistic_idx, left, right):
    """
    利用快速排序找出第n个顺序统计量
    :param init_list:
    :param order_statistic_idx:
    :return:
    """
    if len(init_list) == 1:
        return init_list
    random_index = random.randint(left, right)
    init_list, flag_idx = self.quick_sort(init_list, random_index, left, right)
    # 这里进行了剪枝,不需要想快排一样左右均排序,选择一侧的进行排序即可
    if flag_idx == order_statistic_idx:
        return init_list[flag_idx]
    elif flag_idx > order_statistic_idx:
        return self.randomized_select(init_list, order_statistic_idx, left, flag_idx - 1)
    else:
        return self.randomized_select(init_list, order_statistic_idx, flag_idx + 1, right)

3.最坏情况为线性时间的选择算法

通过将序列分区排序查找中位数的操作来确定整个整个序列的(近似)中位数,然后利用这个中位数执行一趟快排,然后就和上面类似了。


0 条评论

发表回复

Avatar placeholder

您的电子邮箱地址不会被公开。 必填项已用 * 标注