华师一附中OI组
标题:
求数组中的第K大元素
[打印本页]
作者:
diggersun
时间:
2015-11-1 13:17
标题:
求数组中的第K大元素
本帖最后由 diggersun 于 2015-11-1 13:30 编辑
给定一个数组,求其中第K大的元素。
若K=1,是求最大的元素,一般的做法是假设A0最大,然后从A1到 An逐个和他比较,大的留下来,最后留下来的就是最大的元素,我称之为打擂台方式。
若K=2,是求次大的元素,一般的做法是先比较A0和A1,求出最大次大,然后从A2到 An逐个和他们比较,有三种情况,对应三种决策,比最大的大,介于最大和次大之间,比次大的还小。这样也是一种打擂台方式。只是留在台上的是两个人而不是一个。
若K比较大呢?有的同学想现将数组排序,然后直接输出第K大,这样做很是浪费,一个改进的做法是选择排序选择到第K次的时候,或者冒泡排序冒到第K次的时候就不再继续了。
以选择排序为例,第一轮选择实际上将当前最小(大)的数字和数组的第一个数字进行交换,那么,等到第K个数字完成交换后,其实第K小(大)已经求出,无需基础排完整个数组。
有一种分治的思想很重要,可以用来解决这一类的问题,我们这里详细说说:
1、将数组的首元素作为划分元素X,从数组后面开始,每次找到一个比X小的数字,我们把它和X换,相当于换到了前面,然后从前面找到比X大的数字,和X交换,相当于往后面换,直到指针交错无法交换为止,这样,一轮交换后,X到了某个位置,它前面数字都比它小,后面的数字都比它大。
2、假设X的位置Y刚好是第K,那么问题解决了,若Y>k,说明要到前面找,若Y<K,说明要到前面去找,直到Y==k
这样做的话,对于K比较大是很有利的,因为每次几乎都是减半,快于每次减1。前面选择排序或者冒泡,下次查找只比上一次少了一个元素,减少的有限。
作者:
diggersun
时间:
2015-11-1 23:09
#include<iostream>
#include<iomanip>
using namespace std;
int a[20]= {10,1,7,3,5,9,8,2,6,4,11,13,25,19,17,20,12,18,16,15};
int partion(int *a,int l,int r)
{
int p=a[l];//划分元素定为A[l]
while(l<r)
{
while((a[r]>=p)&&(l<r))r--;//后面找比P小的数
a[l]=a[r];
while((a[l]<=p)&&(l<r))l++;//前面找比P大的数
a[r]=a[l];
}
a[l]=p;return l;
}
int main()
{
int x=8;//其实是求第8小,最小的是第0小
int l=0,r=19;
bool bb=true;
int t;
do
{
t=partion(a,l,r);// 得到划分元素的位置
if (t>x)r=t-1; //若位置比x大,说明第X小的在前面
else if (t<x)l=t+1;//到后面去找
else bb=false;//相等表示找到了
} while (bb);
cout<<a[x]<<endl;
return 0;
}
复制代码
作者:
admin
时间:
2018-4-14 20:30
#include <iostream>
using namespace std;
const int mm=10;
int a[mm];
int i;
int hf(int l,int r)
{
int p=a[l];
while (l<r)
{
while ((a[r]>=p) &&(l<r) ) r--; ///一定是先从右边找小的
a[l]=a[r];
while ((a[l]<=p) &&(l<r) ) l++; ///再从左边找大的
a[r]=a[l];
}
a[l]=p;
return l;
}
int get(int x,int l,int r)
{
int p=hf(l,r);
if (p==x) return a[x];
else if (p>x) return get(x,l,p-1);
else return get(x,p+1,r);
}
int main()
{
///在很顺序的里面找
for (i=0; i<=9; i++) a[i]=i;
for (i=0; i<=9; i++) cout<<get(i,0,9)<<' ';
cout<<endl;
///在倒序中找
for (i=0; i<=9; i++) a[i]=9-i;
for (i=0; i<=9; i++) cout<<get(i,0,9)<<' ';
cout<<endl;
///在相同的中找
for (i=0; i<=9; i++) a[i]=1;
for (i=0; i<=9; i++) cout<<get(i,0,9)<<' ';
///在部分相同的中找
cout<<endl;
for (i=0; i<=9; i++) a[i]=10-i%2;
for (i=0; i<=9; i++) cout<<get(i,0,9)<<' ';
///在乱序的中找
cout<<endl;
for (i=0; i<=9; i++) a[i]=i*i%10;
for (i=0; i<=9; i++) cout<<get(i,0,9)<<' ';
///请体会此中的测试技巧
return 0;
}
复制代码
作者:
admin
时间:
2018-4-14 20:32
二楼的是以前对我校高中生讲的,写的是非递归的,很精妙,三楼的是递归的,很容易想到,建议初学者先学三楼的做法。
欢迎光临 华师一附中OI组 (http://hsyit.cn/)
Powered by Discuz! X3.2