题目来自于
http://zhedahht.blog.163.com/blog/#m=0&t=1&c=fks_081075092084086069084074084070080087080066083082
非常感谢何海涛,July大神和众网友。
本博文初衷是为了方便自己准备面试,有一篇文章可以看所有的东西,省的翻来翻去。
代码是手打的Qt C++,解法中不考虑实现最简单的毫无难度的暴力解法了。
因为很短,不区分*.cc和*.hh文件了,都写在一起; 注释初衷也是为了自己理解。
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1.1 设计包含min函数的栈[数据结构]
题目描述:
定义栈的数据结构,要求添加一个min函数,能够得到栈的最小元素。要求函数min、push以及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序列是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序列。
分析与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);
}
}