不得不说编程之美是一本好书,虽然很多题目在做acm中的过程中遇到过,不过还是有很多值得思考的地方
这是今天在编程之美上看到的一个问题,对于栈转化成队列的一个思考
平时都太过依赖c++内函数库中的栈和队列,但是对于他们的扩展我们还是应该自己进行手写栈和队列来实现更简单的算法
题目大意:
假设有这样一个拥有3个操作的队列:
1. EnQueue(v) : 将 v 加入队列
2. DeQueue: 使队列中队首元素删除并返回此元素
3.MaxElement: 返回队列中的最大元素
设计一种数据结构和算法,让MaxElement操作的时间复杂度尽可能地低
首先来思考这样一个问题:
对于前面已知的一段序列,我们加入一个元素x,得到当前序列的最大值会特别容易
maxVal[i] = max(maxVal[i-1] , x);
那么我们删掉第 i 个元素,得到的序列中的最大值也特别容易就是maxVal[i-1]
换个角度想想此时的第 i 个元素可理解为栈顶元素
也就是说这道题目改成拥有3个栈操作的队列那么我们直接就可以在单位时间内得到MaxElement
而保存这些数也就是把所有数扫一遍,O(N)就解决了
但是这里是队列,队列总是删去最前面也就是下标为 0 的元素,这样我们无法利用maxVal[i]来表示当前队列元素中的最大值
那我们可以考虑,既然栈如此方便,如何将队列用栈来表示
首先我们需要两个栈,把数据放入一个栈s2,那么这些数据其实是倒着保存的 , 那么我们要删去队列的首元素,也就是删除栈底的元素,
那么我们可以利用另一个栈s1,将前一个栈s2中的元素再倒一遍进入另一个栈s1,那么此时s1的栈顶也就是队列的栈顶
也就是说只要s1中有元素,那么s1中的栈顶始终是队列首元素
只有当s1中元素都不存在时,才将s2中的元素一次性全倒入s1
int DeQueue()
{
if(s1.empty() && s2.empty()) return -INF; //队列中值不存在
if(s1.empty()){
while(!s2.empty()){
s1.push(s2.pop());
}
}
return s1.pop();
}
那么队列中的所有元素总是分布在s1和s2中,那么最大元素既可能在s1,也可能在s2中,所以返回 return max(s1.Max() , s2.Max());
这样这个程序总的复杂度就能够达到O(N)的线性复杂度了
接下来是自己手写的一个测试过的没有问题的程序
1 #include <cstdio> 2 #include <cstring> 3 #include <exception> 4 using namespace std; 5 #define max(a,b) a>b?a:b 6 const int MAXN = 10005; 7 const int INF = 0x3fffffff; 8 struct Stack{ 9 int top , a[MAXN] , maxVal[MAXN]; 10 //a[]保存栈中的元素,maxVal[i]保存i以及前方的最大值,top为栈顶指针,初始为-1 11 Stack(){ 12 top = -1; 13 memset(maxVal , -1 , sizeof(maxVal)); 14 } 15 void push(int x) 16 { 17 if(top>=MAXN) throw exception(); 18 a[++top] = x; 19 if(top == 0) maxVal[top]=x; 20 else maxVal[top] = max(maxVal[top-1] , x); 21 } 22 bool empty(){return top == -1;} 23 int Max() 24 { 25 if(empty()) return -INF; 26 return maxVal[top]; 27 } 28 int pop() 29 { 30 if(top<0) return -1; 31 int ret = a[top--]; 32 return ret; 33 } 34 }; 35 36 struct Queue{ 37 Stack s1 , s2; 38 Queue() 39 { 40 s1 = Stack(); 41 s2 = Stack(); 42 } 43 void EnQueue(int x) 44 { 45 s2.push(x); 46 } 47 int DeQueue() 48 { 49 if(s1.empty() && s2.empty()) return -INF; //队列中值不存在 50 if(s1.empty()){ 51 while(!s2.empty()){ 52 s1.push(s2.pop()); 53 } 54 } 55 return s1.pop(); 56 } 57 int MaxElement() 58 { 59 return max(s1.Max() , s2.Max()); 60 } 61 bool empty() 62 { 63 return s1.empty()&&s2.empty(); 64 } 65 }; 66 67 int main() 68 { 69 // freopen("a.in" , "r" , stdin); 70 Queue q; 71 72 q = Queue(); 73 // int num[10] = {5 , 7 , 6 , 3 , 2 , 4 , 9 , 15 , 13 , 11}; 74 q.EnQueue(5); 75 int op , x; 76 while(!q.empty()) 77 { 78 printf("输入操作类型:"); 79 scanf("%d" , &op); 80 if(op == 1){ 81 printf("输入一个数入队列:"); 82 scanf("%d" , &x); 83 q.EnQueue(x); 84 } 85 else if(op == 2){ 86 printf("队列首元素 %d 从队列中退出\n" , q.DeQueue()); 87 } 88 else{ 89 printf("当前队列最大元素为 %d\n" , q.MaxElement()); 90 } 91 92 //下方每次用来检测队列中两个栈的数据保存情况 93 /* 94 printf("输出栈1中的元素\n"); 95 for(int i=q.s1.top ; i>=0 ; i--){ 96 printf("%d " , q.s1.a[i]); 97 } 98 puts(""); 99 100 printf("输出栈2中的元素\n"); 101 for(int i=q.s2.top ; i>=0 ; i--){ 102 printf("%d " , q.s2.a[i]); 103 } 104 puts(""); 105 */ 106 } 107 return 0; 108 }