【题目链接】
ybt 1235:输出前k大的数
OpenJudge NOI 2.4 7617:输出前k大的数
【题目考点】
1. 排序
【解题思路】
要输出前k大的数,需要对数字序列做降序排序。由于数据规模达到
1
0
5
10^5
105,所以必须选用复杂度为
O
(
n
l
o
g
n
)
O(nlogn)
O(nlogn)的排序方法。
可选的排序方法有归并排序、快速排序、堆排序、二叉树排序等。可以选择手写,或使用stl中已有的函数或容器。
【题解代码】
解法1:归并排序
- 手写归并排序
#include <bits/stdc++.h>
using namespace std;
#define N 100005
int a[N], t[N], n, k;
void mergeSort(int l, int r)
{
if(l >= r)
return;
int m = (l + r) / 2;
mergeSort(l, m);
mergeSort(m+1,r);
int li = l, ri = m+1, ti = l;
while(li <= m && ri <= r)
{
if(a[li] > a[ri])
t[ti++] = a[li++];
else
t[ti++] = a[ri++];
}
while(li <= m)
t[ti++] = a[li++];
while(ri <= r)
t[ti++] = a[ri++];
for(int i = l; i <= r; ++i)
a[i] = t[i];
}
int main()
{
scanf("%d", &n);
for(int i = 1; i <= n; ++i)
scanf("%d", &a[i]);
scanf("%d", &k);
mergeSort(1, n);
for(int i = 1; i <= k; ++i)
printf("%d\n", a[i]);
return 0;
}
- 使用stable_sort函数
#include <bits/stdc++.h>
using namespace std;
#define N 100005
int a[N], n, k;
bool cmp(int x, int y)
{
return x > y;
}
int main()
{
scanf("%d", &n);
for(int i = 1; i <= n; ++i)
scanf("%d", &a[i]);
scanf("%d", &k);
stable_sort(a+1, a+1+n, cmp);
for(int i = 1; i <= k; ++i)
printf("%d\n", a[i]);
return 0;
}
解法2:快速排序
- 手写快速排序
#include <bits/stdc++.h>
using namespace std;
#define N 100005
int a[N], n, k;
void quickSort(int l, int r)
{
if(l >= r)
return;
int i = l, j = r;
int mid = a[(l+r)/2];//将当前序列在中间位置的数定义为分隔数
while(i <= j)//注意这里不能少了等号
{
while(a[i] > mid)
i++; //在左半部分寻找小于等于中间数的数
while(a[j] < mid)
j--; //在右半部分寻找大于等于中间数的数
if(i <= j) //若找到一组与排序目标不一致的数对
{
swap(a[i], a[j]);//则交换它们
i++;
j--;
}
}
quickSort(l, j);//递归搜索左右区间
quickSort(i, r);
}
int main()
{
scanf("%d", &n);
for(int i = 1; i <= n; ++i)
scanf("%d", &a[i]);
scanf("%d", &k);
quickSort(1, n);
for(int i = 1; i <= k; ++i)
printf("%d\n", a[i]);
return 0;
}
- 使用sort函数
#include <bits/stdc++.h>
using namespace std;
#define N 100005
int a[N], n, k;
bool cmp(int x, int y)
{
return x > y;
}
int main()
{
scanf("%d", &n);
for(int i = 1; i <= n; ++i)
scanf("%d", &a[i]);
scanf("%d", &k);
sort(a+1, a+1+n, cmp);
for(int i = 1; i <= k; ++i)
printf("%d\n", a[i]);
return 0;
}
解法3:堆排序
- 手写堆
#include <bits/stdc++.h>
using namespace std;
#define N 100005
int heap[N], n, k;
//b是否比a高级。堆顶是最高级的元素
bool isPrior(int a, int b)
{
return a > b;//a > b:小顶堆, a < b:大顶堆
}
//第i结点上移
void shiftUp(int i)
{
if(i == 1)//p是根结点
return;
if(isPrior(heap[i/2], heap[i]))
{
swap(heap[i], heap[i/2]);
shiftUp(i/2);
}
}
//第i结点下沉
void shiftDown(int i)
{
if(i > n/2)//如果i是叶子结点
return;
int sel;//选择交换的结点位置
if(2*i+1 <= n && isPrior(heap[2*i],heap[2*i+1]))//有右孩子且右孩子值更大
sel = 2*i + 1;
else//没有右孩子或左孩子值更大
sel = 2*i;
if(isPrior(heap[i],heap[sel]))
{
swap(heap[sel], heap[i]);
shiftDown(sel);
}
}
//建堆
void buildHeap()
{
for(int i = n/2; i >= 1; --i)
shiftDown(i);
}
//插入元素
void insert(int val)
{
heap[++n] = val;
shiftUp(n);
}
//删除元素
void del()
{
swap(heap[1], heap[n]);
n--;
shiftDown(1);
}
//获取堆顶
int top()
{
return heap[1];
}
//堆排序 这样做降序排列用小顶堆
void heapSort()
{
int tot = n;
for(int i = 1; i <= tot - 1; ++i)
del();
}
int main()
{
scanf("%d", &n);
for(int i = 1; i <= n; ++i)
scanf("%d", &heap[i]);
scanf("%d", &k);
buildHeap();
heapSort();
for(int i = 1; i <= k; ++i)
printf("%d\n", heap[i]);
return 0;
}
- 使用stl的优先队列
#include <bits/stdc++.h>
using namespace std;
int n, k;
priority_queue<int, vector<int>, less<int> > pq;//less仿函数为:a<b,大顶堆 做降序排序
int main()
{
int a;
scanf("%d", &n);
for(int i = 1; i <= n; ++i)
{
scanf("%d", &a);
pq.push(a);
}
scanf("%d", &k);
for(int i = 1; i <= k; ++i)
{
printf("%d\n", pq.top());
pq.pop();
}
return 0;
}
解法4:二叉树排序
使用STL multiset
#include <bits/stdc++.h>
using namespace std;
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
multiset<int, greater<int> > ms;//添加greater<int>,返回a > b,这样可以使对ms的中序遍历可以得到降序排序
int n, a, k, i;
cin >> n;
for(int i = 1; i <= n; ++i)
{
cin >> a;
ms.insert(a);
}
cin >> k;
i = 0;//下面输出的元素个数
for(int v : ms)
{
cout << v << endl;
if(++i == k)
break;
}
return 0;
}