程序员面试题精选100题(39)-颠倒栈
2012-11-26 15:14
169 查看
程序员面试题精选100题(39)-颠倒栈
题目:用递归颠倒一个栈。例如输入栈{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}。
接下来我们需要考虑两件事情:一是如何把{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<stack>
using namespace std;
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);
}
}
template<typename T> void ReverseStack(std::stack<T>& Stack)
{
if(!Stack.empty())
{
T top=Stack.top();
Stack.pop();
ReverseStack(Stack);
AddToStackBottom(Stack,top);
}
}
void main()
{
stack<int> Stack;
Stack.push(1);
Stack.push(2);
Stack.push(3);
Stack.push(4);
Stack.push(5);
ReverseStack(Stack);
while(!Stack.empty())
{
cout<<Stack.top();
Stack.pop();
}
cout<<endl;
}
参考:《剑指offer名企面试官精讲典型编程题》——何海涛
题目:用递归颠倒一个栈。例如输入栈{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}。
接下来我们需要考虑两件事情:一是如何把{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<stack>
using namespace std;
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);
}
}
template<typename T> void ReverseStack(std::stack<T>& Stack)
{
if(!Stack.empty())
{
T top=Stack.top();
Stack.pop();
ReverseStack(Stack);
AddToStackBottom(Stack,top);
}
}
void main()
{
stack<int> Stack;
Stack.push(1);
Stack.push(2);
Stack.push(3);
Stack.push(4);
Stack.push(5);
ReverseStack(Stack);
while(!Stack.empty())
{
cout<<Stack.top();
Stack.pop();
}
cout<<endl;
}
参考:《剑指offer名企面试官精讲典型编程题》——何海涛
相关文章推荐
- 程序员面试题精选100题(39)-颠倒栈
- 程序员面试题精选100题(39)-递归颠倒栈
- 程序员面试题精选100题(39)-颠倒栈
- 程序员面试题精选100题(39)-颠倒栈[数据结构]
- 程序员面试题精选100题(09)-链表中倒数第k个结点[数据结构]
- 程序员面试题精选100题(43)-n个骰子的点数
- 程序员面试题精选100题(19)-反转链表
- 程序员面试题精选100题(09)-链表中倒数第k个结点[数据结构]
- 程序员面试题精选100题(43)-n个骰子的点数[算法]
- 程序员面试题精选100题(57)-O(n)时间的排序[算法]
- 程序员面试题精选100题(09)-查找链表中倒数第k个结点
- 程序员面试题精选100题(25)-在从1到n的正数中1出现的次数
- 程序员面试题精选100题(43)-n个骰子的点数
- 程序员面试题精选100题(22)-整数的二进制表示中1的个数
- 程序员面试题精选100题(35)-找出两个链表的第一个公共结点
- 程序员面试题精选100题(04)-二元树中和为某一值的所有路径[数据结构]
- 程序员面试题精选100题(02)-设计包含min函数的栈[数据结构] - c实现
- 程序员面试题精选100题(44)-数值的整数次方
- 程序员面试题精选100题(31)-从尾到头输出链表[数据结构]
- 程序员面试题精选100题(25)-在从1到n的正数中1出现的次数