关于栈和队列的面试题
2018-03-28 23:15
155 查看
1.实现一个栈,要求实现Push,Pop ,Min(返回最小值)的时间复杂度为O(1)
方法一:
解题思路:首先我们在之前封装的栈结构外面再封装一层,将其封装为一个最小栈, 与之前不同的是,我们在栈中存放的是一个结构体,这个结构体中有两个成员, 一个是数据本身的值,另一个为栈内所有存放元素中的最小值,这样即可实现压栈,出栈,取最小值操作都为O(1)
代码如下:
上述方法存在缺陷,每次压栈需压入一个结构体两个数据,有些情况下,每次压入的最小值都是相同的,重复操作,因此我们采用另一种方式。
方法二:
思路:封装一个结构,其中包括两个栈,一个栈保存所有的数据,另一个栈保存最小值, 每次进来一个值都将其压入保存数据的栈中,若该数据比最小值栈的栈顶元素小,也将该值压入最小值栈中,否则不操作。出栈时,每次从数据栈中弹出一个元素,若该元素与最小值栈的栈顶元素相同时,最小值栈也要进行出栈操作,否则不出元素,这种方式 获取最小值更为简单,直接取最小值栈的栈顶元素,他们的时间复杂度都为O(1)。
代码如下:
注意:若连续向栈中压入相同的最小数据时,最小栈只能进行一次压栈,而出栈时,最小栈就会将该最小值弹出来,从而导致错误,因此要使用这种方式,我们需要对最小栈中的每个元素的个数进行计数,当其计数为0时再出栈。代码有待完善。
其中一号栈用来入队列,二号栈用来出队列
入队列:直接对一号栈进行压栈操作
出队列:对二号栈进行出栈操作,当二号栈中没有元素时,将一号栈的元素倒入二号栈中
取队头:获取二号栈的栈顶元素,当二号栈中没有元素时,将一号栈的元素倒入二号栈中
取队尾:获取一号栈的栈顶元素,当一号栈中没有元素时,将二号栈的元素重新倒到二号栈中
代码如下:
入栈:哪个队列不空就向哪个队列进行入队列操作
出栈:从不空的队列向另一空队列中倒元素,直至该队列中剩余一个元素,将该元素出队列
取栈顶:即获取非空队列的队尾元素
代码如下:
方法二:以数组下标为积数的空间作为一号栈的空间,偶数下标空间作为二号栈的空间 每次入栈,栈顶向后走两步
相比之下,方法一比方法二要好些,可以重复利用这段空间。
测试共享栈:
运行结果:
方法一:
解题思路:首先我们在之前封装的栈结构外面再封装一层,将其封装为一个最小栈, 与之前不同的是,我们在栈中存放的是一个结构体,这个结构体中有两个成员, 一个是数据本身的值,另一个为栈内所有存放元素中的最小值,这样即可实现压栈,出栈,取最小值操作都为O(1)
代码如下:
//用当前元素值和栈内最小元素值封装一个结构体 typedef struct Elem { int _data;//元素数值本身 int _minData;//栈中所有元素的最小值 }Elem; //最小栈 typedef struct MinStack { Stack _s; }MinStack; //初始化最小栈 void InitMinStack(MinStack *s) { StackInit(&s->_s); } //压栈 void MinStackPush(MinStack *s, int data) { assert(s); //构造元素 Elem cur; cur._data = data; cur._minData = data; //若栈为空,直接将该元素压栈 if (StackEmpty(&s->_s)) StackPuch(&s->_s, cur); //栈不为空时,用栈顶元素中的最小值与当前元素进行 4000 比较 //若栈顶元素中的最小值小于当前元素值,则用栈顶的最小值 //来更新当前元素中的最小值 else { Elem top = StackTop(&s->_s); if (cur._minData > top._minData) cur._minData = top._minData; StackPuch(&s->_s, cur); } } //出栈 void MinStackPop(MinStack *s) { assert(s); if (StackEmpty(&s->_s)) return; StackPop(&s->_s); } //取栈顶 int MinStackTop(MinStack *s) { Elem top; top = StackTop(&s->_s); return top._minData; }
上述方法存在缺陷,每次压栈需压入一个结构体两个数据,有些情况下,每次压入的最小值都是相同的,重复操作,因此我们采用另一种方式。
方法二:
思路:封装一个结构,其中包括两个栈,一个栈保存所有的数据,另一个栈保存最小值, 每次进来一个值都将其压入保存数据的栈中,若该数据比最小值栈的栈顶元素小,也将该值压入最小值栈中,否则不操作。出栈时,每次从数据栈中弹出一个元素,若该元素与最小值栈的栈顶元素相同时,最小值栈也要进行出栈操作,否则不出元素,这种方式 获取最小值更为简单,直接取最小值栈的栈顶元素,他们的时间复杂度都为O(1)。
代码如下:
//将这两个栈封装成一个结构体 typedef struct MinStack { Stack _data; Stack _minData; }MinStack; //初始化两个栈 void InitMinStack(MinStack *s) { assert(s); StackInit(&s->_data); StackInit(&s->_minData); } //压栈 void MinStackPush(MinStack *s, DataType data) { assert(s); StackPuch(&s->_data, data); if (StackEmpty(&s->_minData) || data < StackTop(&s->_minData)) { StackPuch(&s->_minData,data); } } //出栈 void MinStackPop(MinStack *s) { DataType top = StackTop(&s->_data); if ( top == StackTop(&s->_minData)) { StackPop(&s->_minData); } StackPop(&s->_data); } //获取栈顶 DataType MinStackTop(MinStack *s) { return StackTop(&s->_minData); }
注意:若连续向栈中压入相同的最小数据时,最小栈只能进行一次压栈,而出栈时,最小栈就会将该最小值弹出来,从而导致错误,因此要使用这种方式,我们需要对最小栈中的每个元素的个数进行计数,当其计数为0时再出栈。代码有待完善。
2.使用两个栈实现一个队列
解题思路:其中一号栈用来入队列,二号栈用来出队列
入队列:直接对一号栈进行压栈操作
出队列:对二号栈进行出栈操作,当二号栈中没有元素时,将一号栈的元素倒入二号栈中
取队头:获取二号栈的栈顶元素,当二号栈中没有元素时,将一号栈的元素倒入二号栈中
取队尾:获取一号栈的栈顶元素,当一号栈中没有元素时,将二号栈的元素重新倒到二号栈中
代码如下:
//将两个栈封装成一个队列 typedef struct QueueByStack { Stack s1; Stack s2; }QueueByStack; //初始化该队列 void QueueByStackInit(QueueByStack *q) { assert(q); StackInit(&q->s1); StackInit(&q->s2); } //入队列 void QueueByStackPush(QueueByStack *q,DataType data) { assert(q); StackPuch(&q->s1, data); } //队列判空 int QueueByStackEmpty(QueueByStack *q) { assert(q); return StackEmpty(&q->s1) && StackEmpty(&q->s2); } //出队列 void QueueByStackPop(QueueByStack *q) { assert(q); if (QueueByStackEmpty(q)) return; if (StackEmpty(&q->s2)) { //倒元素 while (!StackEmpty(&q->s1)) { StackPuch(&q->s2, StackTop(&q->s1)); StackPop(&q->s1); } } StackPop(&q->s2); } //获取队头 DataType QueueByStackFront(QueueByStack *q) { assert(q); if (StackEmpty(&q->s2)) { //倒元素 while (!StackEmpty(&q->s1)) { StackPuch(&q->s2, StackTop(&q->s1)); StackPop(&q->s1); } } return StackTop(&q->s2); } //获取队尾 DataType QueueByStackBack(QueueByStack *q) { assert(q); if (StackEmpty(&q->s1)) { while (!StackEmpty(&q->s2)) { StackPuch(&q->s1, StackTop(&q->s2)); StackPop(&q->s2); } } return StackTop(&q->s1); } //求队列中元素个数 int QueueByStackSize(QueueByStack *q) { assert(q); return StackSize(&q->s1) + StackSize(&q->s2); }
3.通过两个队列实现一个栈
解题思路:入栈:哪个队列不空就向哪个队列进行入队列操作
出栈:从不空的队列向另一空队列中倒元素,直至该队列中剩余一个元素,将该元素出队列
取栈顶:即获取非空队列的队尾元素
代码如下:
//将两个队列封装为一个栈 typedef struct StackByQueue { Queue q1; Queue q2; }StackByQueue; //初始化两队列 void StackByQueueInit(StackByQueue *s) { assert(s); InitQueue(&s->q1); InitQueue(&s->q2); } //入栈 void StackByQueuePush(StackByQueue *s,DataType data) { assert(s); if (!EmptyQueue(&s->q1)) QueuePush(&s->q1, data); else QueuePush(&s->q2,data); } //对栈判空 int StackByQueueEmpty(StackByQueue *s) { assert(s); return EmptyQueue(&s->q1) && EmptyQueue(&s->q2); } //出栈 void StackByQueuePop(StackByQueue *s) { assert(s); if (StackByQueueEmpty(s)) return; if (!EmptyQueue(&s->q1)) { while (Size(&s->q1) > 1) { QueuePush(&s->q2, Front(&s->q1)); QueuePop(&s->q1); } QueuePop(&s->q1); } else { while (Size(&s->q2) > 1) { QueuePush(&s->q1, Front(&s->q2)); QueuePop(&s->q2); } QueuePop(&s->q2); } } //取栈顶 DataType StackByQueueTop(StackByQueue *s) { assert(s); if (!EmptyQueue(&s->q1)) return back(&s->q1); else return back(&s->q2); } //获取栈元素个数 int StackByQueueSize(StackByQueue *s) { assert(s); return Size(&s->q1) + Size(&s->q2); }
4.判断元素出栈、入栈顺序的合法性。如入栈的序列(1,2,3,4,5),出栈序列为(4,5,3,2,1)
给出代码:int TestStackInAndOutValid(int *In, int InSize, int *Out, int OutSize) { Stack s; int InIdx = 0;//入栈序列索引 int OutIdx = 0;//出栈序列索引 //若入栈序列与出栈序列元素个数不相等,必定非法 直接返回0 if (InSize != OutSize) return 0; StackInit(&s); //出栈索引未越界即出栈序列中元素未走完时,循环一直继续 while (OutIdx < OutSize) { //若栈为空或者栈顶元素不等于出栈序列中指定索引位置的元素,一直进行入栈 while (StackEmpty(&s) || StackTop(&s) != Out[OutIdx]) { //当入栈索引未越界时再入栈,否则说明出栈序列非法 if (InIdx < InSize) StackPuch(&s, In[InIdx++]); //入栈后,入栈索引向后走一步 else return 0; } //此时栈顶元素等于出栈索引指定元素,进行chuzhan StackPop(&s); OutIdx++;//出栈索引向后走一步 } //此时出栈序列中元素已全部出栈,说明序列合法 return 1; }
5.一个数组实现两个栈(共享栈)
方法一:以数组的两端分别为两个栈的栈底,入栈时两个栈向中间靠拢方法二:以数组下标为积数的空间作为一号栈的空间,偶数下标空间作为二号栈的空间 每次入栈,栈顶向后走两步
相比之下,方法一比方法二要好些,可以重复利用这段空间。
#define MAX_SIZE 10 //在一个数组上搭建共享栈 typedef struct SharedStack { int _array[MAX_SIZE]; int _top1;//一号栈的栈顶 int _top2;//二号栈的栈顶 }SharedStack; //初始化共享栈 void SharedStackInit(SharedStack *s) { assert(s); s->_top1 = 0; s->_top2 = MAX_SIZE - 1; } //入栈 void SharedStackPush(SharedStack *s, DataType data,int which) { assert(s); assert(1 == which || 2 == which); if (s->_top1 > s->_top2)//共享栈已满 return; if (1 == which) s->_array[s->_top1++] = data;//向1号栈中入元素 else s->_array[s->_top2--] = data;//向2号栈中入元素 } //判断一号栈或二号栈是否为空 int SharedStackEmpty(SharedStack *s, int which) { assert(s); assert(1 == which || 2 == which); if (1 == which) return 0 == s->_top1; else return MAX_SIZE-1 == s->_top2; } //出栈 void SharedStackPop(SharedStack *s, int which) { assert(s); assert(1 == which || 2 == which); if (SharedStackEmpty(s, which)) return; if (1 == which) s->_top1--; else s->_top2++; } //获取一号栈或二号栈的栈顶 DataType SharedStackTop(SharedStack *s, int which) { assert(s); assert(1 == which || 2 == which); assert(!SharedStackEmpty(s, which)); if (1 == which) return s->_array[s->_top1 - 1]; else return s->_array[s->_top2 + 1]; } //获取一号栈或二号栈的元素个数 int SharedStackSize(SharedStack *s, int which) { assert(s); assert(1 == which || 2 == which); if (1 == which) return s->_top1; else return MAX_SIZE - s->_top2 - 1; }
测试共享栈:
void TestSharedStack() { SharedStack s; SharedStackInit(&s); SharedStackPush(&s, 1, 1); SharedStackPush(&s, 3, 1); SharedStackPush(&s, 5, 1); SharedStackPush(&s, 7, 1); SharedStackPush(&s, 9, 1); SharedStackPush(&s, 2, 2); SharedStackPush(&s, 4, 2); SharedStackPush(&s, 6, 2); SharedStackPush(&s, 8, 2); SharedStackPush(&s, 10, 2); printf("top1=%d\n", SharedStackTop(&s, 1)); printf("size1=%d\n", SharedStackSize(&s, 1)); printf("top2=%d\n", SharedStackTop(&s, 2)); printf("size1=%d\n", SharedStackSize(&s, 2)); SharedStackPop(&s, 1); SharedStackPop(&s, 2); printf("top1=%d\n", SharedStackTop(&s, 1)); printf("size1=%d\n", SharedStackSize(&s, 1)); printf("top2=%d\n", SharedStackTop(&s, 2)); printf("size1=%d\n", SharedStackSize(&s, 2)); }
运行结果:
相关文章推荐
- 关于队列和栈的几道面试题
- STL-关于栈和队列的面试题
- 关于一些面试题。
- 一道关于C++虚函数和多继承的面试题
- 关于队列链表栈的尾指针的情况对比
- 关于new关键字的面试题
- 一道关于JavaScript解析器错误的面试题
- 一道关于反射的面试题(通过反射修改父类私有属性)
- 关于一道简单的Java 基础面试题的剖析: short s1=1;s1 = s1 +1会报错吗?
- 面试题--用两个栈实现一个队列
- 面试题22:栈的压入、弹出队列
- 栈、队列基础面试题
- 数据结构面试题:两个队列实现一个堆栈
- 给老铁们一些前端面试题,关于HTML+CSS的
- 关于sizeof的面试题,回答很好地解释了sizeof的相关特性
- java面试题inti=2;i+=i-=i*i的值以及关于i++;++i;i=i+1;i+=1 的效率问题
- 关于DP的单调队列优化和斜率优化区别
- 关于消息队列的使用
- 关于队列的10个问题解答
- 关于消息队列的使用