线性时间选择问题-第k小(大)问题-递归与分治

xiaoxiao2021-02-28  29

问题:给定线性序集中n个元素和一个整数k,1≤k≤n,要求找出这n个元素中第k小的元素。在线性时间内O(n)?

解析:

定义查找第k小元素的算法为 Select(Type a[], int p, int r, int k) 。将n个输入元素划分成n/5个组,每组5个元素,只可能有一个组不是5个元素。用任意一种排序算法,将每组中的元素排好序,并取出每组的中位数,共n/5个。递归调用算法select来找出这n/5个元素的中位数。

如果n/5是偶数,就找它的2个中位数中较大的一个。以这个元素作为划分基准。

伪代码:

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++ ) //将元素每5个分成一组,分别排序 BubbleSort(a,p+5*i,p+5*i+4); //将该组中位数与a[p+i]交换位置,即: 将a[p+5*i] 至 a[p+5*i+4]的第3小元素与a[p+i]交换位置; Swap(a[p+5*i+2],a[p+i]); //找中位数的中位数,r-p-4即上面所说的n-5 Type x = Select(a, p, p+(r-p-4)/5, (r-p-4)/10); int i=Partition(a,p,r, x), j=i-p+1; if (k<=j) return Select(a,p,i,k); else return Select(a,i+1,r,k-j); }

注:来自《计算机算法设计与分析》(第四版)王晓东 编著

代码:

#include <bits/stdc++.h> using namespace std; int Partition(int *a, int p, int n, int beginval) { int temp, beginpos; for(int i = 0; p <= n - p + 1; ++i) if(a[p + i] == beginval) { beginpos = p + i; break; } temp = a[p]; a[p] = a[beginpos]; a[beginpos] = temp; temp = a[p]; while(p < n) { while(a[n] >= temp && p < n) --n; a[p] = a[n]; while(a[p] <= temp && p < n) ++p; a[n] = a[p]; } a[p] = temp; return p; } int Select(int *a, int p, int n, int k) { int temp; if(n - p < 75) { sort(a + p, a + n); return a[p + k - 1]; } else { int ArrNum = (n - p + 1) / 5, i; if(5 * ArrNum != n - p + 1) ++ArrNum; for(i = 0; i < ArrNum - 1; ++i) { sort(a + p + i * 5, a + p + i * 5 + 4); temp = a[p + i]; a[p + i] = a[p + i * 5 + 2]; a[p + i * 5 + 2] = temp; } sort(a + p + i * 5, a + n); int midval = Select(a, p, p + ArrNum, ArrNum / 2); int midpos = Partition(a, p, n, midval); int ranking = midpos - p + 1; if(k <= ranking) return Select(a, p, midpos, k); else return Select(a, midpos + 1, n, k - ranking); } } int main() { int a[100]; int n, k; scanf("%d %d", &n, &k); for(int i = 0; i < n; ++i) scanf("%d", &a[i]); int val = Select(a, 0, n, k); printf("%d\n", val); }
转载请注明原文地址: https://www.6miu.com/read-1700086.html

最新回复(0)