题目:定义栈的数据结构,要求添加一个min函数,能够得到栈的最小元素。要求函数min、push以及pop的时间复杂度都是O(1)

如果没有min,该栈可以很轻松地构造出来,现在添加了一个min功能,首先想到的是在栈的数据结构里增加一个字段来标识栈中的最小元素,每次入栈的时候将待入栈的数与该最小元素比较,根据结果来做相应的替换,当仔细想一下之后,发现存在这样一种情况:在某一元素出栈之后,假设该元素为栈中的最小元素,那么标识栈中的最小元素的那个字段应该做何改变?而且再也无法正确找出该栈中的最小元素!
于是,为了记录栈的每一种状态,不可能通过一个字段就能实现,因此,构造一个辅助栈来记录,辅助栈的元素个数与主栈的元素个数相同,辅助栈的栈顶代表当前主栈中最小元素的位置(考虑到栈元素数据结构的复杂性,只记录位置可以节省空间)。

1.元素入栈之后,则可以先在辅助栈的栈顶找到入栈之前主栈中的最小元素,然后与入栈元素相比较
  1)如果入栈元素小,将该元素在主栈中的序号压入辅助栈中,表示当前主栈中的最小元素的序号(其实
   就是主栈的长度-1)
  2)如果入栈元素大,将辅助栈中的原栈顶压入辅助栈中,表示当前主栈中的最小元素的序号还是上
   一次栈的状态中的最小元素的序号

2.元素出栈的时候,只需要将主栈出栈和辅助栈出栈,因为只要当主栈和辅助栈的长度相同,辅助栈中的栈顶就代表了当前主栈中的最小元素的序号,这是一一对应的关系

3.min函数只需要根据辅助栈的栈顶得到最小元素的序号即可在O(1)时间内找到

#include <deque>
   #include <assert.h>

template <typename T> class CStackWithMin
{
public:
      CStackWithMin(void) {}
      virtual ~CStackWithMin(void) {}

T& top(void);
      const T& top(void) const;

void push(const T& value);
      void pop(void);

const T& min(void) const;

private:
     T> m_data;               // the elements of stack
     size_t> m_minIndex;      // the indices of minimum elements
};

// get the last element of mutable stack
template <typename T> T& CStackWithMin<T>::top()
{
      return m_data.back();
}

// get the last element of non-mutable stack
template <typename T> const T& CStackWithMin<T>::top() const
{
      return m_data.back();
}

//将当前元素入栈
template <typename T> void CStackWithMin<T>::push(const T& value)
{
      // append the data into the end of m_data
      m_data.push_back(value);

// 如果当前辅助栈0的元素是个0,那么显然说明当前主栈中的最小元素的序号就是0,将0入栈即可
      if(m_minIndex.size() == 0)
            m_minIndex.push_back(0);
      else
      {    //如果入栈的元素小,那么将该元素在栈中的序号(m_data.size() - 1)压入辅助栈()
            if(value < m_data[m_minIndex.back()])
                  m_minIndex.push_back(m_data.size() - 1);
            else

//否则将上一次栈中的最小元素的序号压入栈中

m_minIndex.push_back(m_minIndex.back());
      }
}

// 弹出栈中的最小元素
template <typename T> void CStackWithMin<T>::pop()
{
      m_data.pop_back();
      m_minIndex.pop_back();
}

// 得到栈中的最小元素
template <typename T> const T& CStackWithMin<T>::min() const
{
      assert(m_data.size() > 0);
      assert(m_minIndex.size() > 0);
      return m_data[m_minIndex.back()];
}

可能有些朋友不明白为什么在出栈的时候只要将两个栈分别出栈即可,读者可以仔细分析一下,记住这样一条规律:只要主栈的长度和辅助栈的长度一样的时候,那么辅助栈的栈顶一定代表着主栈中最小元素的序号。个人认为这是一种变相的动态规划,每一个子结构的性质都满足这条规律,因此,我们只需要保证两个栈的长度相等即可在O(1)时间内找出最小元素,读者可以用下面的例子测试一下(转自何海涛博客)

举个例子演示上述代码的运行过程:

步骤         数据栈      辅助栈        最小值
     1.push 3    3          0             3
     2.push 4    3,4        0,0           3
     3.push 2    3,4,2      0,0,2         2
     4.push 1    3,4,2,1    0,0,2,3       1
     5.pop       3,4,2      0,0,2         2
     6.pop       3,4        0,0           3
     7.push 0    3,4,0      0,0,2         0

多演算几遍,你就会发现上述的规律

