【分治法】线性时间选择

线性时间选择问题:给定线性序集中n个元素和一个整数k,1≤k≤n,要求找出这n个元素中第k小的元素,(这里给定的线性集是无序的)。

1、随机划分线性选择

线性时间选择随机划分法可以模仿随机化快速排序算法设计。基本思想是对输入数组进行递归划分,与快速排序不同的是,它只对划分出的子数组之一进行递归处理

程序清单如下:

[cpp] view plaincopy
  1. //2d9-1 随机划分线性时间选择
  2. #include "stdafx.h"
  3. #include <iostream>
  4. #include <ctime>
  5. using namespace std;
  6. int a[] = {5,7,3,4,8,6,9,1,2};
  7. template <class Type>
  8. void Swap(Type &x,Type &y);
  9. inline int Random(int x, int y);
  10. template <class Type>
  11. int Partition(Type a[],int p,int r);
  12. template<class Type>
  13. int RandomizedPartition(Type a[],int p,int r);
  14. template <class Type>
  15. Type RandomizedSelect(Type a[],int p,int r,int k);
  16. int main()
  17. {
  18. for(int i=0; i<9; i++)
  19. {
  20. cout<<a[i]<<" ";
  21. }
  22. cout<<endl;
  23. cout<<RandomizedSelect(a,0,8,3)<<endl;
  24. }
  25. template <class Type>
  26. void Swap(Type &x,Type &y)
  27. {
  28. Type temp = x;
  29. x = y;
  30. y = temp;
  31. }
  32. inline int Random(int x, int y)
  33. {
  34. srand((unsigned)time(0));
  35. int ran_num = rand() % (y - x) + x;
  36. return ran_num;
  37. }
  38. template <class Type>
  39. int Partition(Type a[],int p,int r)
  40. {
  41. int i = p,j = r + 1;
  42. Type x = a[p];
  43. while(true)
  44. {
  45. while(a[++i]<x && i<r);
  46. while(a[--j]>x);
  47. if(i>=j)
  48. {
  49. break;
  50. }
  51. Swap(a[i],a[j]);
  52. }
  53. a[p] = a[j];
  54. a[j] = x;
  55. return j;
  56. }
  57. template<class Type>
  58. int RandomizedPartition(Type a[],int p,int r)
  59. {
  60. int i = Random(p,r);
  61. Swap(a[i],a[p]);
  62. return Partition(a,p,r);
  63. }
  64. template <class Type>
  65. Type RandomizedSelect(Type a[],int p,int r,int k)
  66. {
  67. if(p == r)
  68. {
  69. return a[p];
  70. }
  71. int i = RandomizedPartition(a,p,r);
  72. int j = i - p + 1;
  73. if(k <= j)
  74. {
  75. return RandomizedSelect(a,p,i,k);
  76. }
  77. else
  78. {
  79. //由于已知道子数组a[p:i]中的元素均小于要找的第k小元素
  80. //因此,要找的a[p:r]中第k小元素是a[i+1:r]中第k-j小元素。
  81. return RandomizedSelect(a,i+1,r,k-j);
  82. }
  83. }

程序解释:利用随机函数产生划分基准,将数组a[p:r]划分成两个子数组a[p:i]和a[i+1:r],使a[p:i]中的每个元素都不大于a[i+1:r]中的每个元素。接着"j=i-p+1"计算a[p:i]中元素个数j.如果k<=j,则a[p:r]中第k小元素在子数组a[p:i]中,如果k>j,则第k小元素在子数组a[i+1:r]中。注意:由于已知道子数组a[p:i]中的元素均小于要找的第k小元素,因此,要找的a[p:r]中第k小元素是a[i+1:r]中第k-j小元素。

在最坏的情况下,例如:总是找到最小元素时,总是在最大元素处划分,这是时间复杂度为O(n^2)。但平均时间复杂度与n呈线性关系,为O(n)(数学证明过程略过,可参考王云鹏论文《线性时间选择算法时间复杂度深入研究》)。

2、利用中位数线性时间选择

中位数:是指将数据按大小顺序排列起来,形成一个数列,居于数列中间位置的那个数据。

算法思路:如果能在线性时间内找到一个划分基准使得按这个基准所划分出的2个子数组的长度都至少为原数组长度的ε倍(0<ε<1),那么就可以在最坏情况下用O(n)时间完成选择任务。例如,当ε=9/10,算法递归调用所产生的子数组的长度至少缩短1/10。所以,在最坏情况下,算法所需的计算时间T(n)满足递推式T(n)<=T(9n/10)+O(n)。由此可得T(n)=O(n)。

实现步骤

(1)将所有的数n个以每5个划分为一组共组,将不足5个的那组忽略,然后用任意一种排序算法,因为只对5个数进行排序,所以任取一种排序法就可以了。将每组中的元素排好序再分别取每组的中位数,得到个中位数。

