目录

  • 线性时间选择
  • 优化
    • 对划分进行随机数改良
    • 取中位数进行划分
  • 全部代码

线性时间选择

问题描述:给定线性序集中 n 个元素和一个整数 k(1 <= k <= n),要求找出着 n 个元素中第 k 小的元素。
RandomizedSelect算法:
该算法实际上是模仿快速排序算法设计的,基本思想是对输入的数组进行递归划分。与快速排序的算法不同的是,它只对划分的数组之一进行递归处理。

划分:以数组的第一个元素作为基准值,设置两个变量分别从前后往中间走,把大于基准值的放在基准值的右半部分,而小于基准值的放在基准值左侧。

int Partition(Type a[], int p, int r) {// 划分, 以 a[p] 为基准,一分为二 int i = p, j = r + 1;int x = a[p];while(1) {while(a[++i] < x && i < r) ;while(a[--j] > x && i >= p) ;if (j <= i) break;swap(a[i], a[j]);}a[p] = a[j];a[j] = x;return j;
}
template <class Type>
int RandomizedSelect(Type a[], int p, int r, int k) {// 线性时间选择找第 k 个小的数 if (p == r) return a[p];int ind = Partition(a, p, r);int j = ind - p + 1; // 左侧区间的长度 if (j >= k) {return RandomSelect(a, p, ind);} else {return RandomSelect(a, ind + 1, r, k - j);}
}

在算法RandomizedSelect执行Partition后,数组a[p:r]被划分成两个子数组:a[p:ind] 、 a[ind + 1, r], 使得每个a[p:ind]数组里的元素都不大于a[ind + 1 : r] 里的元素。接着算法执行计算子数组里面的元素个数,判断第 k 个小的元素落在哪一个区间里面,分治的思想,对其中一个区间进行递归调用算法。
容易看出,在最坏的情况下线性时间算法需要 O ( n 2 ) O(n^2) O(n2)计算时间。例如:再找最小元素时,总在最大元素处进行划分。尽管如此,该算法的平均性能比较好。

优化

对划分进行随机数改良

对划分算法Partition进行优化,使用一个随机数产生器Random,随机的产生p-r之间的一个随机整数,即划分基准是随机的,这个时候线性时间算法的平均时间可以在 O ( n ) O(n) O(n)内找到。

template <class Type>
Type RandomizedPartition(Type a[], int p, int r) {int i = p, j = r + 1;//Type x = a[p];int s = (rand() % (r - p + 1) + p);  // 随机下标Type x = a[s];// 将小于 x 的元素放在左侧, 大于 x 的放在右侧 while(1) {while(a[++i] < x && i < r) ; // 找到一个不小于 x 的元素while(a[--j] > x && j >= p);if (i >= j) {break; // 全部完成,终止 } swap(a[i], a[j]);}a[s] = a[j];a[j] = x;return j;
}

取中位数进行划分

在线性时间内找到一个划分基准,使得这个基准的划分的两个数组的长度都至少时原来的 α \alpha α倍(0 < α \alpha α < 1),那么最坏情况也是O(n)的情况。例如: 如果 α \alpha α = 9 / 10, 算法递归调用所产生的子数组的长度最小缩短 1 / 10。 所以,在最坏的情况下,算法所需要的计算时间T(n)满足递归式 T ( n ) < = T ( 9 n / 10 ) + O ( n ) T(n) <= T(9n/10) + O(n) T(n)<=T(9n/10)+O(n)。由此可得T(n) = O(n)。
划分基准的步骤如下:
1:将 n 个输入的元素每 5 个划分为一组,除了最后一个可能不是 5 个元素的组外,用任意一种排序算法进行排序,找出每组的中位数。
2:递归调用Select找出所有中位数的中位数,以这个数作为基准。

