线性时间选择问题java实现_线性时间选择算法

线性时间选择算法是解决元素选择问题的一种高效方法,尤其在寻找第k小的元素时。本文介绍了线性时间选择的三种方法,包括先排序再查找、随机选择算法和线性时间选择算法,其中线性时间选择算法的时间复杂度为O(n)。详细阐述了线性时间选择算法的步骤,并给出了Java实现代码。
摘要由CSDN通过智能技术生成

线性时间选择算法

顾名思义,“线性时间选择”就是“选择问题”的“线性时间”算法。

1. 选择问题

元素选择问题:给定一个能够线性排序的集合(该集合中有 n 个元素)和 一个整数 k(\(1 \le k \le n\)) ,找出这 n 个元素中第 k 小的元素。

时间下界:

当 \(k = 1 或 k = n\)时,时间复杂度为 \(O(n)\)

当 \(k \le n/log(n) 或 k \ge n - n/log(n)时\),时间复杂度为\(O(n + klog(n)) = O(n)\)(堆排序)

2. 解决方法

方法一:先排序,再找第 k 小的数。至少:\(O(n)\) 平均:\(O(nlog(n))\)时间

方法二:随机选择算法:使用快速排序方法, 最多对一段继续分解 最坏时间\(O(n^2)\), 平均时间\(O(n)\)

RamdomizedPartition(a, p, r) 快排中的分解算法

i=Ramdom(p,r) //在p:r中随机选择一个数i

交换 a[i] 与 a[p] //将a[i]换到左端点

执行Partition(a,p,r)

RamdomizedPartition(a, p, r) //排序a[p:r]

{

i = Ramdom(p, r);

swap(a[i], a[p]);

return Partition(a, p, r);

}

RandomSelect(a, p, r)

RSelect(a,p,r,k) //选择a[p:r]中第k小数

{

if (p == r)

return a[p];

mid=RamdomizePartition(a, p, r);

if( mid >= k)

return (RSelect(a, p, mid, k));

else

return (RSelect(a, mid + 1, r, k - mid);

} //初略时间分析: T(n) = T(9n/10) + O(n) = O(n)

方法三:线性时间选择算法 Select() :对快速排序的改进,最坏时间\(O(n)\)。

将 n 个元素,分成\(\lceil n/5 \rceil\)组,取出每组的中位数(第三小的元素)

取出\(\lceil n/5 \rceil\)个中位数的中位数(Select函数可以取中位数)

利用快排中的分解函数 Partition(),以所求中位数为基准,划分 a[p : r] 为两段。

取其中一段进行递归。

template

Type Select(Type a[], int p, int r, int k)

{

if( r - p < 75 )

{

直接对数组a[p:r]排序;

return a[p+k-1];

}

for( int i = 0; i <= (r - p - 4) / 5 ; i++ ) //分 n/5 组, 取各组中位数

{

swap(a[p + 5*i]至a[p+5*i+4]的第3小元素, a[p+i]);

}

Type x = Select(a, p, p+(r-p-4)/5, (r-p-4)/10); //取中位数的中位数, T(n/5)

int i = Partition(a, p, r, x), j = i - p + 1;

if ( k == j )

return a[i];

else if ( k < j )

return Select(a,p,i-1,k); //选择左片递归, 最多T(3n/4)

else

return Select(a,i+1,r,k-j); //选择右片递归, 最多T(3n/4)

}

时间复杂度分析:

9923f526b76379fda1c65ac35c3f0f51.png

总结:算法优化的历程

算法

快排

随机选择

线性时间选择

时间复杂度

\(O(nlog(n))\)

\(O(n) -- O(n^2)\)

\(O(n)\)

基准值

a[p]

random

中位数

附:line-time-select.c

#include

#define MAX 2000010

int num[MAX];

// 选择排序

void slsort(int p, int q)

{

for (int i = p + 1; i <= q; i++)

{

int temp = num[i], j = i - 1;

while (j >= p)

{

if (num[j] > temp)

{

num[j + 1] = num[j];

j--;

}

else

break;

}

num[j + 1] = temp;

}

}

// 分解函数

int Partition(int p, int q, int mid)

{

int i = p, j = q;

while (i <= q && j >= p)

{

while (num[i] < mid){i++;}

while (num[j] > mid){j--;}

if (i >= j)

break;

else

{

int temp = num[i];

num[i] = num[j];

num[j] = temp;

i++, j--;

}

}

return j;

}

// 选择函数

int Select(int p, int q, int k)

{

if (q - p < 75)

{

slsort(p, q);

return num[p + k - 1];

}

// 选出 n/5 组中每个组的中位数

for (int i = 0; i <= (q - p - 4) / 5; i++)

{

slsort(p + 5 * i, p + 5 * i + 4);

int temp = num[p + 5 * i + 2];

num[p + 5 * i + 2] = num[p + i];

num[p + i] = temp;

}

// 选出各种中位数的中位数 mid

int mid = Select(p, p + (q - p - 4) / 5, ((q - p - 4) / 5 + 1) / 2);

// 以 mid 为基准进行分解

int mid_id = Partition(p, q, mid);

int mid_rank = mid_id - p + 1;

// 递归条件判断

if (k == mid_rank)

{

return num[mid_id];

}

else if (k < mid_rank)

{

return Select(p, mid_id, k);

}

else

{

return Select(mid_id + 1, q, k - mid_rank);

}

}

int main(int argc, char const *argv[])

{

int i = 0, k;

while (scanf("%d", &num[i]) != EOF) {i++;}

scanf("%d", &k); // 选择第几小的元素

if( k > i)

printf("error!\n");

else

print("%d\n", Select(0, i, k));

return 0;

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值