【分治算法】【Python实现】线性时间选择

### 文章目录

  • [@[toc]](#文章目录 @[toc] 问题描述 随机选择算法 Python实现 时间复杂性 BFPRT算法 时间复杂性 Python实现)
  • [问题描述](#文章目录 @[toc] 问题描述 随机选择算法 Python实现 时间复杂性 BFPRT算法 时间复杂性 Python实现)
  • [随机选择算法](#文章目录 @[toc] 问题描述 随机选择算法 Python实现 时间复杂性 BFPRT算法 时间复杂性 Python实现)
  • [Python实现](#文章目录 @[toc] 问题描述 随机选择算法 Python实现 时间复杂性 BFPRT算法 时间复杂性 Python实现)
  • [时间复杂性](#文章目录 @[toc] 问题描述 随机选择算法 Python实现 时间复杂性 BFPRT算法 时间复杂性 Python实现)
  • [BFPRT算法](#文章目录 @[toc] 问题描述 随机选择算法 Python实现 时间复杂性 BFPRT算法 时间复杂性 Python实现)
  • [时间复杂性](#文章目录 @[toc] 问题描述 随机选择算法 Python实现 时间复杂性 BFPRT算法 时间复杂性 Python实现)
  • [Python实现](#文章目录 @[toc] 问题描述 随机选择算法 Python实现 时间复杂性 BFPRT算法 时间复杂性 Python实现)

个人主页:丷从心·

系列专栏:分治算法

学习指南:Python学习指南


问题描述

  • 给定线性序集中 n n n个元素和一个整数 k ( 1 ≤ k ≤ n ) k (1 \leq k \leq n) k(1≤k≤n),找出这 n n n个元素中第 k k k小的元素

随机选择算法

Python实现
python 复制代码
import random


def partition(nums, low, high):
    pivot_index = random.randint(low, high)
    pivot = nums[pivot_index]

    # 将 pivot 元素移动到列表的最右边
    nums[pivot_index], nums[high] = nums[high], nums[pivot_index]

    # 通过交换操作, 将小于 pivot 的元素移动到左边, 大于 pivot 的元素移动到右边
    i = low
    for j in range(low, high):
        if nums[j] < pivot:
            nums[i], nums[j] = nums[j], nums[i]

            i += 1

    # 将 pivot 元素放置到正确的位置
    nums[i], nums[high] = nums[high], nums[i]

    return i


def quick_select(nums, low, high, k):
    if low == high:
        return nums[low]

    # 划分数组, 并获取 pivot 元素的索引
    pivot_index = partition(nums, low, high)

    j = pivot_index - low + 1

    if j == k:
        # 如果 pivot 元素的索引等于 k, 则返回该元素
        return nums[pivot_index]
    elif j > k:
        # 如果 pivot 元素的索引大于 k, 则在左侧继续查找
        return quick_select(nums, low, pivot_index - 1, k)
    else:
        # 如果 pivot 元素的索引小于 k, 则在右侧继续查找
        return quick_select(nums, pivot_index + 1, high, k - j)


def find_kth_smallest(nums, k):
    if k < 1 or k > len(nums):
        raise ValueError('Invalid value of k')

    return quick_select(nums, 0, len(nums) - 1, k)


nums = [3, 1, 5, 2, 4]
k = 2

res = find_kth_smallest(nums, k)

print(f'第 {k} 小的元素为 {res}')
shell 复制代码
第 2 小的元素为 2
时间复杂性
  • 随机选择算法在最坏情况下需要 Ω ( n 2 ) \Omega(n^{2}) Ω(n2)时间,平均情况下需要 O ( n ) O(n) O(n)时间

BFPRT算法

  • 如果能在线性时间内找到一个划分基准,使得按这个基准划分出的两个子数组的长度都至少为原数组长度的 ε \varepsilon ε倍( 0 < ε < 1 0 < \varepsilon < 1 0<ε<1是某个常数),那么在最坏情况下用 O ( n ) O(n) O(n)时间就可以完成选择任务

    • 例如,若 ε = 9 / 10 \varepsilon = 9 / 10 ε=9/10,算法递归调用所产生的子数组的长度至少缩短 1 / 10 1 / 10 1/10,所以在最坏情况下,算法所需的计算时间 T ( n ) T(n) T(n)满足递归式 T ( n ) ≤ T ( 9 n / 10 ) + O ( n ) T(n) \leq T(9n / 10) + O(n) T(n)≤T(9n/10)+O(n),由此可得 T ( n ) = O ( n ) T(n) = O(n) T(n)=O(n)
  • 将 n n n个输入元素划分成 ⌈ n / 5 ⌉ \left\lceil n / 5 \right\rceil ⌈n/5⌉个组,每组 5 5 5个元素(除可能有一个组不是 5 5 5个元素外),用任意一种排序算法,将每组中的元素排好序,并取出每组的中位数,共 ⌈ n / 5 ⌉ \left\lceil n / 5 \right\rceil ⌈n/5⌉个

  • 递归调用找出这 ⌈ n / 5 ⌉ \left\lceil n / 5 \right\rceil ⌈n/5⌉个元素的中位数,如果 ⌈ n / 5 ⌉ \left\lceil n / 5 \right\rceil ⌈n/5⌉是偶数,就找它的两个中位数中较大的一个,然后以这个元素作为划分基准

  • 设所有元素互不相同,找出的基准 x x x至少比 3 ⌊ ( n − 5 ) / 10 ⌋ 3 \left\lfloor (n - 5) / 10 \right\rfloor 3⌊(n−5)/10⌋个元素大,至少比 3 ⌊ ( n − 5 ) / 10 ⌋ 3 \left\lfloor (n - 5) / 10 \right\rfloor 3⌊(n−5)/10⌋个元素小,当 n ≥ 75 n \geq 75 n≥75时, 3 ⌊ ( n − 5 ) / 10 ⌋ ≥ n / 4 3 \left\lfloor (n - 5) / 10 \right\rfloor \geq n / 4 3⌊(n−5)/10⌋≥n/4,所以按此基准划分所得的两个子数组的长度都至少缩短 1 / 4 1 / 4 1/4

时间复杂性
  • 设对 n n n个元素的数组调用算法需要 T ( n ) T(n) T(n)时间
  • 找中位数 x x x最多用 T ( n / 5 ) T(n / 5) T(n/5)时间
  • 按照算法所选的基准 x x x进行划分所得的两个子数组分别最多有 3 n / 4 3n / 4 3n/4个元素,无论对哪一个子数组调用算法都最多用 T ( 3 n / 4 ) T(3n / 4) T(3n/4)时间

T ( n ) ≤ { C 1 , n < 75 C 2 n + T ( n / 5 ) + T ( 3 n / 4 ) , n ≥ 75 T(n) \leq \begin{cases} C_{1} , & n < 75 \\ C_{2} n + T(n / 5) + T(3n / 4) , & n \geq 75 \end{cases} T(n)≤{C1,C2n+T(n/5)+T(3n/4),n<75n≥75

T ( n ) = O ( n ) T(n) = O(n) T(n)=O(n)

Python实现
python 复制代码
import statistics


def find_median_of_medians(arr):
    # 将数组划分为大小为 5 的子数组
    subarrays = [arr[i:i + 5] for i in range(0, len(arr), 5)]

    # 计算每个子数组的中位数
    medians = [statistics.median(subarray) for subarray in subarrays]

    # 如果元素数量小于等于 5, 直接返回中位数
    if len(medians) <= 5:
        return statistics.median(medians)

    # 递归调用中位数的中位数算法
    return find_median_of_medians(medians)


def linear_time_select(arr, k):
    # 找到中位数的中位数
    median_of_medians = find_median_of_medians(arr)

    # 将数组划分为三个部分
    less = [x for x in arr if x < median_of_medians]
    equal = [x for x in arr if x == median_of_medians]
    greater = [x for x in arr if x > median_of_medians]

    # 根据划分后的数组长度选择下一步操作
    if k <= len(less):
        # 在较小的部分递归查找第 k 小元素
        return linear_time_select(less, k)
    elif k <= len(less) + len(equal):
        # 第 k 小元素等于中位数的中位数
        return median_of_medians
    else:
        # 在较大的部分递归查找第 k 小元素
        return linear_time_select(greater, k - len(less) - len(equal))


arr = [3, 1, 5, 2, 4, 9, 7, 8, 6]
k = 5

res = linear_time_select(arr, k)

print(f'第 {k} 小的元素为 {res}')
shell 复制代码
第 5 小的元素为 5

相关推荐
m0_5945263023 分钟前
Python批量合并多个PDF
java·python·pdf
工业互联网专业37 分钟前
Python毕业设计选题:基于Hadoop的租房数据分析系统的设计与实现
vue.js·hadoop·python·flask·毕业设计·源码·课程设计
钱钱钱端44 分钟前
【压力测试】如何确定系统最大并发用户数?
自动化测试·软件测试·python·职场和发展·压力测试·postman
慕卿扬44 分钟前
基于python的机器学习(二)—— 使用Scikit-learn库
笔记·python·学习·机器学习·scikit-learn
Json____1 小时前
python的安装环境Miniconda(Conda 命令管理依赖配置)
开发语言·python·conda·miniconda
小袁在上班1 小时前
Python 单元测试中的 Mocking 与 Stubbing:提高测试效率的关键技术
python·单元测试·log4j
白狐欧莱雅1 小时前
使用python中的pygame简单实现飞机大战游戏
经验分享·python·游戏·pygame
阿_旭1 小时前
基于YOLO11/v10/v8/v5深度学习的维修工具检测识别系统设计与实现【python源码+Pyqt5界面+数据集+训练代码】
人工智能·python·深度学习·qt·ai
阿_旭1 小时前
基于YOLO11/v10/v8/v5深度学习的煤矿传送带异物检测系统设计与实现【python源码+Pyqt5界面+数据集+训练代码】
人工智能·python·深度学习·目标检测·yolo11
测试19982 小时前
外包干了2年,快要废了。。。
自动化测试·软件测试·python·面试·职场和发展·单元测试·压力测试