计算机算法-线性寻找第k小(大)元素(Top K)BFPRT算法

问题描述

从n个元素的序列中选出第k大(第k小)的元素(求第K大就是求第n-K+1小),BFPRT可以保证在最坏情况下仍为线性时间复杂度。整体的思想与快速排序思想相似,借助了分治的思想,以及快排的分区间思想。

算法步骤

(1)对数组元素进行分组,每组5个元素,剩余不足五个元素的为一组;
(2)找出每一组的中位数,这里可以用任意的排序方法,因为只有五个元素,所以复杂度为常数级;
(3)递归调用上一个算法找到中位数中的中位数,一直找到只剩一个元素,记为m;
(4)用m来分割数组,使m前面的都比m小,后面的都比m大。
(5)判断m下标和k的大小,来确定目标元素位置;
(6)确定位置之后再次进行递归;

代码实现

def merge(lst, l, mid, r):
    left = lst[l:mid + 1]
    right = lst[mid + 1:r + 1]  #
    k = l
    while left and right:
        if left[0] <= right[0]:
            lst[k] = left.pop(0)
        else:
            lst[k] = right.pop(0)
        k += 1
    tail = left if left else right
    for n in tail:
        lst[k] = n
        k += 1


def mergesort(lst, l, r):
    if l < r:
        mid = (l + r - 1) // 2
        mergesort(lst, l, mid)
        mergesort(lst, mid + 1, r)
        merge(lst, l, mid, r)
    return lst


def partion(a, m, m_index):
    # 对a进行排序,使得比m小的元素放在m前面,比m大的元素放在m后面
    # 输入:m_index(m在a中的index)
    # 返回m前面, m后面各自元素的数目,以及m在新数组中的index

    # 将m与数组第一个元素交换位置,然后即可像快速排序一样将所有元素以m为中间元素
    # 分到左右两边
    tmp = a[0]
    a[0] = m
    a[m_index] = tmp

    i = 0
    j = len(a) - 1
    control_m = a[0]
    while i < j:
        while i < j and a[j] >= control_m:
            j -= 1

        a[i] = a[j]

        while i < j and a[i] <= control_m:
            i += 1
        a[j] = a[i]

    # 此时i = j, a[i]应该是最终的控制关键字所在位置
    a[i] = control_m
    print("m:{},after partion, a:{}".format(m, a))

    return i, len(a) - i - 1, i


def Bfprt(a, k):
    # 得到a中第k大的元素

    if len(a) < 5:
        # 元素数目不足5个时,直接排序,之后取下表为k-1的数,即为第k大的元素
        mergesort(a, 0, len(a) - 1)
        return a[k - 1]

    total_num = len(a)
    splits = total_num // 5  # 一共分成这么多组  //出来是整数  /为float型
    #print(splits)
    # 获取每一个分组的中位数
    split_medians = []
    for i in range(splits):
        cur = mergesort(a[i * 5:(i + 1) * 5], 0, 4)
        #print (cur)
        mid = cur[2]
        split_medians.append(mid)

    # 递归调用Bfprt算法,求这些中位数的中第splits//2大的元素,也就是中位数的中位数
    m = Bfprt(split_medians, splits // 2)
    # 求出m在a中的index
    m_index = [i for i in range(total_num) if a[i] == m][0]

    # 根据m对a进行划分,线性复杂度
    # num1, num2:小于m的元素的数目, 大于m的元素数目
    num1, num2, m_index = partion(a, m, m_index)
    if k == num1 + 1:#第k大为m
        return m
    elif k <= num1:
        # 在s1集合中
        return Bfprt(a[:m_index], k)
    else:
        # 在s2集合中
        return Bfprt(a[m_index + 1:], k - 1 - m_index)


a = [4, 2, 13, 8, 3, 10, 15, 18, 11, 1, 16, 17, 12, 19, 0, 6, 5, 7, 9, 14]
print("请输入k值:")
t = input()
k=int(t)
print(Bfprt(a, k))
  • 5
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值