(2)取这个中位数的中位数,如果是偶数,就找它的2个中位数中较大的一个作为划分基准。

(3)将全部的数划分为两个部分,小于基准的在左边,大于等于基准的放右边。在这种情况下找出的基准x至少比个元素大。因为在每一组中有2个元素小于本组的中位数,有个小于基准,中位数处于,即个中位数中又有个小于基准x。因此至少有个元素小于基准x。同理基准x也至少比个元素小。而当n≥75时≥n/4所以按此基准划分所得的2个子数组的长度都至少缩短1/4。

程序清单如下:

[cpp] view plaincopy
  1. //2d9-2 中位数线性时间选择
  2. #include "stdafx.h"
  3. #include <ctime>
  4. #include <iostream>
  5. using namespace std;
  6. template <class Type>
  7. void Swap(Type &x,Type &y);
  8. inline int Random(int x, int y);
  9. template <class Type>
  10. void BubbleSort(Type a[],int p,int r);
  11. template <class Type>
  12. int Partition(Type a[],int p,int r,Type x);
  13. template <class Type>
  14. Type Select(Type a[],int p,int r,int k);
  15. int main()
  16. {
  17. //初始化数组
  18. int a[100];
  19. //必须放在循环体外面
  20. srand((unsigned)time(0));
  21. for(int i=0; i<100; i++)
  22. {
  23. a[i] = Random(0,500);
  24. cout<<"a["<<i<<"]:"<<a[i]<<" ";
  25. }
  26. cout<<endl;
  27. cout<<"第83小元素是"<<Select(a,0,99,83)<<endl;
  28. //重新排序,对比结果
  29. BubbleSort(a,0,99);
  30. for(int i=0; i<100; i++)
  31. {
  32. cout<<"a["<<i<<"]:"<<a[i]<<" ";
  33. }
  34. cout<<endl;
  35. }
  36. template <class Type>
  37. void Swap(Type &x,Type &y)
  38. {
  39. Type temp = x;
  40. x = y;
  41. y = temp;
  42. }
  43. inline int Random(int x, int y)
  44. {
  45. int ran_num = rand() % (y - x) + x;
  46. return ran_num;
  47. }
  48. //冒泡排序
  49. template <class Type>
  50. void BubbleSort(Type a[],int p,int r)
  51. {
  52. //记录一次遍历中是否有元素的交换
  53. bool exchange;
  54. for(int i=p; i<=r-1;i++)
  55. {
  56. exchange = false ;
  57. for(int j=i+1; j<=r; j++)
  58. {
  59. if(a[j]<a[j-1])
  60. {
  61. Swap(a[j],a[j-1]);
  62. exchange = true;
  63. }
  64. }
  65. //如果这次遍历没有元素的交换,那么排序结束
  66. if(false == exchange)
  67. {
  68. break ;
  69. }
  70. }
  71. }
  72. template <class Type>
  73. int Partition(Type a[],int p,int r,Type x)
  74. {
  75. int i = p-1,j = r + 1;
  76. while(true)
  77. {
  78. while(a[++i]<x && i<r);
  79. while(a[--j]>x);
  80. if(i>=j)
  81. {
  82. break;
  83. }
  84. Swap(a[i],a[j]);
  85. }
  86. return j;
  87. }
  88. template <class Type>
  89. Type Select(Type a[],int p,int r,int k)
  90. {
  91. if(r-p<75)
  92. {
  93. BubbleSort(a,p,r);
  94. return a[p+k-1];
  95. }
  96. //(r-p-4)/5相当于n-5
  97. for(int i=0; i<=(r-p-4)/5; i++)
  98. {
  99. //将元素每5个分成一组,分别排序,并将该组中位数与a[p+i]交换位置
  100. //使所有中位数都排列在数组最左侧,以便进一步查找中位数的中位数
  101. BubbleSort(a,p+5*i,p+5*i+4);
  102. Swap(a[p+5*i+2],a[p+i]);
  103. }
  104. //找中位数的中位数
  105. Type x = Select(a,p,p+(r-p-4)/5,(r-p-4)/10);
  106. int i = Partition(a,p,r,x);
  107. int j = i-p+1;
  108. if(k<=j)
  109. {
  110. return Select(a,p,i,k);
  111. }
  112. else
  113. {
  114. return Select(a,i+1,r,k-j);
  115. }
  116. }

运行结果如下:

