您的位置:首页 > 职场人生

栈和队列的常见面试题

2017-05-04 14:06 232 查看
1、两个队列实现一个栈

两个队列实现一个栈

2、两个栈实现一个队列

【算法思想】

1>设计类

成员变量:给两个栈s1和s2来模拟实现一个队列

成员函数:入队Push()和出队Pop()

2>给两个指向栈对象s1、s2的指针input和output,分别用来入队和出队

3>按照先进先出的方式模拟入队和出队操作

Push:将input指向不空的栈,然后在input中入队

Pop:将input所指栈中的前n-1(n为栈中元素个数)的数据先转移到output所指的栈中,同时pop掉input中的前n-1个元素,最后pop掉input中的最后一个元素,即可实现先进先出。

【代码实现】

/*用两个栈实现一个队列*/
#pragma once
#include <iostream>
#include <stack>
using namespace std;

template <class T>
class Queue
{
public:
void Push(T data)//入队
{
stack<T>* input = NULL;//入队指针
if (!s1.empty())//如果s1不为空,入队指针就指向s1
input = &s1;
else           //如果s1为空,入队指针就指向s2
input = &s2;
input->push(data);//在入队指针所指的栈中push数据
}
void Pop()//出队
{
if (s1.empty() && s2.empty())//如果两个栈都为空,说明队列是空的,直接返回
{
cout<<"queue empty!"<<endl;
return;
}
stack<T>* input,*output;//定义入队指针和出队指针
if (s1.empty())//如果s1为空,就将入队指针指向s2,出队指针指向s1
{
input = &s2;
output = &s1;
}
else          //如果s1不为空,就将入队指针指向s1,出队指针指向s2
{
input = &s1;
output = &s2;
}
size_t size = input->size();//用临时变量size保存队列中的元素个数
for (size_t i = 0; i < size - 1; ++i)//将入队指针所指栈的前(size-1)个元素转入出队栈
{
output->push(input->top());
input->pop();//同时将转移过的数据删除
}
input->pop();//将最先进入队列的数据出队
swap(input,output);//交换入队和出队指针
}
protected:
stack<T> s1;
stack<T> s2;
};

void TestQueue()
{
Queue<int> q;
q.Push(1);
q.Push(4);
q.Push(3);
q.Push(5);

q.Pop();
q.Pop();
q.Pop();
q.Pop();
q.Pop();
}


3、实现一个栈,使其入栈push,出栈pop,获取栈的最小元素min的时间复杂度均为O(1)

【基本思想】

考虑到栈的入栈push和出栈pop操作的时间复杂度本来就是O(1),所以说明该题的主要考点是要完成对获取栈的最小元素min操作的时间复杂度的优化。

1>设计类

成员变量:给两个栈s和min,s用来进行正常的入栈出栈操作,min栈用来保存栈中的最小元素

成员函数:入队Push()、出队Pop()和获取最小元素Min()

2>模拟实现题目要求的栈

Push:在栈s中push数据,将栈min中的最小元素大于等于当前入栈元素,就将当前元素也push进min栈中

Pop:更新min栈中的最小元素,如果要删除的数据为最小数据,必须将min中的最小数据也删除,否则只需删除s栈的数据

Min:直接返回min栈的栈顶元素

【代码实现】

/*实现一个栈,要求实现Push(出栈)、Pop(入栈)、\
Min(返回最小值的操作)的时间复杂度为O(1)*/

#pragma once
#include <iostream>
#include <stack>
using namespace std;

template <class T>
class  StackMin
{
public:
void Push(T data)//入栈
{
s.push(data);//在栈s中入栈

//如果min为空或者min中的最小值>=data,就将data同时入栈到min
if (min.empty() || data <= min.top())
{
min.push(data);
}
}
void Pop()//出栈
{
if (s.empty())//如果保存数据的栈s为空,说明栈中没数据,直接返回
return;

//如果要删除的数据为最小数据,必须将min中的最小数据也删除,否则只需删除s栈的数据
if (s.top() == min.top())
min.pop();
s.pop();//s.top()>min.top
}
T Min()//获取栈中的最小值(时间复杂度:O(1))
{
return min.top();//min的栈顶元素即为最小值
}
protected:
stack<T> s;  //用来正常的出栈入栈
stack<T> min;//用来保存最小的数据
};

