您的位置:首页 > 职场人生

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

2012-12-27 11:25 309 查看
题目:定义栈的数据结构,要求添加一个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/
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: