数据结构:栈 C++

题目来自于

http://zhedahht.blog.163.com/blog/#m=0&t=1&c=fks_081075092084086069084074084070080087080066083082

非常感谢何海涛,July大神和众网友。


本博文初衷是为了方便自己准备面试,有一篇文章可以看所有的东西,省的翻来翻去。

代码是手打的Qt C++,解法中不考虑实现最简单的毫无难度的暴力解法了。

因为很短,不区分*.cc和*.hh文件了,都写在一起; 注释初衷也是为了自己理解。

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

1.1 设计包含min函数的栈[数据结构]

题目描述:

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

分析和C++代码:

//程序员面试题精选100题:02设计包含min函数的栈[数据结构]

// 题目:定义栈的数据结构,要求添加一个min函数,能够得到栈的最小元素。要求函数min、push以及pop的时间复杂度都是O(1)。
 
#include<iostream>
#include<stack>
 
using namespace std;
 
 
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
// 推荐解法: Use an extra stack to maintain the minimums.
//    To retrieve the current minimum, just return the top element from minimum stack.
//    Each time you perform a push operation, check if the pushed element is a new minimum. If it is, push it to the minimum stack too.
//    When you perform a pop operation, check if the popped element is the same as the current minimum. If it is, pop it off the minimum stack too.
// All of the above operates in O(1) constant time.
template <class T>
class minStack
{
public:
    void push(T data)
    {
        m_dataStack.push(data);
        // 判断data是否是最小的
        if(m_minStack.empty() || data < m_minStack.top())
        {
            m_minStack.push(data);
        }
    }
 
    bool pop()
    {
        if(m_dataStack.empty())
        {
            return false;
        }else
        {
            // 要pop的元素同时也是最小的,那么同时要pop最小栈
            if(m_dataStack.top() == m_minStack.top())
            {
                m_minStack.pop();
            }
            m_dataStack.pop();
        }
        return true;
    }
    
    // getMin不应该会变动成员变量
    bool getMin(T& min) const
    {
        if(m_minStack.empty())
        {
            // 空栈
            return false;
        }else
        {
            // 栈顶元素最小
            min = m_minStack.top();
            return true;
        }
    }
 
private:
    stack<T> m_dataStack;
    stack<T> m_minStack;
 
};
 
 
int main()
{
    minStack<int> myStack;
    for(int ii = 6; ii >0 ; --ii)
    {
        myStack.push(ii);
    }
    int min = -1;
    while (myStack.getMin(min))
    {
        cout << "min of myStack: " << min << endl;
        myStack.pop();
    }
 
 
}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

1.2 用两个栈实现队列[数据结构]

题目描述:

某队列的声明如下:

template<typename T>class CQueue
{
public:
      CQueue() {}
      ~CQueue() {}

      void appendTail(const T& node); // append a element to tail
      void deleteHead();               // remove a element from head 

private:
     T> m_stack1;
     T> m_stack2;
};


分析与C++代码:

// 程序员面试题精选100题:18用两个栈实现队列[数据结构]

// 题目:某队列的声明如下:
//template<typename T> class CQueue
//{
//public:
//      CQueue() {}
//      ~CQueue() {}
//      void appendTail(const T& node);  // append a element to tail
//      void deleteHead();               // remove a element from head
//private:
//     T> m_stack1;
//     T> m_stack2;
//};
#include<iostream>
#include<stack>
using namespace std;
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
// 推荐解法: 倒栈法
// 栈是先进后出,队列是先进先出,所以一个队列是无法用一个栈来实现的;
// 如果我们每次append都把数据压栈1,那么delete时要删除的数据其实在
// 栈底;但现在我们另外还有一个空栈2,如果我们把栈1倒到这个空栈2里,正
// 好就把栈1里的数据顺序翻转了,即 栈2的top就是要删除的数据;
// 如果有新数据来,就压到栈1,如果要删除数据,就删除栈2的top
// 如果栈2为空,就把栈1倒过去。
template<typename T>
class CQueue
{
public:
      CQueue() {}
      ~CQueue() {}
      void appendTail(const T& node);  // append a element to tail
      void deleteHead();               // remove a element from head
private:
     stack<T> m_stack1;
     stack<T> m_stack2;
};
template<typename T>
void CQueue<T>::appendTail(const T& node)
{
    // 新数据压栈1
    m_stack1.push(node);
}
template<typename T>
void CQueue<T>::deleteHead()
{
    // 查看栈2是不是为空,为空就把栈1的数据倒给栈2
    if(m_stack2.empty())
    {
        while(!m_stack1.empty())
        {
            T tmp = m_stack1.top();
            m_stack1.pop();
            m_stack2.push(tmp);
        }
    }
    // 其实应该再检查下是否为空
    // 万一栈1原来也是空,啥也没倒过来
    if(!m_stack2.empty())
    {
        m_stack2.pop();
    }
    return;
}
int main()
{
    CQueue<int> myQueue;
    for(int ii = 6; ii >0 ; --ii)
    {
        myQueue.appendTail(ii);
    }
    for(int ii = 6; ii >0 ; --ii)
    {
        myQueue.deleteHead();
    }
}

