不得不说编程之美是一本好书,虽然很多题目在做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 = ;
  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 = -;
  13. memset(maxVal , - , sizeof(maxVal));
  14. }
  15. void push(int x)
  16. {
  17. if(top>=MAXN) throw exception();
  18. a[++top] = x;
  19. if(top == ) maxVal[top]=x;
  20. else maxVal[top] = max(maxVal[top-] , x);
  21. }
  22. bool empty(){return top == -;}
  23. int Max()
  24. {
  25. if(empty()) return -INF;
  26. return maxVal[top];
  27. }
  28. int pop()
  29. {
  30. if(top<) return -;
  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();
  75. int op , x;
  76. while(!q.empty())
  77. {
  78. printf("输入操作类型:");
  79. scanf("%d" , &op);
  80. if(op == ){
  81. printf("输入一个数入队列:");
  82. scanf("%d" , &x);
  83. q.EnQueue(x);
  84. }
  85. else if(op == ){
  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 ;
  108. }

<<编程之美>> -- 队列中取最大值操作的问题的更多相关文章

  1. 编程之美 set 10 队列中取最大值操作问题

    题目 假设有这样一个拥有三个操作的队列 1. Enqueue(v) 2. Dequeue() 3. MaxEle() 请设计一种数据结构和算法, 让 MAXELE 操作的时间复杂度尽可能的低 思路 1 ...

  2. 在含有null值的复杂类的集合(Collection)中取最大值

    在日常编程中,经常遇到要在一组复杂类的集合(Collection)中做比较.取最大值或最小值. 举个最简单的例子,我们要在一个如下结构的集合中选取包含最大值的元素: public class Clas ...

  3. Java并发编程:阻塞队列(转载)

    Java并发编程:阻塞队列 在前面几篇文章中,我们讨论了同步容器(Hashtable.Vector),也讨论了并发容器(ConcurrentHashMap.CopyOnWriteArrayList), ...

  4. 【转】Java并发编程:阻塞队列

    在前面几篇文章中,我们讨论了同步容器(Hashtable.Vector),也讨论了并发容器(ConcurrentHashMap.CopyOnWriteArrayList),这些工具都为我们编写多线程程 ...

  5. Java并发编程:阻塞队列 <转>

    在前面几篇文章中,我们讨论了同步容器(Hashtable.Vector),也讨论了并发容器(ConcurrentHashMap.CopyOnWriteArrayList),这些工具都为我们编写多线程程 ...

  6. 12、Java并发编程:阻塞队列

    Java并发编程:阻塞队列 在前面几篇文章中,我们讨论了同步容器(Hashtable.Vector),也讨论了并发容器(ConcurrentHashMap.CopyOnWriteArrayList), ...

  7. (转)Java并发编程:阻塞队列

    Java并发编程:阻塞队列 在前面几篇文章中,我们讨论了同步容器(Hashtable.Vector),也讨论了并发容器(ConcurrentHashMap.CopyOnWriteArrayList), ...

  8. linux编程之消息队列

    消息队列是内核地址空间中的内部链表,通过linux内核在各个进程之间传递内容,消息顺序地发送到消息队列中,并且以几种不同的方式 从队列中获取,每个消息队列可以用IPC标识符唯一的进行标识,内核中的消息 ...

  9. JUC 并发编程--06, 阻塞队列(7种), 阻塞等待 api的 代码验证

    这些队列的 api ,就是添加队列,出队列,检测对首元素, 由于 add()--remove(), offer()--poll(),太简单这里不做验证, 只验证后二组api: 阻塞等待( put()- ...

随机推荐

  1. RHEL5.6配置本地yum源

    试验环境:RedHat Enterprise Linux 5.6(虚拟机) 一.挂载镜像 配置yum源第一步需要挂载镜像,或者直接复制操作系统的光盘文件至操作系统目录中. 挂载镜像命令如下: moun ...

  2. Hibernate3中重复引用hbm文件错误信息记录

    Hibernate3中重复引用hbm文件错误信息记录. 八月 ::, ERROR - Context initialization failed org.springframework.beans.f ...

  3. java数组实现买彩票(二个一维数组的比较思想)

    /** 设计一个程序,模拟从彩球池里随机抽取5个彩球(彩球池里一共有11个彩球,编号为1~11), 要求在控制台打印出这5个被取出来的彩球的编号(注意编号不能重复). 思路: 1.创建一个int类型的 ...

  4. D. Winter Is Coming 贪心(好题)

    http://codeforces.com/contest/747/problem/D 大概的思路就是找到所有两个负数夹着的线段,优先覆盖最小的长度.使得那时候不用换鞋,是最优的. 但是这里有个坑点, ...

  5. Apollo源码搭建调试看一文就够

    Apollo(阿波罗)是携程框架部门研发的分布式配置中心,能够集中化管理应用不同环境.不同集群的配置,配置修改后能够实时推送到应用端,并且具备规范的权限.流程治理等特性,适用于微服务配置管理场景. 我 ...

  6. ie浏览器和火狐浏览器对对容器宽度定义的差异

    首先我们说说firefox和IE对CSS的宽度显示有什么不同: 其实CSS ’width’ 指的是标准CSS中所指的width的宽度,在firefox中的宽度就是这个宽度.它只包含容器中内容的宽度.而 ...

  7. JS中的逻辑运算符&&、||,位运算符|,&

    1.JS中的||符号: 运算方法: 只要“||”前面为false,不管“||”后面是true还是false,都返回“||”后面的值. 只要“||”前面为true,不管“||”后面是true还是fals ...

  8. flex弹性布局操练2

    上一个是练习的1个内元素的,这次练习两个元素的. ul.box1 { list-style:none; background-color:black; display:flex; justify-co ...

  9. 文档兼容性定义,使ie按指定的版本解析

    作为开发人员,特别是作为Web的前端开发人员 ,最悲催的莫过于要不断的,不断的去调试各种浏览器的显示效果,而这其中最让人头痛的莫过于MS下的IE系列浏览器,在IE系列中的调试我们将会发现没有一个是好伺 ...

  10. Redis 存储字符串和对象

    今天用redis存储,发现客户端jedis提供的存储方法中存储的类型只有String和byte数据,没有能够存储对象的,网上发现可以序列化存储对象.这就开始了我第一次序列化之旅.     1 测试类 ...