对于曾经,假设要我求第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.合并:这个问题不须要合并。

其相应的代码例如以下:

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大元素的更多相关文章

  1. 算法导论 第八章 线性时间排序(python)

    比较排序:各元素的次序依赖于它们之间的比较{插入排序O(n**2) 归并排序O(nlgn) 堆排序O(nlgn)快速排序O(n**2)平均O(nlgn)} 本章主要介绍几个线性时间排序:(运算排序非比 ...

  2. 算法导论学习---红黑树具体解释之插入(C语言实现)

    前面我们学习二叉搜索树的时候发如今一些情况下其高度不是非常均匀,甚至有时候会退化成一条长链,所以我们引用一些"平衡"的二叉搜索树.红黑树就是一种"平衡"的二叉搜 ...

  3. 算法导论学习-Dynamic Programming

    转载自:http://blog.csdn.net/speedme/article/details/24231197 1. 什么是动态规划 ------------------------------- ...

  4. 算法导论学习-binary search tree

    1. 概念: Binary-search tree(BST)是一颗二叉树,每个树上的节点都有<=1个父亲节点,ROOT节点没有父亲节点.同时每个树上的节点都有[0,2]个孩子节点(left ch ...

  5. 算法---数组总结篇2——找丢失的数,找最大最小,前k大,第k小的数

    一.如何找出数组中丢失的数 题目描述:给定一个由n-1个整数组成的未排序的数组序列,其原始都是1到n中的不同的整数,请写出一个寻找数组序列中缺失整数的线性时间算法 方法1:累加求和 时间复杂度是O(N ...

  6. "《算法导论》之‘线性表’":基于静态分配的数组的顺序表

    首先,我们来搞明白几个概念吧(参考自网站数据结构及百度百科). 线性表 线性表是最基本.最简单.也是最常用的一种数据结构.线性表中数据元素之间的关系是一对一的关系,即除了第一个和最后一个数据元素之外, ...

  7. 算法导论学习-prim算法

    一. 关于最小生成树 对于无向连通图G=(V,E),其中V表示图的顶点,E表示图的边,对于每条边都有一个权值,可以理解为边a->b的权值C为从a走到b要走的路程为C.现在我们希望找到一个无回路的 ...

  8. 算法导论学习-RED-BLACK TREE

    1. 红黑树(RED-BLACK TREE)引言: ------------------------------------- 红黑树(RBT)可以说是binary-search tree的非严格的平 ...

  9. 算法导论学习-heapsort

    heap的定义:如果数组a[1,....n]满足:a[i]>a[2*i] && a[i]>a[2*i+1],1<=i<=n/2,那么就是一个heap,而且是ma ...

随机推荐

  1. react随笔-1(为什么在react使用jq无法正确渲染组件位置)

    今天心血来潮,打开了sublime想玩玩react,然后大家都知道的先引入一大串 就是在百度静态资源库里找到的. 然后贴html代码 对的,没错,就这么一行,毕竟只是测试嘛 然后js代码 大家一定要注 ...

  2. kvm安装图终端界面及形界面安装系统

    1.图形界面安装: qemu-img create -f qcow2 /kvm/os/vm-01.qcow2 16G mkdir -p /kvm/iso cd /kvm/iso 上传事先下载好的镜像文 ...

  3. 如何学好C++语言(转)

    不久前发现陈皓的blog -- 酷壳,闲暇之余看看,学到不少东西,感觉作为一个程序员自己才刚开始也还有很长的路需要去走.真后悔本科到研究生的6年间看书不够,那只有从现在起不断的学习.最近有李开复老师得 ...

  4. php 后端规范

    后端框架地址: git@gitee.com:xielisen/xcore.git 框架使用规范,内部沟通 Controller命名规范 1, 首字母大写,其余小写 2, 对应数据库名称. 不要下划线 ...

  5. Spider-天眼查字体反爬

    字体反爬也就是自定义字体反爬,通过调用自定义的woff文件来渲染网页中的文字,而网页中的文字不再是文字,而是相应的字体编码,通过复制或者简单的采集是无法采集到编码后的文字内容! 1.思路 近期在爬取天 ...

  6. HDU 1087 Super Jumping! Jumping! Jumping! (LIS的最大和)

    题意: 给定n个数的序列, 找出最长上升子序列和. 分析: #include<cstdio> #include<iostream> #include<queue> ...

  7. webservice学习第二天

    1 课程回顾 l 什么是webservice 远程调用技术:系统和系统之间的调用,获取远程系统里的业务数据 Webservice使用http传输SOAP协议的数据的一种远程调用技术 l Webserv ...

  8. 如何安装python包

    安装python包有两种方法: 使用Python包管理器pip工具 在Linux系统中,首先 yum install python-pip 然后就可以欢快的pip install *** 啦 源代码安 ...

  9. Ural 1960 Palindromes and Super Abilities

    Palindromes and Super Abilities Time Limit: 1000ms Memory Limit: 65536KB This problem will be judged ...

  10. Leetcode 224.基本计算器

    基本计算器 实现一个基本的计算器来计算一个简单的字符串表达式的值. 字符串表达式可以包含左括号 ( ,右括号 ),加号 + ,减号 -,非负整数和空格  . 示例 1: 输入: "1 + 1 ...