编程之美读书笔记2.5—寻找最大的K个数
2014-11-28 09:37
190 查看
给定一个N个数的数组arr
,任意排列,选出其中最大的K个数。
方法1:排序,取后K个数。复杂度: O(NlogN)
方法2:partition,随意选一个数组中的数key,对数组进行划分,返回位置pos,如果pos右侧的数等于K,则结束,如果大于K,则再对从pos到数组尾的子数组进行partition,如果小于K,则对从数组头到pos位置的子数组进行partition,找出剩下的元素。时间复杂度:O(NlogK)
void printarray(int *arr,int beg,int end)
{
for(int i = end-1; i>=beg-1; --i)
{
cout<<arr[i]<<'
';
}
}
int partition(int *arr,int beg,int end)
{
if(beg<1||beg>end)
{
cout<<"error
input"<<'\n';
exit(0);
}
if(beg==end)
return
beg;
int i = beg-2;
int j = beg-1;
int key = arr[end-1];
while(j<end-1)
{
if(arr[j]<=key)
{
++i;
int
temp = arr[i];
arr[i]
= arr[j];
arr[j]
= temp;
}
++j;
}
int temp = arr[end-1];
arr[end-1] = arr[++i];
arr[i] = temp;
return i+1;
}
void maxK(int *arr,int beg,int end,int K)
{
if(beg<1||beg>end)
{
cout<<"error
input"<<'\n';
exit(0);
}
if(end-beg+1==K)
{
printarray(arr,beg,end);
return;
}
int mid = partition(arr,beg,end);
int tmp = end-mid+1;
if(tmp==K)
printarray(arr,mid,end);
else if(tmp<K)
{
printarray(arr,mid,end);
maxK(arr,beg,mid-1,K-tmp);
}
else
maxK(arr,mid+1,end,K);
}
int main()
{
int a[]={2,5,8,7,4,1,3,6};
maxK(a,1,8,4);
return 0;
}
3.可以使用二分搜索的策略来寻找N个数中的第K大的数。对于一个给定的数p,可以在O(N)的时间复杂度内找出所有不小于p的数。
寻找第k大的元素:
如果所有N个数都是正整数,且它们的取值范围不太大,可以考虑申请空间,记录每个整数出现的次数,然后再从大到小取最大的K个。比如,所有整数都在(0, MAXN)区间中的话,利用一个数组count[MAXN]来记录每个整数出现的个数(count[i]表示整数i在所有整数中出现的个数)。只需要扫描一遍就可以得到count数组。然后,寻找第K大的元素:
极端情况下,如果N个整数各不相同,我们甚至只需要一个bit来存储这个整数是否存在(bit位为1或为0),这样使用的空间可以大大压缩。
当然也可以使用像计数排序、桶排序等这些以O(N)的时间排序算法也可以寻找第K大的数,但这也是以空间换时间为代价的。
实际情况下,并不一定保证所有元素都是正整数,且取值范围不太大。上面的方法仍然可以推广使用。如果N个数中最大的数Vmax,最小的Vmin,我们可以把这个区间[Vmax,Vmin]分成M块,每个小区间的跨度为d=(Vmax-Vmin)/M,即[Vmin,Vmin+d],[Vmin+d,Vmin+2d]......然后,扫描一遍所有元素,统计各个小区间中的元素个数,就可以知道第K大的元素在哪一个小区间。然后,再在那个小区间中找第K大的数(此时这个小区间中,第K大的数可能就是第T大的数了,这个T和每个小区间的个数有关)。我们需要找一个尽量大的M,但M的取值受到内存的限制。
方法4:最小堆,建立一个K元素的最小堆,用arr的前K个元素初始化,然后从arr的第K+1个元素开始扫描数组,如果大于最小堆的根,则互换元素,并对根做minheap_fy,这样最后最小堆里剩的就是最大的K个元素.时间复杂度:O(NlogK)
void minheap_fy(int *heap,int index,int heapsize)
{
int left = index<<1;
int right = left + 1;
int smallest = index;
if(left<=heapsize&&heap[smallest-1]>heap[left-1])
smallest
= left;
if(right<=heapsize&&heap[smallest-1]>heap[right-1])
smallest
= right;
if(smallest != index)
{
int
temp = heap[index-1];
heap[index
- 1] = heap[smallest-1];
heap[smallest-1]
= temp;
minheap_fy(heap,smallest,heapsize);
}
}
void build_minheap(int *heap,int heapsize)
{
int temp = heapsize/2;
while(temp>0)
{
minheap_fy(heap,temp,heapsize);
--temp;
}
}
void maxK2(int *arr,int size,int K)
{
if(size<K)
{
cout<<"error
K"<<'\n';
return;
}
int *heap = new int[K];
for(int i = 0;i<K;++i)
{
heap[i]
= arr[i];
}
build_minheap(heap,K);
for(int i = K; i<size; ++i)
{
if(arr[i]>heap[0])
{
heap[0]
= arr[i];
minheap_fy(heap,1,K);
}
}
for(int i = 0;i<K;++i)
cout<<heap[i]<<'
';
delete[] heap;
}
,任意排列,选出其中最大的K个数。
方法1:排序,取后K个数。复杂度: O(NlogN)
方法2:partition,随意选一个数组中的数key,对数组进行划分,返回位置pos,如果pos右侧的数等于K,则结束,如果大于K,则再对从pos到数组尾的子数组进行partition,如果小于K,则对从数组头到pos位置的子数组进行partition,找出剩下的元素。时间复杂度:O(NlogK)
void printarray(int *arr,int beg,int end)
{
for(int i = end-1; i>=beg-1; --i)
{
cout<<arr[i]<<'
';
}
}
int partition(int *arr,int beg,int end)
{
if(beg<1||beg>end)
{
cout<<"error
input"<<'\n';
exit(0);
}
if(beg==end)
return
beg;
int i = beg-2;
int j = beg-1;
int key = arr[end-1];
while(j<end-1)
{
if(arr[j]<=key)
{
++i;
int
temp = arr[i];
arr[i]
= arr[j];
arr[j]
= temp;
}
++j;
}
int temp = arr[end-1];
arr[end-1] = arr[++i];
arr[i] = temp;
return i+1;
}
void maxK(int *arr,int beg,int end,int K)
{
if(beg<1||beg>end)
{
cout<<"error
input"<<'\n';
exit(0);
}
if(end-beg+1==K)
{
printarray(arr,beg,end);
return;
}
int mid = partition(arr,beg,end);
int tmp = end-mid+1;
if(tmp==K)
printarray(arr,mid,end);
else if(tmp<K)
{
printarray(arr,mid,end);
maxK(arr,beg,mid-1,K-tmp);
}
else
maxK(arr,mid+1,end,K);
}
int main()
{
int a[]={2,5,8,7,4,1,3,6};
maxK(a,1,8,4);
return 0;
}
3.可以使用二分搜索的策略来寻找N个数中的第K大的数。对于一个给定的数p,可以在O(N)的时间复杂度内找出所有不小于p的数。
寻找第k大的元素:
#include <iostream> using namespace std; //快速排序的划分函数 int partition(int a[],int l,int r) { int i,j,x,temp; i = l; j = r+1; x = a[l]; //将>=x的元素换到左边区域 //将<=x的元素换到右边区域 while (1) { while(a[++i] > x); while(a[--j] < x); if(i >= j) break; temp = a[i]; a[i] = a[j]; a[j] = temp; } a[l] = a[j]; a[j] = x; return j; } //随机划分函数 int random_partition(int a[],int l,int r) { int i = l+rand()%(r-l+1);//生产随机数 int temp = a[i]; a[i] = a[l]; a[l] = temp; return partition(a,l,r);//调用划分函数 } //线性寻找第k大的数 int random_select(int a[],int l,int r,int k) { int i,j; if (l == r) //递归结束 { return a[l]; } i = random_partition(a,l,r);//划分 j = i-l+1; if(k == j) //递归结束,找到第K大的数 return a[i]; if(k < j) { return random_select(a,l,i-1,k);//递归调用,在前面部分查找第K大的数 } else return random_select(a,i+1,r,k-j);//递归调用,在后面部分查找第K大的数 } int main() { int a[]={1,2,3,4,6,6,7,8,10,10}; cout<<random_select(a,0,9,1)<<endl; cout<<random_select(a,0,9,5)<<endl; return 0; }
如果所有N个数都是正整数,且它们的取值范围不太大,可以考虑申请空间,记录每个整数出现的次数,然后再从大到小取最大的K个。比如,所有整数都在(0, MAXN)区间中的话,利用一个数组count[MAXN]来记录每个整数出现的个数(count[i]表示整数i在所有整数中出现的个数)。只需要扫描一遍就可以得到count数组。然后,寻找第K大的元素:
for(sumCount = 0, v = MAXN-1; v >= 0; v--) { sumCount += count[v]; if(sumCount >= K) break; } return v;
极端情况下,如果N个整数各不相同,我们甚至只需要一个bit来存储这个整数是否存在(bit位为1或为0),这样使用的空间可以大大压缩。
当然也可以使用像计数排序、桶排序等这些以O(N)的时间排序算法也可以寻找第K大的数,但这也是以空间换时间为代价的。
实际情况下,并不一定保证所有元素都是正整数,且取值范围不太大。上面的方法仍然可以推广使用。如果N个数中最大的数Vmax,最小的Vmin,我们可以把这个区间[Vmax,Vmin]分成M块,每个小区间的跨度为d=(Vmax-Vmin)/M,即[Vmin,Vmin+d],[Vmin+d,Vmin+2d]......然后,扫描一遍所有元素,统计各个小区间中的元素个数,就可以知道第K大的元素在哪一个小区间。然后,再在那个小区间中找第K大的数(此时这个小区间中,第K大的数可能就是第T大的数了,这个T和每个小区间的个数有关)。我们需要找一个尽量大的M,但M的取值受到内存的限制。
方法4:最小堆,建立一个K元素的最小堆,用arr的前K个元素初始化,然后从arr的第K+1个元素开始扫描数组,如果大于最小堆的根,则互换元素,并对根做minheap_fy,这样最后最小堆里剩的就是最大的K个元素.时间复杂度:O(NlogK)
void minheap_fy(int *heap,int index,int heapsize)
{
int left = index<<1;
int right = left + 1;
int smallest = index;
if(left<=heapsize&&heap[smallest-1]>heap[left-1])
smallest
= left;
if(right<=heapsize&&heap[smallest-1]>heap[right-1])
smallest
= right;
if(smallest != index)
{
int
temp = heap[index-1];
heap[index
- 1] = heap[smallest-1];
heap[smallest-1]
= temp;
minheap_fy(heap,smallest,heapsize);
}
}
void build_minheap(int *heap,int heapsize)
{
int temp = heapsize/2;
while(temp>0)
{
minheap_fy(heap,temp,heapsize);
--temp;
}
}
void maxK2(int *arr,int size,int K)
{
if(size<K)
{
cout<<"error
K"<<'\n';
return;
}
int *heap = new int[K];
for(int i = 0;i<K;++i)
{
heap[i]
= arr[i];
}
build_minheap(heap,K);
for(int i = K; i<size; ++i)
{
if(arr[i]>heap[0])
{
heap[0]
= arr[i];
minheap_fy(heap,1,K);
}
}
for(int i = 0;i<K;++i)
cout<<heap[i]<<'
';
delete[] heap;
}
相关文章推荐
- 编程之美读书笔记_2.5 寻找最大的K个数 测试代码
- 编程之美读书笔记_2.5 寻找最大的K个数
- 2.5 寻找最大的k个数
- 编程之美 -- 2.5 寻找最大的K个数(快排方法)
- 编程之美 -- 2.5 寻找最大的K个数(堆排序方法)
- 编程之美 2.5 寻找最大的K个数
- 【编程之美】2.5 寻找最大的k个数
- 编程之美-2.5寻找最大的K个数
- 编程之美 2.5 寻找最大的K个数
- [编程之美] PSet2.5 寻找最大的K个数
- 编程之美:第二章 数字之魅 2.5寻找最大的k个数
- 编程之美2.5 寻找最大的K个数
- 算法->编程之美2.5 寻找最大的K个数
- 编程之美2.5——寻找最大的K个数
- 编程之美读书笔记2.10—寻找数组中的最大值和最小值
- 编程之美2.5 寻找最大的K个数
- 《编程之美》学习笔记——2.5寻找最大的K个数
- 读书笔记之编程之美 – 2.5 寻找最大的K个数
- 编程之美2.5 寻找最大的K个数
- 编程之美2.5寻找最大的K个数