题目:用递归颠倒一个栈。例如输入栈{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++]);
}