![]() ![]() ![]() ![]() |
题目大意
让你在栈的入栈弹栈基础之上在添加一个功能——PeekMedian,选出第k小的元素,规定n为偶数时k = n/2;奇数时为(n+1)/2.思路解析
一道很硬的题,涉及到高级数据结构树状数组,有关知识点讲解请移步:点击这里
用暴力只能通过一个测试点。声明C数组,大小为100010,下标为值,元素为1表示该值存在;所以用getsum累加时,x前面有n个1就说明x是第n+1位。另外,在找第k小的数时要用二分法。还要注意不能使用C++的cin输入,在声明max的时候过大会导致PAT编译失败.
2020.5.20更新:
今天复习数据结构忽然回忆到这题,觉得有些不妥。
对于本题利用树状数组未免有些“小题大做”了。本题要找第K小的元素,可以考虑使用快排中的分组思想。
这是因为每一趟快排都会把一个元素pivot放到正确位置,且pivot之前的元素均小于pivot,这也就说明pivot的位置pos就是第pos小的元素。
有小伙伴可能会疑问:这种方法对于有重复元素的情况还适用吗?
是适用的。请看下面的分组代码:
...
while(low < high){
while(low < high && arr[high] >= pivot) {
high--;
}
arr[low] = arr[high];
while(low < high && arr[low] <= pivot) {
low++;
}
arr[high] = arr[low];
}
arr[low] = pivot;
...
请看到第2行和第6行while中的判断条件,high指针是将小于pivot的元素放到了pivot之前,所以就算有重复元素也不会记入K;同理,low指针将大于pivot的元素放到pivot的后面。
示例代码
#include<iostream>
#include<stack>
using namespace std;
int n;
int mmax = 100010;
int C[100010];//角标作为值,元素为1表明存在该值
int lowbit(int i) {
return i&(-i);
}
void update(int x,int v) {
for (int i = x; i < mmax; i += lowbit(i)) {
C[i] += v;
}
}
int getsum(int x) {
int sum = 0;
for (int i = x; i >= 1; i -= lowbit(i)) {
sum += C[i];
}
return sum;
}
int main() {
scanf("%d", &n);
char str[15];
stack<int> sta;
for (int i = 0; i < n; i++) {
scanf("%s", str);
if (str[1] == 'e') {//PeekMedian
if (sta.empty()) {
printf("Invalid\n");
continue;
}
int left = 1, right = mmax, k = (sta.size() + 1) / 2;
while (left < right) {
int mid = (left + right) / 2;
if (getsum(mid) >= k) {
right = mid;
}
else {
left = mid + 1;
}
}
printf("%d\n", left);
}
else if (str[1] == 'u') {//push
int temp;
scanf("%d", &temp);
sta.push(temp);
update(temp, 1);
}
else {//pop
if (sta.empty()) {
printf("Invalid\n");
continue;
}
printf("%d\n", sta.top());
update(sta.top(), -1);//要将该位置为0,所以减去前面加的1
sta.pop();
}
}
return 0;
}