1.3 栈的push、pop序列[数据结构] 

题目描述:

输入两个整数序列。其中一个序列表示栈的push顺序,判断另一个序列有没有可能是对应的pop顺序。为了简单起见,我们假设push序列的任意两个整数都是不相等的。

比如输入的push序列是12345,那么45321就有可能是一个pop系列。因为可以有如下的pushpop序列:push 1push 2push 3push 4poppush 5poppoppoppop,这样得到的pop序列就是45321。但序列43512就不可能是push序列12345pop序列。

分析与C++代码:

http://codercareer.blogspot.fi/2011/11/no-21-push-and-pop-sequences-of-stacks.html

// 程序员面试题精选100题:24栈的push、pop序列[数据结构]

// 题目:输入两个整数序列。其中一个序列表示栈的push顺序,
// 判断另一个序列有没有可能是对应的pop顺序。
// 为了简单起见,我们假设push序列的任意两个整数都是不相等的。
// 比如输入的push序列是1、2、3、4、5,那么4、5、3、2、1就有可能是一个pop系列。
// 因为可以有如下的push和pop序列:push 1,push 2,push 3,push 4,pop,push 5,pop,pop,pop,pop,
// 这样得到的pop序列就是4、5、3、2、1。但序列4、3、5、1、2就不可能是push序列1、2、3、4、5的pop序列。
#include<iostream>
#include<stack>
using namespace std;
template<typename T>
bool isPopSequence(stack<T>& myStack, T pushArray[], T popArray[], int length);
int main()
{
    stack<int> myStack;
    int pushArray[] = {1,2,3,4,5};
//    int popArrary[] = {4,3,5,1,2}; // no
    int popArrary[] = {4,5,3,2,1}; // yes
    int length = sizeof(pushArray)/sizeof(pushArray[0]);
    cout << "length: " << length << endl;
    if(isPopSequence(myStack, pushArray, popArrary, length))
    {
        cout << "yes!" << endl;
    }else
    {
        cout << "no!" << endl;
    }
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
// 推荐解法: 辅助栈
//行不行最好的方法就是试一试,所以要创建一个栈。
//可以这样试:
//1)看pop序列的第一个元素,把push序列里这个元素和之前的元素都先压栈,然后pop栈顶;
//2)然后查看第二个pop序列的元素,看它是不是栈顶元素,
//    是pop;
//    不是继续把push序列压栈,直到这个元素;
//         如果push序列压完了还没有这个元素,返回false;
//         如果有这个元素了,就pop出栈;回到第一步继续查看。
//If the number to be popped is currently on top of stack, just pop it.
//If it is not on the top of stack, we have to push remaining numbers into
//the auxiliary stack until we meet the number to be popped.
//If the next number to be popped is not remaining in the push sequence,
//it is not a pop sequence of a stack.
// http://codercareer.blogspot.fi/2011/11/no-21-push-and-pop-sequences-of-stacks.html
template<typename T>
bool isPopSequence(stack<T>& myStack, T pushArray[], T popArray[], int length)
{
    if (pushArray == NULL || popArray == NULL)
    {
        return false;
    }
    int pushArrayIndex = 0;
    int popArrayIndex = 0;
    while (popArrayIndex < length)
    {
        // 栈顶元素不是要pop的
        if (myStack.empty() || myStack.top() != popArray[popArrayIndex] )
        {
            // 如果还有未压栈的数据,一直压栈
            for(; pushArrayIndex < length; ++pushArrayIndex)
            {
                myStack.push(pushArray[pushArrayIndex]);
                // 如果压到了要pop的数据,说明可以查下一个popArrayIndex了
                if (myStack.top() == popArray[popArrayIndex])
                {
                    cout << pushArrayIndex << ": " <<myStack.top() <<" " << popArrayIndex << ": " << popArray[popArrayIndex] << endl;
                    myStack.pop();
                    pushArrayIndex++;
                    popArrayIndex++;
                    break;
                }
            }
            // 如果已经没有可以压的数据,而栈顶仍不是要pop的数据,返回false
            if(( myStack.top() != popArray[popArrayIndex]) && (pushArrayIndex == length))
            {
                return false;
            }
        }
        // 栈顶元素是要pop的
        if(!myStack.empty() && myStack.top() == popArray[popArrayIndex])
        {
            myStack.pop();
            popArrayIndex++;
        }
    }
    return true;
}

1.4 颠倒栈[数据结构]

问题描述:

用递归颠倒一个栈。例如输入栈{1, 2, 3, 4, 5}1在栈顶。颠倒之后的栈为{5, 4, 3, 2, 1}5处在栈顶。

分析与C++代码:

// 程序员面试题精选100题:39 颠倒栈[数据结构]

// 题目:用递归颠倒一个栈。例如输入栈{1, 2, 3, 4, 5},1在栈顶。
// 颠倒之后的栈为{5, 4, 3, 2, 1},5处在栈顶。
 
#include<iostream>
#include<stack>
#include<string>
 
using namespace std;
 
// 注意stack传进来不能是引用 不然打印时就pop空了
template<typename T>
void printStack(stack<T> myStack, string info)
{
    cout << info <<": ";
    while(!myStack.empty())
    {
        cout << myStack.top() << "->";
        myStack.pop();
    }
    cout << "end" << endl;
}
 
template<typename T>
void addToStackBottom(stack<T>& myStack, T elem);
template<typename T>
void reverseStack(stack<T>& myStack);
 
int main()
{
    stack<int> myStack;
    for (int ii = 0; ii < 5; ii++)
    {
        myStack.push(ii);
    }
    printStack(myStack,"original");
 
    reverseStack(myStack);
 
    printStack(myStack,"reversed");
}
 
 
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
// 推荐解法: 递归
// 最直接的方法就是再创建一个数组,把这个数组倒过去,这样自然就是颠倒栈了。
// 不过这种方法空间复杂度O(n)
//
// (注意::其实递归 根本就不省空间)
// 我们再来考虑怎么递归。我们把栈{1, 2, 3, 4, 5}看成由两部分组成:栈顶元素1和剩下的部分{2, 3, 4, 5}。
// 如果我们能把{2, 3, 4, 5}颠倒过来,变成{5, 4, 3, 2},然后在把原来的栈顶元素1放到底部,
// 那么就整个栈就颠倒过来了,变成{5, 4, 3, 2, 1}。
// 接下来我们需要考虑两件事情:一是如何把{2, 3, 4, 5}颠倒过来变成{5, 4, 3, 2}。
// 我们只要把{2, 3, 4, 5}看成由两部分组成:栈顶元素2和剩下的部分{3, 4, 5}。
// 我们只要把{3, 4, 5}先颠倒过来变成{5, 4, 3},然后再把之前的栈顶元素2放到最底部,也就变成了{5, 4, 3, 2}。
// 也就是每一次试图颠倒一个栈的时候,现在栈顶元素pop出来,再颠倒剩下的元素组成的栈,
// 最后把之前的栈顶元素放到剩下元素组成的栈的底部。递归结束的条件是剩下的栈已经空了。\
// Reverse a stack recursively in three steps:
// 1. Pop the top element
// 2. Reverse the remaining stack
// 3. Add the top element to the bottom of the remaining stack
 
template<typename T>
void reverseStack(stack<T>& myStack)
{
    if(!myStack.empty())
    {
        T top = myStack.top();
        myStack.pop();
        reverseStack(myStack);
        addToStackBottom(myStack, top);
    }
}
// 我们需要考虑的另外一件事情是如何把一个元素e放到一个栈的底部,也就是如何实现addToStackBottom。
// 这件事情不难,只需要把栈里原有的元素逐一pop出来。当栈为空的时候,push元素e进栈,此时它就位于栈的底部了。
// 然后再把栈里原有的元素按照pop相反的顺序逐一push进栈。
// 注意到我们在push元素e之前,我们已经把栈里原有的所有元素都pop出来了,我们需要把它们保存起来,
// 以便之后能把他们再push回去。我们当然可以开辟一个数组来做,但这没有必要。由于我们可以用递归来做这件事情,
// 而递归本身就是一个栈结构。我们可以用递归的栈来保存这些元素。
// add an elem to the bottom in three steps:
// 1. Pop the top element
// 2. solve the smaller "add an elem to the bottom" problem // 减治的思想 减去一个常量
// 3. Push the top element back
template<typename T>
void addToStackBottom(stack<T>& myStack, T elem)
{
 
    if(myStack.empty())
    {
        myStack.push(elem);
    }
    else
    {
        T top = myStack.top();
        myStack.pop();
        addToStackBottom(myStack, elem);
        myStack.push(top);
    }
}
 



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值