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

题目:定义栈的数据结构,要求添加一个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
     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/


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值