分治法--线性时间选择相关推荐

  1. 算法笔记——【分治法】线性时间选择

    线性时间选择问题:给定线性序集中n个元素和一个整数k,1≤k≤n,要求找出这n个元素中第k小的元素,(这里给定的线性集是无序的). 随机划分线性选择 线性时间选择随机划分法可以模仿随机化快速排序算法设 ...

  2. 分治法:线性时间选择

    大家好,我是连人,本期分享线性时间选择问题. 线性时间选择是基于快速排序的一种延申,本质上和快排具有类似的地方.它的目的是找出这个数组中第k小的值. 既然只需找出1个值,我们就不必将整个数组排序.通过 ...

  3. 线性时间选择【递归分治法】

    顾名思义:这篇文章讲解的就是如果用线性时间算法来作出元素选择问题. 问题描述:给定线性序集中n个元素和一个整数k,1<=k<=n.要求找出这n个元素中第k小的元素,即如果将这个n个元素依其 ...

  4. 程序员的算法课(13)-分治法

    一.什么是分治 [百度百科]分治法((Divide and Conquer))可以通俗的解释为:把一片领土分解,分解为若干块小部分,然后一块块地占领征服,被分解的可以是不同的政治派别或是其他什么,然后 ...

  5. 分治法的关键特征_经典算法思想2——分治(Divide-and-Conquer)

    分治法,字面意思是"分而治之",就是把一个复杂的1问题分成两个或多个相同或相似的子问题,再把子问题分成更小的子问题直到最后子问题可以简单地直接求解,原问题的解即子问题的解的合并,这 ...

  6. Java常用算法二:分治法

    文章目录 一.分治算法的基本步骤 二.分治算法解决汉诺塔问题 2.1 汉诺塔的规则: 2.2 使用分治算法 笔记参考:尚硅谷 分治法就是把很复杂的问题分而治之,把一个很大的问题分成几个很小的问题,再把 ...

  7. C#内功修炼(算法)——分治法(一)

    分治法(递归的解决问题) 分治策略是:对于一个规模为n的问题,若该问题可以容易地解决(比如说规模n较小)则直接解决,否则将其分解为k个规模较小的子问题,这些子问题互相独立且与原问题形式相同,递归地解这 ...

  8. 线性时间选择-分治算法

    问题描述  给定线性序集中n个元素和一个整数k,1≤k≤n,要求找出这n个元素中第k小的元素.在线性时间内O(n)? k=1; 最小元素 O(n) k=n; 最大元素 O(n) k=(n+1)/2: ...

  9. 算法设计与分析——分治法

    主要思想 (其实有这个思想也想不出来): 1.划分:整个问题划分成多个子问题 2.求解:求解各子问题的解 3.合并:合并子问题的解 (手说:"我会了",脑子:"不会&qu ...

  10. python分治算法_python算法实现-分治法

    分治法概念将一个复杂的问题分成两个或更多的相同或相似的子问题,再把子问题分成更小的子问题----"分" 将最后子问题可以简单的直接求解----"治" 将所有子问 ...

最新文章

  1. Java项目:网上水果蔬菜项目系统设计和实现(java+springboot+mysql+ssm)
  2. 开发人员改变世界的初心
  3. azure devops中文显示乱码_【Azure DevOps系列】Azure DevOps生成代码覆盖率
  4. 在阿里云的Ubuntu ECS instance 使用Apt-get安装git
  5. 如何使用Hibernate从Play生成DDL脚本! 框架项目
  6. bzoj1037 [ZJOI2008]生日聚会Party 插数dp
  7. 菜鸟学习笔记:Java基础篇6(数组、字符串)
  8. fastjson php,Fastjson 对象或数组转JSON
  9. 软件对操作系统有要求?操作系统不符合要求你软件就不玩了?
  10. mac拼音输入法下面不显示汉字
  11. 微信小程序登录方法,授权登陆及获取微信用户手机号
  12. Android之drawable下快速生成icon图片vector
  13. 微信公众号迁移,认证; 名称触发商标怎么办
  14. 测试工作中比较好用的几款对比工具
  15. 未来的计算机也无法突破冯诺依曼结构,冯诺依曼计算机的基本原理
  16. 中国大学python程序设计答案_Python程序设计1-中国大学mooc-试题题目及答案
  17. C#winform中OpenFileDialog的用法
  18. 已在调试程序中暂停 怎么办 解决办法
  19. 模拟手机通讯录联系人功能
  20. 微软将删除个人版Win10中的IE11

热门文章

  1. WebStorm破解激活
  2. python——numpy——roll()函数
  3. Unity播放序列帧,功能丰富
  4. GBASE数据库安装手册中的一点命令
  5. python抠图教程视频_3行Python代码实现8秒抠图的AI神器,根本无需PS(附视频教程)...
  6. python 微博自动点赞软件_微博超话自动软件-微博超话自动工具(签到+发帖+自动写文案)下载-西西软件下载...
  7. mysql关联分组查询,Mysql 分组查询/子查询/关联查询【总结】
  8. 本地邮件服务器 易邮 使用
  9. CorelDRAWX4的VBA插件开发(十四)快速定位形状
  10. 计算机硬盘驱动器可以删吗,有关删除存储驱动器上的数据的注意事项