最坏情况为线性时间的选择算法——算法导论P123

     最坏情况为线性时间的选择算法select与前面的随机选择算法randomied_select的区别在于其主元不是随机选择的(具体如何选择见算法导论P123)。

      当我们随机选择主元时,有可能会出现所有元素都在某一边的情况,这种情况使得算法的效率会很低。为了改进这一点,可先将数组划分成若干组,每组五个元素(最后一组可以不足五个元素,这里也可以每组7,9...个元素,但是3个元素不可以),求每组的中位数,选取这些中位数的中位数为主元,这样就可保证每次划分的两边元素个数都不会少于3n/10-6,从而提高算法效率。

      算法主要步骤:见P123.

      代码(小白一枚,有错误和不当之处,还请指正): 

​
# !/usr/bin/env python
# -*- coding=utf-8 -*-
# Summary: 最坏情况为线性的选择算法【此算法跟随机选择算法的区别是其主元不是随机选择的:首先将数组分为n/5组(这里5换成3不行,
#          7,9..可以),除了最后一组可以不足五个元素,其余每组五个元素;寻找n/5组中每组的中位数(对每组元素进行插入排序,然
#          后确定有序元素的中位数);对上一步中得到的中位数递归调用select,找到其中位数x,将x作为主元】;最坏情况为线性时间。
# Author:  HuiHui
# Date:    2019-02-27
# Reference: 网易公开课-算法导论P123
#################################

def local_sort(A,p,r):#对数组A的p to r部分进行插入排序
    for j in range(p+1,r+1):
        x=A[j]
        for i in range(j,p-1,-1):#找x的正确位置i;注意不能将x与A[i](i从j-1开始到p)比较,直到遇到A[i]<=x时认为i+1为x的正确
                                 #位置。因为若x的正确位置其实是p=i+1,我们需遇到A[i=p-1]<=x,但这超出了i的范围。
            if A[i-1]>x:
                A[i]=A[i-1]
            else:
                break
        A[i]=x
    return A

def median(A, p, r ):####分组并排序,记录每组中位数,并返回中位数对应到A中位置t(即主元位置)

    ####分组并排序,记录每组中位数
    long = r - p + 1  # A[p]到A[r]的长度
    if long // 5 == 0:
        return r
    B = []
    for i in range(1, long // 5 + 1):
        A = local_sort(A, p + 5 * (i - 1), p + 5 * i - 1)  # 逐段对每五个元素进行插入排序
        B.append(A[p + 5 * i - 3])  # 记录每组中位数
    B_0 = B
    mid = select(B_0, 0, len(B_0) - 1, len(B_0) // 2)  # 用select找出上述所有中位数的中位数

    ##????????给出中位数对应到数组B中的位置t_0和A中位置t(即主元位置)
    for i in range(0, len(B)):
        if B[i] == mid:
            t_0 = i
            t = p + 5 * (t_0 + 1) - 3
            break
    return t

def modified_partition(A,p,r):#随机划分,返回主元重排后的位置
    t=median(A, p, r )#主元
    A[t],A[r]=A[t],A[r]#主元放末尾
    x=A[r]
    m=p-1
    for j in range(p,r):
        if A[j]<=x:
            m=m+1
            A[m],A[j]=A[j],A[m]
    A[m+1],A[r]=A[r],A[m+1]
    return m+1

def select(A,p,r,i):
    if p==r:
        return A[p]
    q=modified_partition(A,p,r)
    k=q-p+1#
    if k==i:#递归基线条件
        return A[q]
    if k>i:
        return select(A,p,q-1,i)#在q之前得数组中查找
    if k<i:
        return select(A,q+1,r,i-k)#在q之后得数组中查找

def MININU(A):#找最小值的方法(i=1)
    min=A[0]
    for j in range(1,len(A)):
        if min>A[j]:
            min=A[j]
    return min

###############################
def main():
    Q= [89, 10, 21, 5, 2, 8, 33, 27, 63,55,66]
    B=MININU(Q)
    A=select(Q,0,10,11)
    print(A)
    print(B)

if __name__ == '__main__':  # 当.py文件被直接运行时,if __name__ == '__main__'之下的代码块将被运行;当.py文件以模块形式被导入时,if __name__ == '__main__'之下的代码块不被运行。
    main()


########总结###########
#1.?处是否可以用其他方法
#2.主元一定要放末尾吗

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值