算法导论学习之线性时间求第k小元素+堆思想求前k大元素
2017-07-16 12:03
381 查看
对于曾经,假设要我求第k小元素。或者是求前k大元素,我可能会将元素先排序,然后就直接求出来了,可是如今有了更好的思路。
一.线性时间内求第k小元素
这个算法又是一个基于分治思想的算法。
其详细的分治思路例如以下:
1.分解:将A[p,r]分解成A[p,q-1]和A[q+1,r]两部分。使得A[p,q-1]都小于A[q],A[q+1,r]都不小于A[q];
2.求解:假设A[q]恰好是第k小元素直接返回,假设第k小元素落在前半区间就到A[p,q-1]递归查找。否则到A[q+1,r]中递归查找。
3.合并:这个问题不须要合并。
其相应的代码例如以下:
事实上这个过程非常相似于快排,可是为什么快排的时间复杂度是O(nlgn),而这个算法的时间复杂度仅仅有O(n)?基本的原因在于这个算法每次仅仅要处理分解以后一半的区间,而不像快排那样两边都要处理。
当然这仅仅是一个简单的分析,更详细数学分析在这里就不说了。事实上我们也能够利用堆的性质来求出第k小元素,仅仅要我们建立一个最小堆后然后再调整k-1次即可了,这样时间复杂度是O(n)+O((k-1)lgn)。
以下给出一份完整的代码:
二.利用堆求前k大元素
这个算法的思想比較简单: 假设我们要求n个元素中前k大的元素。我们就先将这n个元素中的前k个元素建立一个最小堆,然后从k+1。
。。
n依次推断。假设某个元素大于堆中最小的元素,我们就将其替代堆中的最小元素,并且调整一下堆。
这样将全部元素都检查完了之后,堆中的k个元素也就是这n个元素中的前k大元素了。时间复杂度O(k)+O((n-k)lgk)。
代码例如以下
一.线性时间内求第k小元素
这个算法又是一个基于分治思想的算法。
其详细的分治思路例如以下:
1.分解:将A[p,r]分解成A[p,q-1]和A[q+1,r]两部分。使得A[p,q-1]都小于A[q],A[q+1,r]都不小于A[q];
2.求解:假设A[q]恰好是第k小元素直接返回,假设第k小元素落在前半区间就到A[p,q-1]递归查找。否则到A[q+1,r]中递归查找。
3.合并:这个问题不须要合并。
其相应的代码例如以下:
int RandomziedSelect(int *a,int p,int r,int k) { if(p==r)///假设当前区间仅仅剩一个元素,那么这个元素一定就是我们要求的 return a[p]; int q=RandomParatition(a,p,r); ///随机划分函数 int x=q-p+1;///求出a[p,q]之间的长度 if(x==k) ///a[q]恰好是第k小元素 return a[q]; if(x>k) ///x小于k说明第k小元素在a[p,q-1]之间 return RandomziedSelect(a,p,q-1,k); else ///x大于k说明第k小元素在a[q+1,r]之间,并且是这个区间的第k-x小元素 return RandomziedSelect(a,q+1,r,k-x); }
事实上这个过程非常相似于快排,可是为什么快排的时间复杂度是O(nlgn),而这个算法的时间复杂度仅仅有O(n)?基本的原因在于这个算法每次仅仅要处理分解以后一半的区间,而不像快排那样两边都要处理。
当然这仅仅是一个简单的分析,更详细数学分析在这里就不说了。事实上我们也能够利用堆的性质来求出第k小元素,仅仅要我们建立一个最小堆后然后再调整k-1次即可了,这样时间复杂度是O(n)+O((k-1)lgn)。
以下给出一份完整的代码:
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<ctime>
#include<fstream>
using namespace std;
int Paratition(int *a,int p,int r)
{
int key=a[r];
int i=p-1;
for(int j=p;j<r;j++)
if(a[j]<key)
{
i++;
swap(a[i],a[j]);
}
swap(a[i+1],a[r]);
return i+1;
}
int RandomParatition(int *a,int p,int r)
{
int x=rand()%(r-p+1)+p;///产生[p,r]之间的随机数
swap(a[x],a[r]); ///交换a[x]和a[r]的值,事实上就是将a[x]作为划分的关键值
return Paratition(a,p,r);
}
int RandomziedSelect(int *a,int p,int r,int k) { if(p==r)///假设当前区间仅仅剩一个元素,那么这个元素一定就是我们要求的 return a[p]; int q=RandomParatition(a,p,r); ///随机划分函数 int x=q-p+1;///求出a[p,q]之间的长度 if(x==k) ///a[q]恰好是第k小元素 return a[q]; if(x>k) ///x小于k说明第k小元素在a[p,q-1]之间 return RandomziedSelect(a,p,q-1,k); else ///x大于k说明第k小元素在a[q+1,r]之间,并且是这个区间的第k-x小元素 return RandomziedSelect(a,q+1,r,k-x); }
int main()
{
int b[100];
ifstream fin("lkl.txt");
int n,k;
//cout<<"请输入n,k: ";
fin>>n>>k;
//cout<<"请输入"<<n<<"个元素: "<<endl;
for(int i=1;i<=n;i++)
fin>>b[i];
int ans=RandomziedSelect(b,1,n,k);
sort(b+1,b+n+1);
for(int i=1;i<=n;i++)
cout<<b[i]<<" ";
cout<<endl;
cout<<"第"<<k<<"小元素为: "<<ans<<endl;
return 0;
}
二.利用堆求前k大元素
这个算法的思想比較简单: 假设我们要求n个元素中前k大的元素。我们就先将这n个元素中的前k个元素建立一个最小堆,然后从k+1。
。。
n依次推断。假设某个元素大于堆中最小的元素,我们就将其替代堆中的最小元素,并且调整一下堆。
这样将全部元素都检查完了之后,堆中的k个元素也就是这n个元素中的前k大元素了。时间复杂度O(k)+O((n-k)lgk)。
代码例如以下
#include<iostream> #include<cstdio> #include<cstring> #include<cmath> #include<fstream> using namespace std; #define maxn 100 ///最小堆调整函数 void MinHeadfly(int *a,int i,int HeadSize) { int l=i*2,r=2*i+1; int largest; if(a[i]>a[l]&&l<=HeadSize) largest=l; else largest=i; if(a[largest]>a[r]&&r<=HeadSize) largest=r; if(largest!=i) { swap(a[i],a[largest]); MinHeadfly(a,largest,HeadSize); } } ///最小堆建立函数 void MinHeadBuild(int *a,int n) { for(int i=n/2;i>=1;i--) MinHeadfly(a,i,n); } ///最小堆排序函数,从大到小排序 void MinHeadSort(int *a,int HeadSize) { int k=HeadSize; for(int i=HeadSize;i>=2;i--) { swap(a[i],a[1]); k--; MinHeadfly(a,1,k); } } ///求b数组的前k大元素 void prek(int *a,int *b,int n,int k) { MinHeadBuild(a,k); for(int i=k+1;i<=n;i++) if(b[i]>a[1]) { a[1]=b[i]; MinHeadfly(a,1,k); } MinHeadSort(a,k); cout<<"前"<<k<<"大元素为:"<<endl; for(int i=1;i<=k;i++) cout<<a[i]<<" "; cout<<endl; } int a[maxn],b[maxn]; int main() { ifstream fin("lkl.txt"); int n,k; //cout<<"请输入n,k: "; fin>>n>>k; //cout<<"请输入"<<n<<"个元素: "<<endl; for(int i=1;i<=n;i++) { fin>>b[i]; if(i<=k) a[i]=b[i]; } prek(a,b,n,k); return 0; }
相关文章推荐
- 算法导论学习之线性时间求第k小元素+堆思想求前k大元素
- 分治算法;随机化划分函数;快速排序;线性时间选择第K小元素;快速排序平均时间复杂度nlgn;
- 算法导论学习之线性时间排序+排序算法稳定性总结
- 线性时间查找第k大元素
- HDU6040-Hints_of_sd0061-STL/快排思想/线性时间找第k大
- 线性时间选择:从n个元素中找出第k小的元素
- 算法导论学习笔记-第8章 线性时间排序
- 算法导论学习之线性时间排序
- 线性时间求第k小(分治思想)
- 算法导论--在平均线性时间下选择元素
- java线性时间寻找第k小元素
- 算法导论标准版快速排序和线性时间求解数组第K小数
- 在数组A中寻找第k小的元素-最坏情况为线性时间的算法
- 线性时间选择第K小元素(随机化选择第K小元素)C++
- 在线性时间复杂度内求解第k小元素问题
- 选择问题:线性时间内找到序列的第k小的元素
- 《算法导论》第8章 线性时间排序 个人笔记
- 递归与分治策略-2.9.1线性时间选择(随机划分基准)(第k小问题)
- 线性时间选择第k小(递归)
- 选择第k小的元素-O(n)的时间复杂度