一、最小栈
最小栈其实和栈没有什么区别的,唯一的区别在于最小栈是可以在O(1)时间内得到当前的栈空间里,最小的值。解决这个问题提供两种方法:
Round1:
创建一个辅助栈,比较当前元素和辅助元素栈顶元素的大小 ,如果大于辅助栈栈顶元素,则不理会,反之则需要压入辅助栈。此时辅助栈的栈顶元素就是该栈的最小值。
代码实现如下(栈的操作在可参考前面写的博客–栈的实现):
void SqStackMin1(SqStack *sq1,SqStack *sq2,int flag,SqStackType value){
if(sq1 == NULL || sq2 == NULL){
return;
}
if(flag == 1){
if(sq1 -> size == 1){
SqStackPush(sq2,value);
}
//比较当前元素和辅助元素栈顶元素的大小
//如果大于辅助栈栈顶元素,则不理会
//反之则需要压入辅助栈
else{
SqStackType cur1 = SqStackTopValue(sq2);
if(value > cur1){
return;
}
else{
SqStackPush(sq2,value);
}
}
}
else if(flag == 0){
SqStackType cur2 = SqStackTopValue(sq2);
if(value > cur2){
return;
}else{
SqStackPop(sq2);
}
}
}
void SqStackPush1(SqStack *sq1, SqStack *sq2, SqStackType value){
if(sq1 == NULL || sq2 == NULL){
return;
}
if(sq1 -> size > sq1 -> capacity){
SqStackResize(sq1);
}
sq1 -> data[sq1 -> size] = value;
sq1 -> size++;
SqStackMin1(sq1,sq2,1,value);
}
void SqStackPop1(SqStack *sq1, SqStack *sq2){
if(sq1 == NULL || sq2 == NULL){
return;
}
if(sq1 -> size == 0){
return;
}
SqStackType tmp = SqStackTopValue(sq1);
SqStackMin1(sq1,sq2,0,tmp);
sq1 -> size--;
}
Round2:
既然我们需要在O(1)时间内得到当前的栈空间里,最小的值,我们可以把最小的值放在栈顶即可实现要求,但是这样就破坏了入栈顺序,那如何实现?
我们可以每次入栈的时候入两次,第一次入栈是需要入栈的值,第二次入栈的元素和上次(本两次入栈的前一次)的元素进行比较,如果比其大,则第二次入栈的元素为上一次两次入栈第二次的元素,如果小,第二次入栈的元素就是第一次入栈的元素的值。出栈的时候就需要每次出栈两次。说的有些绕,可以看看代码来理解。
代码实现如下:
void SqStackPush2(SqStack *sq,SqStackType value){
if(sq == NULL){
return;
}
if(sq -> size == 0){
sq -> data[sq -> size] = value;
sq -> size++;
sq -> data[sq -> size] = value;
sq -> size++;
}
SqStackType tmp = SqStackTopValue(sq);
if(value > tmp){
SqStackPush(sq,value);
SqStackPush(sq,tmp);
}else{
SqStackPush(sq,value);
SqStackPush(sq,value);
}
}
void SqStackPop2(SqStack *sq){
if(sq == NULL){
return;
}
if(sq -> size == 0){
return;
}
SqStackPop(sq);
SqStackPop(sq);
}
SqStackType SqStackGetTop(SqStack* sq){
//栈顶元素是最小值
SqStackType min = SqStackTopValue(sq);
return min;
}
二、两个栈实现一个队列
栈和队列的根本区别就是,栈是后进先出,队列是先进先出。因此用两个栈实现一个队列,就是需要用两个栈来实现先进先出这种情况,那如何实现呢?
首先我们定义两个栈input和output,实现入队操作是非常简单的,只要入input栈即可,当实现出队列的时候,我们首先将input内的元素一次出栈,然后依次入栈output,然后再让output出栈,这样就实现了队列的出队列操作。
具体代码实现如下:
typedef SqStackType QueueType;
typedef struct Queue{
SqStack input;
SqStack output;
}Queue;
void QueueInit(Queue* q){
if(q == NULL){
return;
}
InitSqStack(&q -> input);
InitSqStack(&q -> output);
}
void QueuePush(Queue* q, QueueType value){
if(q == NULL){
return;
}
QueueType tmp;
while(SqStackTop(&q -> output, &tmp)){
SqStackPop(&q -> input);
SqStackPush(&q -> output, tmp);
}
SqStackPush(&q -> input, value);
}
void QueuePop(Queue* q){
if(q == NULL){
return;
}
QueueType tmp;
while(SqStackTop(&q -> input, &tmp)){
SqStackPop(&q -> output);
SqStackPush(&q -> input, tmp);
}
SqStackPop(&q -> output);
}
int QueueFront(Queue* q, QueueType* value){
if(q == NULL || value == NULL){
return 0;
}
QueueType tmp;
while(SqStackTop(&q -> input, &tmp)){
SqStackPop(&q -> input);
SqStackPush(&q -> output, tmp);
}
int ret = SqStackTop(&q -> output, value);
return ret;
}
////////////////////////////////////
///////////TEST//////////////////
//////////////////////////////////
void Test(){
Queue q;
QueueInit(&q);
QueuePush(&q,'a');
QueuePush(&q,'b');
QueuePush(&q,'c');
QueuePush(&q,'d');
QueueType tmp;
QueueFront(&q,&tmp);
printf("tmp excepted a,actual %c\n",tmp);
QueuePop(&q);
QueueFront(&q,&tmp);
printf("tmp excepted b,actual %c\n",tmp);
QueuePop(&q);
QueueFront(&q,&tmp);
printf("tmp excepted c,actual %c\n",tmp);
QueuePop(&q);
QueueFront(&q,&tmp);
printf("tmp excepted d,actual %c\n",tmp);
QueuePop(&q);
return;
}
int main(){
Test();
return 0;
}
三、两个队列实现一个栈
用两个队列实现一个栈,跟用两个栈实现一个队列思路大体相同,只有出栈方面稍有差别,要使队列的先进先出变为栈的后进先出,我们先定义两个队列queue1,queue2,先将元素入栈到queueu1中,出栈时,除queue1的第一个元素外,其余元素移入到queue2中,然后出栈queue1中的那个元素,再次出栈的时候就使queue1和queue2相互颠倒操作即可,这样就实现了栈后进先出的特点了。
具体代码实现如下(队列操作详见博客–队列的实现)
#include <stdio.h>
#include "SqQueue.h"
typedef SqQueueType StackType;
typedef struct Stack{
SqQueue queue1;
SqQueue queue2;
}Stack;
void StackInit(Stack* stack){
if(stack == NULL){
return;
}
SqQueueInit(&stack -> queue1);
SqQueueInit(&stack -> queue2);
}
void StackPush(Stack* stack,StackType value){
if(stack == NULL){
return;
}
SqQueue* entry = SqQueueSize(&stack -> queue1) > 0 ? &stack -> queue1 : &stack -> queue2;
SqQueuePush(entry, value);
}
void StackPop(Stack* stack){
if(stack == NULL){
return;//非法输入
}
size_t size1 = SqQueueSize(&stack -> queue1);
size_t size2 = SqQueueSize(&stack -> queue2);
if(size1 == 0 && size2 == 0){
return;
}
SqQueue* exit = size1 > 0 ? &stack -> queue1 : &stack -> queue2;
SqQueue* backup = size1 == 0 ? &stack -> queue1 : &stack -> queue2;
while(SqQueueSize(exit) > 1){
StackType tmp;
SqQueueFront(exit, &tmp);
SqQueuePop(exit);
SqQueuePush(backup, tmp);
}int StackTop(Stack* stack, StackType* value){
if(stack == NULL || value == NULL){
return 0;
}
size_t size1 = SqQueueSize(&stack -> queue1);
size_t size2 = SqQueueSize(&stack -> queue2);
if(size1 == 0 && size2 == 0){
return 0;
}
SqQueue* non_empty = size1 > 0 ? &stack -> queue1 : &stack -> queue2;
SqQueue* empty = size1 == 0 ? &stack -> queue1 : &stack -> queue2;
StackType tmp;
while(SqQueueSize(non_empty) > 0){
SqQueueFront(non_empty, &tmp);
SqQueuePop(non_empty);
SqQueuePush(empty, tmp);
}
*value = tmp;
return 1;
}
op(exit);
}
/
/TEST///
/
void Test(){
Stack stack;
StackInit(&stack);
StackPush(&stack, 'a');
StackPush(&stack, 'b');
StackPush(&stack, 'c');
StackPush(&stack, 'd');
StackType tmp;
StackTop(&stack, &tmp);
printf("tmp expected d, actual %c\n", tmp);
StackPop(&stack);
StackTop(&stack, &tmp);
printf("tmp expected c, actual %c\n", tmp);
StackPop(&stack);
StackTop(&stack, &tmp);
printf("tmp expected b, actual %c\n", tmp);
StackPop(&stack);
StackTop(&stack, &tmp);
printf("tmp expected a, actual %c\n", tmp);
StackPop(&stack);
}
int main(){
Test();
return 0;
}
四、判断字符串是否按照出栈顺序
首先来了解下题目的大致意思
给定一个字符串,如:a b d e c
一个栈的入栈顺序为:a b c d e
那么出栈顺序显而易见:e d c b a
题目意思为:字符串 a b d e c 是否能按出栈顺序所打出。比如说,我们先入栈a元素,接着再将a元素出栈,则得到a元素,接着再入b元素,同理出栈,再入c元素,发现与字符串d元素并不一致,那么继续入栈,入d元素,发现与字符串元素一致,继续出栈,依次类推,最终得到我们的字符串abdef,不过整个过程中,我们只执行了入栈出栈操作,并没有改变其入栈出栈的顺序。这就是所要考的内容,给定任意入栈顺序,任意字符串,判断字符串是否能够根据入栈出栈操作得到即可。
这里我们的思路大致是,每次入栈后取栈顶元素与字符串比较,如果一致则出栈栈顶元素,接着比较下一个栈顶是否与字符串第二个元素一致,如果不一致,继续入栈,以此类推,直至入栈结束,或者是字符串到最后一个元素,即可判断。
代码实现如下:
#include <stdio.h>
#include "SqStack.h"
int Str_Stack(SqStack* sq,char* str1, char* str2, int len){
if(str1 == NULL || str2 == NULL){
return 0;
}
SqStackType value;
int i = 0;
for(; i < len; i++){
SqStackPush(sq, *str1);
SqStackTop(sq, &value);
while(value == *str2){
SqStackPop(sq);
SqStackTop(sq, &value);
str2++;
}
if(*str2 == '\0'){
return 1;
}
str1++;
}
return 0;
}
int main()
{
SqStack sq;
InitSqStack(&sq);
int ret;
char str1[5] = {'a', 'b', 'c', 'd', 'e'};
char str2[5] = {'a', 'c', 'd', 'e', 'b'};
char str3[5] = {'a', 'd', 'b', 'e', 'c'};
int len = sizeof(str1)/sizeof(str1[0]);
ret = Str_Stack(&sq, str1, str2, len);
printf("expected ret 1,actual ret %d\n",ret);
ret = Str_Stack(&sq, str1, str3, len);
printf("expected ret 0,actual ret %d\n",ret);
return 0;
}
五、共享栈(一个数组实现两个栈)
如图,我们可以定义两个指针分别指向数组的头和尾,然后分别作为两个栈的开始,两个栈每次入栈时,head栈从前往后增长,即head++,tail栈从后往前增长,即tail- -,当head >= tail时,共享栈就满了。
代码实现如下:
//sharedstack.h
#pragma once
#include <stdio.h>
#include <stdlib.h>
#define MAXSIZE 10
typedef char StackType;
typedef struct Stack{
StackType data[MAXSIZE];
size_t head;
size_t tail;
}Stack;
void SharedStackInit(Stack* s);
void SharedStackPush1(Stack* s, StackType value);
void SharedStackPush2(Stack* s, StackType value);
void SharedStackPop1(Stack* s);
void SharedStackPop2(Stack* s);
int SharedStackTop1(Stack* s, StackType* vlaue);
int SharedStackTop2(Stack* s, StackType* vlaue);
void ShareStackDestroy(Stack* s);
void ShareStackDestroy(Stack* s);
//sharedstack.c
#include <stdio.h>
#include "sharedstack.h"
#include "SqStack.h"
void SharedStackInit(Stack* s){
if(s == NULL){
return;//非法输入
}
s -> head = 0;
s -> tail = MAXSIZE;
}
void SharedStackPush1(Stack* s, StackType value){
if(s == NULL){
return;//非法输入
}
if(s -> head >= s -> tail){
return;//共享栈满
}
s -> data[s -> head++] = value;
}
void SharedStackPush2(Stack* s, StackType value){
if(s == NULL){
return;//非法输入
}
if(s -> head >= s -> tail){
return;//共享栈满
}
s -> data[--s -> tail] = value;
}
void SharedStackPop1(Stack* s){
if(s == NULL){
return;//非法输入
}
if(s -> head == 0){
return;//空栈
}
--s -> head;
return;
}
void SharedStackPop2(Stack* s){
if(s == NULL){
return;//非法输入
}
if(s -> tail == MAXSIZE){
return;//空栈
}
++s -> tail;
return;
}
int SharedStackTop1(Stack* s, StackType* value){
if(s == NULL || value == NULL){
return 0;//非法输入
}
if(s -> head == 0){
return 0;//栈空
}
*value = s -> data[s -> head - 1];
return 1;
}
int SharedStackTop2(Stack* s, StackType* value){
if(s == NULL || value == NULL){
return 0;//非法输入
}
if(s -> tail == MAXSIZE){
return 0;//栈空
}
*value = s -> data[s -> tail];
return 1;
}
///////////////////////////////////////////
///////////////////TEST///////////////////
//////////////////////////////////////////
#if 1
#define TESTHEAD printf("================%s==============\n", __FUNCTION__)
void PrintChar(Stack* s,const char* msg){
printf("############%s##############\n", msg);
if(s == NULL){
return;//非法输入
}
printf("头部栈\n");
size_t i = 0;
for(; i < s -> head; ++i){
printf("%c\n", s -> data[i]);
}
printf("尾部栈\n");
size_t j = MAXSIZE;
for(; j > s -> tail; --j){
printf("%c\n", s -> data[j - 1]);
}
}
void TestInit(){
TESTHEAD;
Stack s;
SharedStackInit(&s);
PrintChar(&s, "初始化栈");
printf("head expect 0,actual %lu\n", s.head);
printf("tail expect 10, actual %lu\n", s.tail);
}
void Test(){
TESTHEAD;
Stack s;
SharedStackInit(&s);
SharedStackPush1(&s,'a');
SharedStackPush1(&s,'b');
SharedStackPush1(&s,'c');
SharedStackPush1(&s,'d');
SharedStackPush2(&s,'e');
SharedStackPush2(&s,'f');
SharedStackPush2(&s,'g');
SharedStackPush2(&s,'h');
SharedStackPush2(&s,'i');
PrintChar(&s, "进栈");
SharedStackPop1(&s);
SharedStackPop2(&s);
PrintChar(&s, "出栈");
StackType tmp1 ;
StackType tmp2;
int ret1 = SharedStackTop1(&s, &tmp1);
int ret2 = SharedStackTop2(&s, &tmp2);
printf("ret1 expect 1,actual %d\n", ret1);
printf("tmp1 expect c, actaual %c\n", tmp1);
printf("ret2 expect 1,actual %d\n", ret2);
printf("tmp2 expect h, actaual %c\n", tmp2);
}
int main(){
TestInit();
Test();
}