本文分析自何海涛博客
http://zhedahht.blog.163.com/blog/static/25411174200712895228171/

何海涛算法面试题感悟之二:设计包…相关推荐

  1. 何海涛算法面试题感悟之一:将二叉…

    题目:输入一棵二元查找树,将该二元查找树转换成一个排序的双向链表.要求不能创建任何新的结点,只调整指针的指向. 比如将二元查找树                                     ...

  2. 何海涛算法面试题感悟之五:查找最…

    题目:输入n个整数,输出其中最小的k个. 例如输入1,2,3,4,5,6,7和8这8个数字,则最小的4个数字为1,2,3和4. 一个简单的方法就是排序,先将这n个数按从大到小排序,最快需要O(nlog ...

  3. 何海涛算法面试题感悟之四:二元树…

    题目:输入一个整数和一棵二元树.从树的根结点开始往下访问一直到叶结点所经过的所有结点形成一条路径.打印出和与输入整数相等的所有路径. 例如输入整数22和如下二元树 10                 ...

  4. 何海涛算法面试题感悟之六:二元查…

    题目:输入一个整数数组,判断该数组是不是某二元查找树的后序遍历的结果.如果是返回true,否则返回false. 例如输入5.7.6.9.11.10.8,由于这一整数序列是如下树的后序遍历结果: 8   ...

  5. 何海涛算法面试题感悟之九:寻找链…

    题目:输入一个单向链表,输出该链表中倒数第k个结点.链表的倒数第0个结点为链表的尾指针.链表结点定义如下: struct ListNode {       int       m_nKey;      ...

  6. 何海涛算法面试题感悟之三:子数组…

    题目:输入一个整形数组,数组里有正数也有负数.数组中连续的一个或多个整数组成一个子数组,每个子数组都有一个和.求所有子数组的和的最大值.要求时间复杂度为O(n). 例如输入的数组为1, -2, 3, ...

  7. prim算法_历时两月,终拿字节跳动offer,算法面试题分享「带答案」

    欢迎关注专栏<Java架构筑基>--专注于Java技术的研究与分享! Java架构筑基​zhuanlan.zhihu.com Java架构筑基--专注于Java技术的研究与分享! 后续文章 ...

  8. 【机器学习算法面试题】四.深度神经网络中激活函数有哪些?

    欢迎订阅本专栏:<机器学习算法面试题> 订阅地址:https://blog.csdn.net/m0_38068876/category_11810806.html [机器学习算法面试题]一 ...

  9. 66 道前端算法面试题附思路分析助你查漏补缺

    大家好,我是漫步. 今天来分享一篇干货,前端关于算法的分析不多,下文列举了66道前端算法面试题,希望对你有所帮助. 作者:Eno_Yao https://segmentfault.com/a/1190 ...

最新文章

  1. 编程成长日记——有意思的编程题(二)
  2. python3是unicode还是utf-8_ASCII、Unicode、UTF-8以及Python3编码问题
  3. C++ : 二进制法生成子集
  4. [Eclipse]GEF入门系列(序)
  5. [deviceone开发]-do_SlideListView的简单示例
  6. Empire C:Basic 4
  7. Wpf之无法添加wpf窗体
  8. paip.deivsuit安装.检测文件与文件夹锁定进程与解锁
  9. php程序读取firework生成的png图片数据
  10. 公钥基础设施PKI体系介绍
  11. Oracle Parallel使用方法
  12. 创立仅一年GMV突破3亿!这个新锐品牌如何在快手实现爆发?
  13. 2010年01期《程序员》配套源码及相关链接
  14. 大数据清洗2(元素操作)
  15. uniapp微信小程序项目启动步骤
  16. Android开发 系统服务,android 系统服务 开发
  17. My97DatePicker时间控件使用
  18. 关于adb不识别设备解决方案
  19. 支付宝当面付-扫码支付
  20. 为什么delphi编译生成的exe文件这么大?

热门文章

  1. 成都网站优化之企业网站内部优化
  2. APP安全之SQL注入之什么是SQL注入(一)
  3. CLion+mingw-w64开发OpenGL如何导入glad
  4. 极兔、百世被罚后:每单涨价 4、5 毛
  5. oracle java.sql.SQLException: 列名无效
  6. 卡尔曼滤波 预测与测量我们更应该相信谁?
  7. cpolar内网穿透外网远程访问本地网站
  8. 关于 sso 博客大巴的神仙的一点思路
  9. 【GTK4】又快年底了,100行代码教你做一款简单的年会抽奖软件
  10. 4.Anaconda查找包,下载包,更新包,删除包