7-1 特殊堆栈
链接:题目详情 - 7-1 特殊堆栈 (pintia.cn)
思路:
C做法:创建一个栈S和一个链表L(用于存储栈S中存在的元素并保证有序,这样当用peek函数时遍历寻找所需位置的元素并输出即可,同时在遇到push和pop的操作时,使用链表会便于进行插入和删除操作,便于实现,只是都需要遍历,所以时间复杂度大)。
纯C做法:
#include <stdio.h>
#include <stdlib.h>
struct SNode{
int data;
struct SNode* next;
};
typedef struct SNode* St;
int N=0;//记录元素个数
//返回一个头结点
St Makeempty(){
St S;
S=(St)malloc(sizeof(struct SNode));
S->next=NULL;
return S;
}
void push_k(St S,int key){
St p;
p=(St)malloc(sizeof(struct SNode));
p->data=key;
p->next=S->next;
S->next=p;
N++;
}
void pop_k(St S,St L){
if(S->next==NULL){
printf("Invalid\n");
return;
}
else{
int e;
e=S->next->data;//记录要被出栈的元素值
printf("%d\n",e);
//遍历链表寻找要被删除的e
St q=L->next,r=L;
while(e!=q->data){
q=q->next;
r=r->next;
}
r->next=q->next;//找到了,删除
St p=S->next;//出栈
S->next=p->next;
N--;//栈内元素数量--
return;
}
}
void PeekMedian(St S,St L){
St p=L;
int ci;
//如果链表空,说明还没有元素,返回
if(L->next==NULL){
printf("Invalid\n");
return;
}
//计算第几小元
if(N%2==0) ci=N/2;
else ci=(N+1)/2;
//遍历寻找第几小
while(ci--){
p=p->next;
}
printf("%d\n",p->data);
}
//新元素入栈的同时加入到链表中,同时保证有序
void insertxu(St L,int key){
St p=L->next,q;
q=(St)malloc(sizeof(struct SNode));
q->data=key;
//如果表空
if(p==NULL){
q->next=L->next;
L->next=q;
}
else{
St r=L;
//遍历寻找合适位置
while(p){
if(p->data<key){
p=p->next;
r=r->next;
}
else break;
}
//插入
r->next=q;
q->next=p;
}
}
int main(){
St S;
S=Makeempty();
St L=(St)malloc(sizeof(struct SNode));
L->next=NULL;
int n,x;
char s[10];
scanf("%d",&n);
for(int i=0;i<n;i++){
getchar();
scanf("%s",s);
if(s[1]=='u'){
scanf("%d",&x);
push_k(S,x);
insertxu(L,x);
}
else if(s[1]=='o'){
pop_k(S, L);
}
else if(s[1]=='e'){
PeekMedian( S, L);
}
}
}
以上写法只适用于数据范围较小的时候,否则会超时。
使用二分+vector数组可以有效降低时间复杂度,处理数据范围较大的情况。
AC代码:
#include <stdio.h>
#include <stdlib.h>
#include <vector>
using namespace std;
vector<int> vi;//元素在此容器中有序
struct SNode{
int data;
struct SNode* next;
};
typedef struct SNode* St;
int N=0;//记录栈(同时也是vi数组)中元素个数
St Makeempty(){
St S;
S=(St)malloc(sizeof(struct SNode));
S->next=NULL;
return S;
}
//二分查找第一个大于等于key的元素的位置,把key插入到该位置
void vi_insert(int key){
int mid=-1,l=0,r=N-1;
while(l<r){
mid=(l+r)/2;
if(vi[mid]>=key) r=mid;
else l=mid+1;
}
vi.insert(vi.begin()+l,key);
}
//二分查找x所在的位置,并删除
void vi_erase(int x){
int mid=-1,l=0,r=N-1;
while(l<=r){
mid=(l+r)/2;
if(vi[mid]==x) break;
else if(vi[mid]>x) r=mid-1;
else l=mid+1;
}
vi.erase(vi.begin()+mid);
return;
}
//入栈
void push_k(St S,int key){
St p;
p=(St)malloc(sizeof(struct SNode));
p->data=key;
p->next=S->next;
S->next=p;
N++;
}
//出栈
void pop_k(St S){
if(S->next==NULL){
printf("Invalid\n");
return;
}
St p=S->next;
printf("%d\n",p->data);
S->next=p->next;
vi_erase(p->data);
N--;//要在vi数组中删除栈顶元素后才能令N--
return;
}
void PeekMedian(){
int op;
if(vi.size()==0){
printf("Invalid\n");
return;
}
if(N%2==0) op=N/2;
else op=(N+1)/2;
printf("%d\n",vi[op-1]);//存储下标是从0~N-1,所以要让op-1
}
int main(){
St S;
S=Makeempty();
int n,x;
char s[10];
scanf("%d",&n);
for(int i=0;i<n;i++){
getchar();
scanf("%s",s);
if(s[1]=='u'){
scanf("%d",&x);
push_k(S,x);
if(N==1) vi.push_back(x);//N=1时,vi数组中还没有数,所以直接加入
else vi_insert(x);
}
else if(s[1]=='o'){
pop_k(S);
}
else if(s[1]=='e'){
PeekMedian();
}
}
}