c语言 线性时间选择 分治法,分治法:线性时间选择 python

大家好,我是连人,本期分享线性时间选择问题。

线性时间选择是基于快速排序的一种延申,本质上和快排具有类似的地方。它的目的是找出这个数组中第k小的值。

既然只需找出1个值,我们就不必将整个数组排序。通过分治法和分区(partition)可以只将k所在范围内的值进行查找即可。

当然可以使用二分法去确立k的范围,但是我的课本上没有所以我们今天不讨论。

下面介绍两种算法:随机选择和中位数选择。

随机选择

随机选择是在当前处理的数组中随机挑选一个数,以这个数对数组进行partition操作,判断k与这个数的位置p的关系,如果k<=p,则再将左边进行当前操作,反之则对右边操作,体现了分治的思想。

举个例子。

查找第三小的数字

1,5,7,2,4,8,6

选择了7作为基准

partition后:

1,5,6,2,4,7,8

7的位置是6,3<6,所以将

1,5,6,2,4,7

再进行这次操作,直到数组只剩下一个数,即为所求

可以得知,随机选择在最坏情况下(例如找最大值时总是在最小值处划分)需要将所有元素都遍历一遍,需要O(n2)的时间。尽管如此,该算法的平均性能还是很好。可以证明,随机选择算法可以在O(n)的平均时间内找出n个输入元素中第k小的元素。

他说是,辣就是,反正我也不会证。

下面贴代码:

import random

def partition(a, left, right):

i = left

j = right

key = a[left]

while i < j:

while a[j] >= key and i < j:

j -= 1

a[i] = a[j]

while a[i] <= key and i < j:

i += 1

a[j] = a[i]

a[i] = key

return i

def randomized_partition(a, left, right):

if left > right: # 为了保护randint函数的

i = random.randint(left, right)

a[i], a[left] = a[left], a[i]

return partition(a, left, right)

def randomized_select(a, left, right, k):

if left == right:

return a[left]

i = randomized_partition(a, left, right)

j = i - left + 1

if k <= j:

return randomized_select(a, left, i, k)

else:

return randomized_select(a, i + 1, right, k)

number = random.randint(60, 80)

array = []

for i in range(0, number):

array.append(random.randint(0, 2*number))

print("一共生成了" + str(number) + "个数")

print("他们是:")

print(array)

k = input("你要查找第几小的数字?")

print(randomized_select(array, 0, number - 1, int(k)))

中位数选择

这种算法可以在最坏情况下用O(n)时间就完成选择任务。

这个算法由如下步骤完成:

①将数组分为若干组,每5个一组,最后一组可能会不满5个。

②将各个组排序,取出中位数。

③找出这些中位数的中位数,以这个结果为标准进行划分。

我在第三步对中位数数组还在75位以上的再次进行了取中位数操作。

针对这个算法,我们再举一个例子。

1,4,2,5,10,7,3,11,6,14,18,8,9,13

就先举14个数,方便理解。

首先,将这14个数按5个一组进行分组。最后一组只有四个单独列出来。

{1,4,2,5,10},{7,3,11,6,14},{18,8,9,13}

将每个组进行排序操作,得

{1,2,4,5,10},{3,6,7,11,14},{8,9,13,18}

将每个组的中位数取出来(为了节省空间通常会使用交换把中位数交换到最前面),得

4,7,9

再将中位数进行排序(虽然这个例子自动排好了)

将中位数7取出,按7为基准对原数组进行partition。

判断k与7的位置关系,k在左边就对左边继续选择,反之对右边选择,体现了分治思想。

在集中中位数时,我没有使用“将中位数换到最前方”,而是新开的一个数组,这样做可以使代码更易懂,但缺点是增加了空间的消耗。

import random

def sort(a, l, r): # 冒泡排序

for i in range(l, r + 1):

for j in reversed(range(i, r + 1)):

if a[i] >= a[j]:

a[i], a[j] = a[j], a[i]

def partition(a, left, right, x): # 分区,这里不解释了

i = left

j = right

while i < j:

while a[j] > x and i < j:

j -= 1

while a[i] < x and i < j:

i += 1

a[i], a[j] = a[j], a[i]

return i

def find_mid(a, l, r):

if r - l < 75:

sort(a, l, r)

return a[(l + r) // 2]

else:

b = [] # 存储中位数的数组

i = l

while True:

if 5 * i + 4 <= r: # 判断是否为最后一组

sort(a, 5 * i, 5 * i + 4)

b.append(a[5 * i + 2])

else:

sort(a, 5 * i, r)

b.append(a[(5 * i + r) // 2])

break

i += 1

return find_mid(b, 0, len(b) - 1)

def select(a, l, r, k):

if r - l < 75:

sort(a, l, r)

return a[l + k - 1]

else:

mid_number = find_mid(a, l, r)

mid_position = partition(a, l, r, mid_number)

if k > mid_position:

return select(a, mid_position + 1, r, k)

else:

return select(a, l, mid_position, k)

number = random.randint(150, 200)

array = []

for i in range(0, number):

array.append(random.randint(0, number * 2))

print(array)

print("生成了" + str(number) + "个数")

k = input('输入想要第几小的数字')

print(select(array, 0, number - 1, int(k)))

转载注明出处。

a4b35b04d7823f736d43d112ddd2ee9d.png

de638df49f1d5a3342eecff86bcb011e.png

连人

发布了10 篇原创文章 · 获赞 1 · 访问量 727

私信

关注

标签:right,python,分治,中位数,partition,while,数组,线性,left

来源: https://blog.csdn.net/mirocky/article/details/104074850

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值