void TestStackMin()
{
StackMin<int> sm;
sm.Push(4);
sm.Push(3);
sm.Push(5);
sm.Push(1);
sm.Push(1);
sm.Push(2);

sm.Pop();
sm.Pop();
}


4、判断元素出栈入栈的合法性

对于一个栈,已知元素的入栈序列,判断一个由栈中所有元素组成的排列是否是可能的出栈序列。

比如,进栈序列为1 2 3 4,则可能的出栈序列有4 3 2 1,1 4 3 2等。而1 4 2 3就不是。

【算法思路】

给定一个栈s,按照入栈序列进行push数据,如果s的栈顶元素不等于出栈序列当前所指元素,就push入栈序列当前所指数据,同时让入栈序列向后走;如果s的栈顶元素等于出栈序列当前所指元素,就pop掉s栈中的栈顶元素,同时让出栈序列向后走。当入栈序列的所有元素都已入栈而出栈序列还没走到尾时说明入栈出栈序列不合法,反之则是合法的。

【代码实现】

/*元素出栈、入栈顺序的合法性。)*/
#pragma once
#include <iostream>
#include <stack>
#include <cassert>
using namespace std;

template <class T>
class Stack_Legal
{
public:
bool IsLegal(T* in,T* out,size_t insz,size_t outsz)
{
assert(insz == outsz);//制止所给的入栈序列和出栈序列的长度不一样

size_t inIndex = 0;//入栈序列的下标
size_t outIndex = 0;//出栈序列的下标

while(outIndex != outsz)//出栈序列的下标没走到尾,就说明还有元素没有匹配出栈
{
//当栈为空或栈顶元素与出栈序列的当前元素不匹配
if (s.empty() || s.top() != out[outIndex])
{
//如果所有元素都已入栈而出栈序列还没有走到尾,说明序列不匹配
if(inIndex == insz)
return false;
//如果入栈序列还没有走到尾,就将当前元素入栈
s.push(in[inIndex++]);
}
//如果栈顶元素和出栈序列的当前元素相同,就将出栈序列的下标向后走,并pop栈顶元素
else
{
++outIndex;
s.pop();
}
}

return true;
}

protected:
stack<T> s;
};

void TestStackLegal()
{
Stack_Legal<int> s;
int in1[] = {1,2,3,4,5};
int out1[] = {5,4,3,2,1};
size_t insz1 = sizeof(in1)/sizeof(in1[0]);
size_t outsz1 = sizeof(out1)/sizeof(out1[0]);
cout<<s.IsLegal(in1,out1,insz1,outsz1)<<endl;

int in2[] = {1,2,3,4,5};
int out2[] = {1,2,3,4,5};
size_t insz2 = sizeof(in2)/sizeof(in2[0]);
size_t outsz2 = sizeof(out2)/sizeof(out2[0]);
cout<<s.IsLegal(in2,out2,insz2,outsz2)<<endl;

int in3[] = {1,2,3,4,5};
int out3[] = {3,4,2,5,1};
size_t insz3 = sizeof(in3)/sizeof(in3[0]);
size_t outsz3 = sizeof(out3)/sizeof(out3[0]);
cout<<s.IsLegal(in3,out3,insz3,outsz3)<<endl;

int in4[] = {1,2,3,4,5};
int out4[] = {5,2,4,3,1};
size_t insz4 = sizeof(in4)/sizeof(in4[0]);
size_t outsz4  = sizeof(out4)/sizeof(out4[0]);
cout<<s.IsLegal(in4,out4,insz4,outsz4)<<endl;
}


5、用一个数组实现两个栈

算法一:

将数组的两头分别作为两个栈的栈顶指针,插入数据时向中间靠拢,指针相遇说明空间已满,则需对数组进行扩容

算法二:

将数组的中间两个数据位置分别作为两个栈的栈顶指针,插入数据时向两边走, 其中有一个指针走到边界说明空间已满,则需对数组进行扩容

(缺点:有极端情况,一个栈满了,另一个栈很空但仍需扩容,空间利用率低)

算法三:

将数组的奇偶位置分别作为两个栈的栈顶指针,插入数据时各自向前走, 其中有一个指针走到右边界说明空间已满,则需对数组进行扩容

(缺点:有极端情况,一个栈满了,另一个栈很空但仍需扩容,空间利用率低)

基于使内存的空间利用率尽可能的高一些,此处只对算法一进行实现。

【实现代码】

/*用一个数组实现两个栈*/
#pragma once
#include <iostream>
#include <stack>
#include <cassert>
using namespace std;

enum Choose
{
First,//对第一个栈进行操作
SECOND,//对第二个栈进行操作
};

//算法一:
/*将数组的两头分别作为两个栈的栈顶指针,插入数据时向中间靠拢,\
指针相遇说明空间已满,则需对数组进行扩容*/
template <class T>
class ArrayStack
{
public:
ArrayStack()
:_n(5)//为了方便测试,默认数组只给5个空间
,_a(new T[_n])
,_sTop1(0)
,_sTop2(_n-1)
{}
~ArrayStack()
{
if (_a)
{
delete []_a;
_a = NULL;
}
}

void Push1(Choose choose,T data)//入栈(参数choose:选择在哪一个栈中进行入栈)
{
assert(_a); //预防数组为空

if(_sTop1 > _sTop2)//当两个下标已经交错,说明数组空间已满,需要扩容
_Dilatation();//扩容函数

if (choose == First)//在第一个栈中push数据
_a[_sTop1++] = data;
else               //在第二个栈中push数据
_a[_sTop2--] = data;
}
void Pop1(Choose choose)
{
if (choose == First)//pop第一个栈中的元素
{
if (_sTop1 == 0)//栈为空
{
cout<<"stack1 empty!"<<endl;
return;
}
--_sTop1;//将栈顶下标向前移
}
else             //pop第二个栈中的元素
{
//size_t size2 = _a+n - _a +_sTop2;//保存栈2 的元素个数
if (_sTop2 == _n)//栈为空
{
cout<<"stack2 empty!"<<endl;
return;
}
++_sTop2;//将栈顶下标向后移
}
}
protected:
void _Dilatation()//扩容函数
{
size_t n = _n;//保存旧空间的元素个数
_n *= 2;          //每次以二倍的速度扩容
T* tmp = new T[_n];//开辟新空间

//将栈1中旧空间的内容拷贝到新空间中
for (size_t i = 0; i < _sTop1; ++i)
{
tmp[i] = _a[i];
}

size_t size2 = n - _sTop2;//保存栈2的元素个数
_sTop2 += n+1;//将栈2的栈顶指针相应的向后移动增加的字节数

//将栈2中旧空间的内容拷贝到新空间中
for (size_t i = _n-1; --size2; --i)
{
tmp[i] = _a[--n];//注意循环条件和赋值方式
}
delete []_a;//释放旧空间
swap(_a,tmp);//指向新空间
}
private:
size_t _n;
T* _a;
size_t _sTop1;//栈1的栈顶下标
size_t _sTop2;//栈2的栈顶下标
};

void TestArrayStack()
{
ArrayStack<int> as;
as.Push1(First,1);
as.Push1(SECOND,5);
as.Push1(First,2);
as.Push1(SECOND,6);
as.Push1(First,3);
as.Push1(First,4);

as.Pop1(First);
as.Pop1(First);
as.Pop1(First);
as.Pop1(First);
as.Pop1(First);
as.Pop1(SECOND);
as.Pop1(SECOND);
as.Pop1(SECOND);
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  算法 面试题