zoukankan      html  css  js  c++  java
  • 面试题:递归颠倒栈 与栈排序

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

    分析:乍一看到这道题目,第一反应是把栈里的所有元素逐一pop出来,放到一个数组里,然后在数组里颠倒所有元素,最后把数组中的所有元素逐一push进入栈。这时栈也就颠倒过来了。颠倒一个数组是一件很容易的事情。不过这种思路需要显示分配一个长度为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}

    思路类似hanoi问题

    接下来我们需要考虑两件事情:一是如何把{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}

    至于怎么把{3, 4, 5}颠倒过来……很多读者可能都想到这就是递归。也就是每一次试图颠倒一个栈的时候,现在栈顶元素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(std::stack<T>& stack)

    {

        if(!stack.empty())

        {

            T top = stack.top();

            stack.pop();

            ReverseStack(stack);

            AddToStackBottom(stack, top);

        }

    }

    我们需要考虑的另外一件事情是如何把一个元素e放到一个栈的底部,也就是如何实现AddToStackBottom。这件事情不难,只需要把栈里原有的元素逐一pop出来。当栈为空的时候,push元素e进栈,此时它就位于栈的底部了。然后再把栈里原有的元素按照pop相反的顺序逐一push进栈。

    注意到我们在push元素e之前,我们已经把栈里原有的所有元素都pop出来了,我们需要把它们保存起来,以便之后能把他们再push回去。我们当然可以开辟一个数组来做,但这没有必要。由于我们可以用递归来做这件事情,而递归本身就是一个栈结构。我们可以用递归的栈来保存这些元素。

    基于如上分析,我们可以写出AddToStackBottom的代码:

    // Add an element to the bottom of a stack:
    template<typename T> void AddToStackBottom(std::stack<T>& stack, T t)
    {
        if(stack.empty())
        {
            stack.push(t);
        }
        else
        {
            T top = stack.top();
            stack.pop();
            AddToStackBottom(stack, t);
            stack.push(top);
        }
    }
     

    完整代码:

    #include<iostream>
    #include<vector>
    #include<stack>
    using namespace std;
    
    void reverseStack2(stack<int> s,int n)
    {
        if(n==2)
        {
            int a=s.top();s.pop();
            int b=s.top();s.pop();
            s.push(b);
            s.push(a);
        }
        else
        {
            int topData=s.top(); s.pop();
            reverseStack2(s,n-1);
            
        }
    
    }
    void addToStackBottom(stack<int>& s,int top)
    {
        if(s.empty())
        {
            s.push(top);
        }
        else
        {
            int tmp=s.top();s.pop();
            //错误:addToStackBottom(s,tmp);
            addToStackBottom(s,top);
            s.push(tmp);
        }
    }
    
    
    
    void reverseStack(stack<int>& s)
    {
        if(!s.empty())
        {
            int top=s.top();s.pop();
            reverseStack(s);
            addToStackBottom(s,top);
        }
    }
    int main()
    {
        stack<int> s;
        for(int i=1;i<=5;i++)
            s.push(i);
    
        cout<<"栈顶 ";
         
            for(int i=5;i>=1;i--)
                cout<<i<<ends;
             
         
        cout<<"栈尾"<<endl;
    
        reverseStack(s);
    
        cout<<"栈顶 ";
        while(!s.empty())
        {
            int top=s.top();s.pop();
            cout<<top<<ends;
        }
        cout<<"栈尾"<<endl;
    
    }

    转自:剑指offer:http://zhedahht.blog.163.com/blog/static/25411174200943182411790/

    另外一个相似的题目;

    题目:输入两个整数序列。其中一个序列表示栈的push 顺序,
    判断另一个序列有没有可能是对应的pop 顺序。
    为了简单起见,我们假设push 序列的任意两个整数都是不相等的。
    比如输入的push 序列是1、2、3、4、5,那么4、5、3、2、1 就有可能是一个pop 系列。

     (剑指offer:

    分析:这到题除了考查对栈这一基本数据结构的理解,还能考查我们的分析能力。

    这道题的一个很直观的想法就是建立一个辅助栈,每次push的时候就把一个整数push进入这个辅助栈,同样需要pop的时候就把该栈的栈顶整数pop出来。

    我们以前面的序列4、5、3、2、1为例。第一个希望被pop出来的数字是4,因此4需要先push到栈里面。由于push的顺序已经由push序列确定了,也就是在把4 push进栈之前,数字1,2,3都需要push到栈里面。此时栈里的包含4个数字,分别是1,2,3,4,其中4位于栈顶。把4 pop出栈后,剩下三个数字1,2,3。接下来希望被pop的是5,由于仍然不是栈顶数字,我们接着在push序列中4以后的数字中寻找。找到数字5后再一次push进栈,这个时候5就是位于栈顶,可以被pop出来。接下来希望被pop的三个数字是3,2,1。每次操作前都位于栈顶,直接pop即可。

    再来看序列4、3、5、1、2。pop数字4的情况和前面一样。把4 pop出来之后,3位于栈顶,直接pop。接下来希望pop的数字是5,由于5不是栈顶数字,我们到push序列中没有被push进栈的数字中去搜索该数字,幸运的时候能够找到5,于是把5 push进入栈。此时pop 5之后,栈内包含两个数字1、2,其中2位于栈顶。这个时候希望pop的数字是1,由于不是栈顶数字,我们需要到push序列中还没有被push进栈的数字中去搜索该数字。但此时push序列中所有数字都已被push进入栈,因此该序列不可能是一个pop序列。

    也就是说,如果我们希望pop的数字正好是栈顶数字,直接pop出栈即可;如果希望pop的数字目前不在栈顶,我们就到push序列中还没有被push到栈里的数字中去搜索这个数字,并把在它之前的所有数字都push进栈。如果所有的数字都被push进栈仍然没有找到这个数字,表明该序列不可能是一个pop序列

    http://zhedahht.blog.163.com/blog/static/25411174200732102055385/

    /////////////////////////////////////////////////////////////////////////////
    // Given a push order of a stack, determine whether an array is possible to 
    // be its corresponding pop order
    // Input: pPush   - an array of integers, the push order
    //        pPop    - an array of integers, the pop order
    //        nLength - the length of pPush and pPop
    // Output: If pPop is possible to be the pop order of pPush, return true.
    //         Otherwise return false
    /////////////////////////////////////////////////////////////////////////////
    bool IsPossiblePopOrder(const int* pPush, const int* pPop, int nLength)
    {
          bool bPossible = false;
    
          if(pPush && pPop && nLength > 0)
          {
                const int *pNextPush = pPush;
                const int *pNextPop = pPop;
    
                // ancillary stack
                std::stack<int> stackData;
    
                // check every integers in pPop
                while(pNextPop - pPop < nLength)
                {
                      // while the top of the ancillary stack is not the integer 
                      // to be poped, try to push some integers into the stack
                      while(stackData.empty() || stackData.top() != *pNextPop)
                      {
                            // pNextPush == NULL means all integers have been 
                            // pushed into the stack, can't push any longer
                            if(!pNextPush)
                                  break;
    
                            stackData.push(*pNextPush);
    
                            // if there are integers left in pPush, move 
                            // pNextPush forward, otherwise set it to be NULL
                            if(pNextPush - pPush < nLength - 1)
                                  pNextPush ++;
                            else
                                  pNextPush = NULL;
                      }
    
                      // After pushing, the top of stack is still not same as 
                      // pPextPop, pPextPop is not in a pop sequence
                      // corresponding to pPush
                      if(stackData.top() != *pNextPop)
                            break;
    
                      // Check the next integer in pPop
                      stackData.pop();
                      pNextPop ++;
                }
    
                // if all integers in pPop have been check successfully, 
                // pPop is a pop sequence corresponding to pPush 
                if(stackData.empty() && pNextPop - pPop == nLength)
                      bPossible = true;
          }
    
          return bPossible;
    }

    思路:建立一个栈,若栈不为空,检查栈顶元素是否与pop序列将要弹出的元素相同,若相同则弹出栈顶元素,若不同,则检查push序列,将push序列中将与pop序列将要弹出元素不同的全部压入栈,直到找到相同的,若在push剩下的元素中找不到与pop序列将要弹出的元素相同的元素,则这个pop序列不可能是push序列的一个弹出序列,循环下去,直到pop序列完成,或肯定了pop不是朴实的一个出栈顺序。

      基于以上分析,代码如下:

    bool IsPopSeries(int *push, int *pop, int n)
    {
        assert (push != NULL);
    
        assert (pop != NULL);
    
        assert (n > 0);
    
        int *Stack = NULL;
        int nTop = -1;//nTop指向栈顶元素,不是栈顶+1
    
        if (NULL == (Stack = (int *)malloc (n * sizeof (int))))
        {
            printf ("Fail to malloc space to Stack!
    ");
            exit(1);
        }
    
        int i = 0;
        int j = 0;
    
        while (j < n)
        {
            if (nTop >= 0)
            {
                // 若栈不为空则首先检查栈顶元素是否符合要求
                if (Stack[nTop] == pop[j])
                {
                    --nTop;
                    ++j;
    
                    continue;
                }
            }
    
            while ((i < n) && (push[i] != pop[j]))
            {
                // 在push序列中找下一个要弹出的元素
                Stack[++nTop] = push[i++];
            }
    
            if (i >= n)
            {
                // push序列找完了都没有找到pop序列中将要弹出的元素
                // 则pop不可能是push的弹出序列
                break;
            }
    
            ++i;
            ++j;
        }
    
        if (j < n)
        {
            return (false);
        }
        
        return true;
    }

     以

    1 2 3  4 5

    4 5 3 2 1 为例:

    开始

    开始
    push[0]!=pop[0]
    while ((i < n) && (push[i] != pop[j]))
    {
    // 在push序列中找下一个要弹出的元素
    Stack[++nTop] = push[i++];
    }
    只要pop[j]和push不相等
    把push压入中,
    stack变成了1,2,3.
    nTop=2; i=3;


    ++i,++j;
    i=4,j=1; 每次遇到相等的就增加j


    第二次while:
    nTop>=0;
    stack[2]!=pop[1]

    ++i,++j
    i=5;j=2;

    while(i<n && push[i]!=pop[j])

    另外一种代码:

    这题可以利用一个辅助栈来实现,过程很简单,但描述起来会有一些困难,所以直接代码了。
     
    #include 
    #include
    using namespace std;
    bool isStackPushPop(int a[], int b[], int n)
    {
     stack ms;
     int i = -1, j = 0;
     while (i < n && j < n)
     {
      if (ms.empty() || ms.top() != b[j])
       ms.push(a[++i]);
      else
      {
       ms.pop();
       j ++;
      }
     }
     return ms.empty();
    }
    int main()
    {
     int a[] = {1,2,3,4,5};
     int b[] = {5,4,2,3,1};
     cout< return 0;
    }

    注意i开始为-1,使用a[++i],

    如果我改成i=0; a[i++]错误。为什么?

    我们假设n=2;push一个元素后i=1,push下一个后i=2;

    这是i<n不成立就退出了,(假设push为:1,2,pop为2,1,应该是合法的,由于推出就变成不合法了),

    改正办法:只需把i<n改成i<=n即可

    int i,j;
    i=j=0;
    while(i<=n && j<n)
    {
    if(s.empty() || s.top()!=b[j])
    {
    s.push(a[i++]);
    }

     给定一个入栈序列,求所有可能的出栈序列

    看以前的:http://www.cnblogs.com/youxin/p/3351944.html

  • 相关阅读:
    监控Redis集群
    host主机监控规则
    Prometheus自身的监控告警规则
    Prometheus alerts 各种告警规则
    Elasticsearch官方文档离线访问实操指南
    Ceph 存储集群
    采用阿里云 yum的方式安装ceph
    设置HTTP请求自动跳转HTTPS
    jumpserver 2222端口的使用
    安装jumpserver 2.1.2版本遇到的坑
  • 原文地址:https://www.cnblogs.com/youxin/p/3295534.html
Copyright © 2011-2022 走看看