// 利用中位数来进行优化,时间复杂度最大也是O(n)
void bubbleSort(int a[],int p,int r);
int Partition(int a[],int p,int r,int val){ // 传入val数值, 以数值val为基准值 int pos;for(int q=p; q<=r; q++){if(a[q]==val){pos=q;break;}}swap(a[p],a[pos]);int i=p,j=r+1,x=a[p];while(1){while(a[++i]<x&&i<r);while(a[--j]>x);if(i>=j)break;swap(a[i],a[j]);}a[p]=a[j];a[j]=x;return j;
}
template <class Type>
Type Select(Type a[], int p, int r, int k) {if (r - p < 75) {bubbleSort(a, p, r); // 用一个简单的算法排序,小于75时Select执行时间不超过一个常数 ind = k - p + 1;return a[k - p + 1];}for (int i = 0; i <= (r - p - 4) / 5; i++) {// 每 5 个元素分为一组int s=p+5*i,t=s+4;sort(a + s, a + s + t);swap(a[p+i],a[s+2]);//交换每组中的中位数到前面}int x = Select(a, p, (r - p - 4) / 4, r - p + 1); // 找中位数的中位数, 及全体的中位数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, k - j, k);}
}

下面给出上述算法的验证截图:

全部代码

全部代码在下面:

#include <cstdio>
#include <iostream>
#include <stdlib.h>
#include <algorithm>
#include <ctime>
#include "windows.h"
#define MAX_N 1000000
#define LEN 30
using namespace std;
/* 线性时间选择, 找第 k 小的元素 */ int arr[MAX_N + 5];
int n, mod, ind;
// 利用随机数来取基准值, 平均时间复杂度为O(n)
template <class Type>
Type RandomizedPartition(Type a[], int p, int r) {int i = p, j = r + 1;//Type x = a[p];int s = (rand() % (r - p + 1) + p); Type x = a[s];// 将小于 x 的元素放在左侧, 大于 x 的放在右侧 while(1) {while(a[++i] < x && i < r) ; // 找到一个不小于 x 的元素while(a[--j] > x && j >= p);if (i >= j) {break; // 全部完成,终止 } swap(a[i], a[j]);}a[s] = a[j];a[j] = x;return j;
}template <class Type>
Type RandomizedSelect(Type a[], int p, int r, int k) {if (p == r) {ind = p;return a[p];}int i = RandomizedPartition(a, p, r); // 将数组 a 一分为 2 int j = i - p + 1;if (k <= j) return RandomizedSelect(a, p, i, k);else return RandomizedSelect(a, i + 1, r, k - j);
}
void set_console_color(unsigned short color_index)
{SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), color_index);
}// 利用中位数来进行优化,时间复杂度最大也是O(n)
void bubbleSort(int a[],int p,int r)
{for(int i=p; i<r; i++){for(int j=i+1; j<=r; j++){if(a[j]<a[i])swap(a[i],a[j]);}}
}int Partition(int a[],int p,int r,int val){ // 传入val数值, 以数值val为基准值 int pos;for(int q=p; q<=r; q++){if(a[q]==val){pos=q;break;}}swap(a[p],a[pos]);int i=p,j=r+1,x=a[p];while(1){while(a[++i]<x&&i<r);while(a[--j]>x);if(i>=j)break;swap(a[i],a[j]);}a[p]=a[j];a[j]=x;return j;
}
template <class Type>
Type Select(Type a[], int p, int r, int k) {if (r - p < 75) {bubbleSort(a, p, r); // 用一个简单的算法排序,小于75时Select执行时间不超过一个常数 ind = k - p + 1;return a[k - p + 1];}for (int i = 0; i <= (r - p - 4) / 5; i++) {// 每 5 个元素分为一组int s=p+5*i,t=s+4;sort(a + s, a + s + t);swap(a[p+i],a[s+2]);//交换每组中的中位数到前面}int x = Select(a, p, (r - p - 4) / 4, r - p + 1); // 找中位数的中位数, 及全体的中位数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, k - j, k);}
}int main() {srand(time(NULL)); /*根据当前时间设置“随机数种子”*/set_console_color(9);cout << "请输入数据规模 n 和最大数据 mod " << endl;set_console_color(7);cin >> n >> mod;set_console_color(9);cout << "请输入 k " << endl;set_console_color(7);int k;cin >> k;for (int i = 0; i < n; i++) {arr[i] = rand() % mod;}//int ans = RandomizedSelect(arr, 0, n - 1, k);int ans = Select(arr, 0, n - 1, k);set_console_color(2);//cout << "ans = " << ans << endl;cout << "数组中第 " << k << " 小的数是 " << arr[ind] << endl;set_console_color(7); cout << "*******排序后的结果如下**********" << endl;sort(arr, arr + n);for (int i = 0; i < n; i++) {if (i - ind)cout << arr[i] << " ";else {set_console_color(2);cout << arr[i] << " ";set_console_color(7);   }if (i && i % 30 == 0) cout << endl;}cout << endl;return 0;
}

参考链接-算法:线性时间选择

线性时间选择求第k小数(分治)相关推荐

  1. hdu3949(线性基,求第k小的异或和

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3949 XOR Time Limit: 2000/1000 MS (Java/Others)    Me ...

  2. 【HDU - 4217 】Data Structure? (线段树求第k小数)

    题干: Data structure is one of the basic skills for Computer Science students, which is a particular w ...

  3. 【HDU - 4006】The kth great number (优先队列,求第k大的数)

    题干: Xiao Ming and Xiao Bao are playing a simple Numbers game. In a round Xiao Ming can choose to wri ...

  4. C++:求第k小的数

    问题:给定n(1<=n<=1000000)个元素,求第k小数(1<=k<=n). 输入:一组样例.第一行输入两个整数n和k.第二行输入n个不同的int范围内的数. 输出:输出一 ...

  5. [leetcode] 4 寻找两个有序数组的中位数(二分+递归查找第K小数)(重要)

    问题描述 给定两个大小为 m 和 n 的有序数组 nums1 和 nums2. 请你找出这两个有序数组的中位数,并且要求算法的时间复杂度为 O(log(m + n)). 你可以假设 nums1 和 n ...

  6. 线性时间复杂度求数组中第K大数

    求数组中第K大的数可以基于快排序思想,步骤如下: 1.随机选择一个支点 2.将比支点大的数,放到数组左边:将比支点小的数放到数组右边:将支点放到中间(属于左部分) 3.设左部分的长度为L, 当K &l ...

  7. 线性时间选择算法的分治思想:邮局选址问题和士兵战队问题

    一. 实验题目 1. 邮局选址问题 在一个按照东西和南北方向划分成规整街区的城市里,n 个居民点散乱地分 布在不同的街区中.用 x 坐标表示东西向,用 y 坐标表示南北向.各居民点的 位置可以由坐标( ...

  8. 【分治】线性时间选择(C++)

    一.线性时间选择 1. 问题描述 给定线性序集中n个元素和一个整数k,1≤k≤n,要求找出这n个元素中第k小的元素. 2. 线性时间选择-随机划分 算法步骤: 生成1个随机数 i ,将数组a[p:r] ...

  9. szu 寒训第二天 树状数组 二维树状数组详解,以及树状数组扩展应用【求逆序对,以及动态第k小数】

    树状数组(Binary Index Tree) 树状数组可以解决可以转化为前缀和问题的问题 这是一类用以解决动态前缀和的问题 (有点像线段树简版) 1.对于 a1 + a2 + a3 + - + an ...

最新文章

  1. 【Boost】boost库asio详解2——io_service::run函数无任务时退出的问题
  2. WCF 第十三章 可编程站点 所有都与URI相关
  3. 复数混频发射机原理与仿真
  4. java内存问题怎么排查,java占内存高排查 java应用占用内存过高排查的解决方案...
  5. linux清理swap内容,Linux如何清理swap.buffer及cache等缓存
  6. web前端java script BOM学习笔记2017.8.1
  7. REVERSE-PRACTICE-CTFSHOW-4
  8. 32 CO配置-控制-产品成本控制-成本对象控制-期末结算-定义差异码
  9. 浙江大学PTA 数据结构 习题2.2 数组循环左移 (20 分)
  10. java特殊字符转义html_java转义html特殊字符
  11. 恶意软件针对中国用户 试图攫取用户账户和密码
  12. 微信端php 开发技术要求,PHP微信开发技术 - WebFalse文档托管平台
  13. Winform截图小程序
  14. 元件怎么反转_电气图纸怎么看?三分钟教你学会看懂,受用一生
  15. html中心点缩放,html canvas 让物体随物体中心缩放
  16. 元数据管理在数据仓库的实践应用
  17. 2020最新xcode打包IPA(完整详细图文详解)
  18. 华为选用ServiceHot ITSM提升运维服务能力
  19. python抓取网站访客手机号_网站获取访客QQ系统
  20. 【AVS系列】AVS2参考软件RD17.0

热门文章

  1. 证书全部无效的解决方法
  2. jlink 连接后 sw device 不显示jlink
  3. pandas怎么修改列名
  4. IPhone 6的自适应布局
  5. 六、Scala从入门到精通一一面对对象编程(初级)
  6. 7-5 计算银行存款的本息
  7. python做考勤统计
  8. 休闲娱乐之一些小小建议
  9. java利用redis实现排行榜_Redis实现排行榜
  10. 美化vim 安装google浏览器 解决不能打开的问题