一道分治算法问题

2015-04-08 21:17:46 +08:00
 xingo

二流学校自学算法,在做练习题的时候这道题不会(6.30)
http://ww4.sinaimg.cn/large/6b8bbe7ejw1eqyep46iiaj21ts35sb2a.jpg
http://ww3.sinaimg.cn/large/6b8bbe7ejw1eqyet817qyj21ts35s4qq.jpg

你们敢相信我这算法老师也不懂???我感觉他连题目都没看懂。。。。。他以为是一个进去找一个出来。。。那SELECT算法时间复杂度已经是O(n)了,还需要啥O(n logr)...
谷歌了也没找到。。。。。。。

2944 次点击
所在节点    问与答
24 条回复
dingyaguang117
2015-04-08 22:15:55 +08:00
看描述S是有序集合。
题目应该是让你这样做,S中找出ki小
=>
S分成2段,前一段找到ki小,其中ki要小于S长度的一半,后一段也是一样。
应该是这样的分治吧
xingo
2015-04-08 22:26:00 +08:00
@dingyaguang117 S不是有序集合,S是无序集合,SELECT的功能就是在无序集合中找到k小元素,时间复杂度为O(n)
如果是这样的话,那直接二分搜索了啊。。。。
binux
2015-04-08 22:26:18 +08:00
什么叫『第 k 个最小元素』?『最』了怎么还有『第』?
binux
2015-04-08 23:12:49 +08:00
我假设就是第 k 小的元素好了

首先把 K 排序,取第 r/2 个元素,SELECT 从 S 中找到第 K(r/2) 小元素 S(r/2)。
将 S 按照 S(r/2) 分割为两半,一半全小于 S(r/2) 一半全大于 S(r/2)。
在小的那一半中查找 K(j<r/2),在大的那一半查找 K(j>r/2)。
over
sumhat
2015-04-08 23:18:20 +08:00
发图之前请先转一下角度 -_-
jiang42
2015-04-09 00:10:39 +08:00
发图醉了。。。。
Andiry
2015-04-09 01:07:55 +08:00
这图喷了,@binux 是正解。先找中位数,然后分治
xingo
2015-04-09 07:25:16 +08:00
@binux 感谢层主

小的那一部分没有问题,大的一部分的k就不是之前的k了呀,大的一部分的第k小元素,就不是S里的第K小元素了呀

顺着层主的思路想了一下,如果按照这样递归,就变成了快速排序了啊
xingo
2015-04-09 07:33:43 +08:00
@sumhat @jiang42 原图已经旋转了,抱歉带来不方便了
yyai3
2015-04-09 09:31:58 +08:00
@xingo 使用二分查找,先取出S中某个数,将S分割成两半(S1,S2),S1集合的总数为n1,

K中小于n1的数,继续在S1集合中找,直到K集合为空
K中大于n1的数,在S2集合中找,查找的位置为K-n1,直到K集合为空
K中等于n1+1的数,则返回。并从集合K中删除
binux
2015-04-09 09:50:48 +08:00
@xingo 你不会把大的那一半减去 K(r/2) 吗。。。
dingyaguang117
2015-04-09 10:25:41 +08:00
@xingo 如果不是有序,平凡的消耗O(r*n)这句话就有问题了,O(N)能选择出一个无序数组中的第x小元素?
Angdo
2015-04-09 10:29:11 +08:00
select 查找第k小元素本来就是 O n的 for循环 1到r使算法变成nr 把1-r那块变成 logr 不就是nlogr的了?
Angdo
2015-04-09 10:41:01 +08:00
@yyai3 这不是二分搜索 最坏情况它的时间复杂度是 On^2 每次都是最大值 导致每次都不被拆分成两部分
Angdo
2015-04-09 10:51:20 +08:00
@dingyaguang117 'SELECT算法就是用来查找第k小元素的算法 为O n 题目意思是查找第k1到kr小元素就直接for循环从k1计算到kr 为O r 一共就是O nr
Angdo
2015-04-09 11:07:30 +08:00
之前错了先把k排序O rlogr 再取中值 select 得到 第k r/2 小元素 这时 S被分成了两部分 前面小于 第k r/2 小元素 后面大于第k r/2 小元素 然后两部分k再取中值 重复上面
hpeng
2015-04-09 12:45:19 +08:00
如果你想不明白那个正解,给个关键字你搜 top k
xingo
2015-04-09 13:41:43 +08:00
@binux 当时没想到嘛!!!!看到下面的回复就会了!!!!_(:з」∠)_
@yyai3 综合你们两个的!!!我找到思路了!!!!


先写个伪代码,大家看看对不对




对K排序

void batch_select(int A[],int K[],int A_low,int A_high,int k_low,int k_high){
if (k_high-k_low)<1{
result[k_low]=select(A,A_low,A_high,K[k_low]);//result数组用来存放结果
return;
}
else if (k_high-k_low)==1 {
result[k_low]=select(A,A_low,A_high,K[k_low]);
result[k_high]=select(A,A_low,A_high,K[k_high]);
return;
}
else {
在S中找到select(A,A_low,A_high,K[(k_low+k_high)/2]),分成两组,返回此数字的元素位置为A_mid
for (int i=(k_low+k_high)/2;i<=k_high;i++){
K[i]=K[i]-A_mid;

}
result[(k_low+k_high)/2]=S[A_mid];
batch_select(A,K,A_low,A_mid-1,k_low,(k_low+k_high)/2);
batch_select(A,K,A_mid+1,A_high,(k_low+k_high)/2,k_high);
return;

}


}

@Angdo 应该就是上面这个思路吧,其实和yyai3的一个意思,他就是没说的很明白

@dingyaguang117 如果是有序的,那找第k小元素的时间复杂度是O(1)哦

@hpeng 谢谢谢谢!!!!!原来是这个!!!!
Angdo
2015-04-09 18:55:28 +08:00
@xingo 在select最后已经把s分成两部分在select里递归select书上那什么比k小数组a什么和select 比k大,直接修改select算法
Angdo
2015-04-09 19:05:03 +08:00
@xingo k先排序 把select改成输入数组k 分成比k小的数组 和比k大的数组 然后再select里取中值 按原来select一遍 select最后会把s分成两份 然后两部分递归select

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/182407

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX