队列和栈相关面试题总结
2017-05-23 23:49
369 查看
1、实现一个栈,要求实现Push(出栈)、Pop(入栈)、Min(返回最小值的操作)的时间复杂度为O(1)??
分析:
出栈和入栈根据栈自身提供的接口不难实现,而返回最小值,我们知道遍历一次栈即可找到最小值,但是对栈的操作只能在栈顶,因此,要遍历势必要改变栈的状态,而且还要求时间复杂度为O(1),即更不能遍历栈。我们可以利用两个栈同时进行操作,一个是我们放数据的栈,而另一个栈顶专门放数据中的最小值,最后需要当前最小值,直接取第二个栈顶元素即可。
2、源代码如下:
分析:栈--->先进后出 队列--->先进先出
在进行操作时,我们可利用栈的特性,在进行push操作时,使用栈操作,而进行pop操作时将栈中元素逐一放入另一栈
源代码:
3、两个队列实现栈
思路:栈---先进后出 队列---先进先出
创建两个队列,一个队列放数据,另一个当作辅助队列,当进行push(尾插)操作时直接用队列的push进行操作,而进行pop操作时,由于队列的pop是进行头删,不符合栈pop的条件,这时可将队列1中数据从第一个开始另一辅助队列2中,但是,第一个队列中最后一个元素不放入,然后,将第一个队列清空,再将辅助队列中元素按顺序放入第一个队列中即可。
源代码:
4、一个数组实现两个栈
分析:
方案一:将数组的下标为0的位置当做第一个栈的栈底,下标为1的位置当做第二个栈的栈底,将数组的偶数位置看做第一个栈的存储空间,奇数位置看做第二个栈的存储空间。
方案二:从中间分别向两边压栈
将数组的中间位置看做两个栈的栈底,压栈时栈顶指针分别向两边移动,当任何一边到达数组的起始位置或是数组尾部,则开始扩容。
方案三:从两边向中间压栈
将数组的起始位置看作是第一个栈的栈底,将数组的尾部看作第二个栈的栈底,压栈时,栈顶指针分别向中间移动,直到两栈顶指针相遇,则扩容。
比较:方案二和方案一当两栈中元素不同时,比较浪费空间,方案三节省空间
方案3代码:
#include <iostream>
using namespace std;
#include <assert.h>
//一个数组实现两个栈
//从两边开始存
template<class T>
class Stack
{
public:
Stack()
:_arr(NULL)
,_size1(0)
,_size2(0)
,_capacity(0) //初始化容量为2
{}
void Push1(const T& x)
{
_checkcapacity();
_arr[_size1] = x;
_size1++;
}
void Pop1()
{
assert(Size1() != 0);
_size1--;
}
size_t Size1()
{
return _size1;
}
T& Top1()
{
return _arr[_size1-1];
}
void Push2(const T& x)
{
_checkcapacity();
size_t botton = _capacity-_size2-1;
_arr[botton] = x;
botton--;
_size2++;
}
void Pop2()
{
assert(Size2() != 0);
_size2--;
}
size_t Size2()
{
return _size2;
}
T& Top2()
{
assert(_size2 != 0);
return _arr[_capacity-1];
}
private:
void _checkcapacity()
{
if ((_size1+_size2)==_capacity || (_capacity==0)) //容量满 或 空
{
size_t _newcapacity = _capacity*2+2;
T* tmp = new T[_newcapacity];
for (size_t i=0; i<_size1; i++) //第一个栈放数组左边
{
tmp[i] = _arr[i];
}
size_t t = _capacity-1;
for(size_t j=_newcapacity-1; (j>(_newcapacity-_size1)) && (t>_size1-1); --t,j--)
{
tmp[j] = _arr[t];
}
_capacity = _newcapacity;
swap(_arr,tmp);
}
}
private:
T* _arr;
size_t _size1;
size_t _size2;
size_t _capacity;
};
int main()
{
Stack<int> a;
a.Push1(1);
cout<<a.Size1()<<endl;
a.Push1(8);
a.Pop1();
a.Push1(5);
a.Push1(3);
cout<<a.Size1()<<endl;
cout<<a.Top1()<<endl;
a.Push1(2);
a.Push2(1);
a.Push2(0);
a.Push2(4);
cout<<a.Size2()<<endl;
cout<<a.Top2()<<endl;
a.Push2(1);
a.Push2(1);
a.Push2(4);
a.Push2(10);
a.Pop1();
cout<<a.Size2()<<endl;
a.Pop1();
a.Pop2();
a.Pop2();
cout<<a.Size2()<<endl;
cout<<a.Top2()<<endl;
return 0;
}
分析:
出栈和入栈根据栈自身提供的接口不难实现,而返回最小值,我们知道遍历一次栈即可找到最小值,但是对栈的操作只能在栈顶,因此,要遍历势必要改变栈的状态,而且还要求时间复杂度为O(1),即更不能遍历栈。我们可以利用两个栈同时进行操作,一个是我们放数据的栈,而另一个栈顶专门放数据中的最小值,最后需要当前最小值,直接取第二个栈顶元素即可。
2、源代码如下:
//push出栈 ------ pop入栈 min返回最小值 ------ 时间复杂度为O(1) //不能遍历 #include <iostream> using namespace std; #include <stack> template <class T> class StackMinValue { public: StackMinValue() {} void Pop(const T& x) { s.push(x); if(min.empty()) { min.push(x); } else if(min.top()>=x) { min.push(x); } } void Push() //出栈 { if(min.top() == s.top() && s.size() != 1) { min.pop(); } s.pop(); } T& Min() { return min.top(); } ~StackMinValue() {} private: stack<T> s,min; }; int main() { StackMinValue<int> a; a.Pop(3); a.Pop(2); cout<<a.Min(); a.Pop(5); a.Pop(8); a.Pop(1); cout<<a.Min(); a.Push(); a.Push(); cout<<a.Min(); a.Push(); a.Push(); cout<<a.Min(); return 0; }2、使用两个栈实现一个队列
分析:栈--->先进后出 队列--->先进先出
在进行操作时,我们可利用栈的特性,在进行push操作时,使用栈操作,而进行pop操作时将栈中元素逐一放入另一栈
源代码:
#include <iostream> using namespace std; #include <stack> #include <assert.h> template<class T> class Queue { public: Queue() {} void Push(const T& x) //尾插 { a.push(x); } void Pop() //头删 { assert(!a.empty()); while(!a.empty()) { b.push(a.top()); a.pop(); } b.pop(); while(!b.empty()) { a.push(b.top()); b.pop(); } } size_t Size() { return a.size(); } bool Empty() { return a.empty(); } T& front() //返回队头元素 -------找到再恢复 { assert(a.empty() != true); while (!a.empty()) { b.push(a.top()); a.pop(); } T tmp = b.top(); while (!b.empty()) { a.push(b.top()); b.pop(); } return tmp; } T& back() //返回队尾元素 { return a.top(); } ~Queue() {} private: stack<T> a,b; }; int main() { Queue<int> a; a.Push(1); a.Push(2); a.Push(3); cout<<a.front()<<endl; cout<<a.Size()<<endl; a.Push(4); a.Pop(); cout<<a.front()<<endl; return 0; }
3、两个队列实现栈
思路:栈---先进后出 队列---先进先出
创建两个队列,一个队列放数据,另一个当作辅助队列,当进行push(尾插)操作时直接用队列的push进行操作,而进行pop操作时,由于队列的pop是进行头删,不符合栈pop的条件,这时可将队列1中数据从第一个开始另一辅助队列2中,但是,第一个队列中最后一个元素不放入,然后,将第一个队列清空,再将辅助队列中元素按顺序放入第一个队列中即可。
源代码:
#include <iostream> using namespace std; #include <queue> #include <assert.h> // 两个队列实现一个栈 template <class T> class TStack { public: TStack() {} void Push(const T& x) //进栈---尾插 { s1.push(x); } void Pop() //出栈--- { assert(!s1.empty()); while (!s1.empty() && (s1.size()!=1)) //最后一个元素不进队列 { s2.push(s1.front()); s1.pop(); } s1.pop(); while (!s2.empty()) { s1.push(s2.front()); s2.pop(); } } bool Empty() { if (s1.empty()) { return true; } return false; } T& Top() { return s1.back(); } size_t Size() { return s1.size(); } private: queue<T> s1,s2; }; int main() { TStack<int> a; a.Push(1); a.Push(7); a.Push(5); a.Push(6); cout<<a.Top()<<endl; cout<<a.Size()<<endl; a.Pop(); cout<<a.Size()<<endl; cout<<a.Top()<<endl; return 0; }
4、一个数组实现两个栈
分析:
方案一:将数组的下标为0的位置当做第一个栈的栈底,下标为1的位置当做第二个栈的栈底,将数组的偶数位置看做第一个栈的存储空间,奇数位置看做第二个栈的存储空间。
方案二:从中间分别向两边压栈
将数组的中间位置看做两个栈的栈底,压栈时栈顶指针分别向两边移动,当任何一边到达数组的起始位置或是数组尾部,则开始扩容。
方案三:从两边向中间压栈
将数组的起始位置看作是第一个栈的栈底,将数组的尾部看作第二个栈的栈底,压栈时,栈顶指针分别向中间移动,直到两栈顶指针相遇,则扩容。
比较:方案二和方案一当两栈中元素不同时,比较浪费空间,方案三节省空间
方案3代码:
#include <iostream>
using namespace std;
#include <assert.h>
//一个数组实现两个栈
//从两边开始存
template<class T>
class Stack
{
public:
Stack()
:_arr(NULL)
,_size1(0)
,_size2(0)
,_capacity(0) //初始化容量为2
{}
void Push1(const T& x)
{
_checkcapacity();
_arr[_size1] = x;
_size1++;
}
void Pop1()
{
assert(Size1() != 0);
_size1--;
}
size_t Size1()
{
return _size1;
}
T& Top1()
{
return _arr[_size1-1];
}
void Push2(const T& x)
{
_checkcapacity();
size_t botton = _capacity-_size2-1;
_arr[botton] = x;
botton--;
_size2++;
}
void Pop2()
{
assert(Size2() != 0);
_size2--;
}
size_t Size2()
{
return _size2;
}
T& Top2()
{
assert(_size2 != 0);
return _arr[_capacity-1];
}
private:
void _checkcapacity()
{
if ((_size1+_size2)==_capacity || (_capacity==0)) //容量满 或 空
{
size_t _newcapacity = _capacity*2+2;
T* tmp = new T[_newcapacity];
for (size_t i=0; i<_size1; i++) //第一个栈放数组左边
{
tmp[i] = _arr[i];
}
size_t t = _capacity-1;
for(size_t j=_newcapacity-1; (j>(_newcapacity-_size1)) && (t>_size1-1); --t,j--)
{
tmp[j] = _arr[t];
}
_capacity = _newcapacity;
swap(_arr,tmp);
}
}
private:
T* _arr;
size_t _size1;
size_t _size2;
size_t _capacity;
};
int main()
{
Stack<int> a;
a.Push1(1);
cout<<a.Size1()<<endl;
a.Push1(8);
a.Pop1();
a.Push1(5);
a.Push1(3);
cout<<a.Size1()<<endl;
cout<<a.Top1()<<endl;
a.Push1(2);
a.Push2(1);
a.Push2(0);
a.Push2(4);
cout<<a.Size2()<<endl;
cout<<a.Top2()<<endl;
a.Push2(1);
a.Push2(1);
a.Push2(4);
a.Push2(10);
a.Pop1();
cout<<a.Size2()<<endl;
a.Pop1();
a.Pop2();
a.Pop2();
cout<<a.Size2()<<endl;
cout<<a.Top2()<<endl;
return 0;
}
相关文章推荐
- 栈和队列面试题总结
- 栈与队列相关面试题
- 栈和队列相关面试题(3)
- 堆栈和队列的数据结构和相关操作总结
- 《STL源码剖析》相关面试题总结
- 链表相关的面试题总结
- 个人总结javaWeb以及SSH等相关面试题
- 栈和队列相关面试题(1)
- Linux TCP队列相关参数的总结
- 消息队列相关函数总结
- 产品专员相关面试题总结
- Linux TCP队列相关参数的总结
- Linux TCP队列相关参数的总结
- 栈和队列相关面试题(2)
- 【数据结构】链表的原理及与其相关的常见面试题总结
- 子串子序列相关面试题总结
- 面试题二叉树相关问题总结
- 栈和队列 相关 面试题
- 前端面试总结---CSS相关面试题
- 栈和队列 相关 面试题