栈与队列刷题(循环队列,队列实现栈,栈实现队列)

在了解了栈和队列的概念与源代码以后,我们可以做几道题练练手

队列


在这里插入图片描述

1.用栈实现队列

我们都知道,栈是后进先出,队列是先进先出,那么怎么用栈来实现队列呢?
在这里插入图片描述
这是一个栈,栈顶元素是5,栈底元素是1,如果出栈肯定是5先出去了,我们想让1先出去,那就可以借助两个栈,我们先把12345按照54321的顺序出栈,出栈的元素不是没了,而是进入到了另一个栈中:
在这里插入图片描述

那么按照队列的性质先进先出,1就可以出去了,正好也符合pop栈中后进先出的性质。
所以我们可以使用两个栈来实现一个队列(一个出栈,一个入栈),当用来出队的栈没有元素时,就把入队的栈里的元素放到出栈中,根据栈的性质,会反过来,就达到了我们的目的。
代码:

typedef int STDataType;
typedef struct Stack
{
	STDataType* array;
	int capacity;		
	int size;  // 容量 
}Stack;
// 初始化栈 
void StackInit(Stack* ps); 
// 入栈 
void StackPush(Stack* ps, STDataType data); 
// 出栈 
void StackPop(Stack* ps); 
// 获取栈顶元素 
STDataType StackTop(Stack* ps); 
// 获取栈中有效元素个数 
int StackSize(Stack* ps); 
// 检测栈是否为空,如果为空返回非零结果,如果不为空返回0 
int StackEmpty(Stack* ps); 
// 销毁栈 
void StackDestory(Stack* ps); 
#define DEFSTACKSIZE 100
 
void CheckCapacity(Stack* ps)
{
	if (ps->size >= ps->capacity)
	{
		ps->capacity *= 2;
		ps->array = (STDataType *)realloc(ps->array, ps->capacity * sizeof(STDataType));
	}
}
 
void StackInit(Stack* ps)
{
	ps->array = (STDataType *)calloc(DEFSTACKSIZE, sizeof(STDataType));
	ps->capacity = DEFSTACKSIZE;
	ps->size = 0;
}
 
void StackPush(Stack* ps, STDataType x)
{
	CheckCapacity(ps);
 
	ps->array[ps->size] = x;
	ps->size++;
}
 
void StackPop(Stack* ps)
{
	if (ps->size == 0)
	{
		return;
	}
	ps->size--;
}
 
STDataType StackTop(Stack* ps)
{
	if (ps->size == 0)
	{
		return (STDataType)0;
	}
	return ps->array[ps->size - 1];
}
 
int StackEmpty(Stack* ps)
{
	return ps->size == 0;
}
 
int StackSize(Stack* ps)
{
	return ps->size;
}
 
void StackDestory(Stack* ps)
{
	if (ps->array)
	{ 
		free(ps->array);
		ps->array = NULL;
		ps->size = 0;
		ps->capacity = 0;
	}
}
//以上为栈
typedef struct 
{
    Stack pushST;
    Stack popST;
} MyQueue;


MyQueue* myQueueCreate() 
{
    MyQueue* nQueue = (MyQueue*)malloc(sizeof(MyQueue));
    StackInit(&nQueue->pushST);
    StackInit(&nQueue->popST);
    return nQueue;
}

void myQueuePush(MyQueue* obj, int x) 
{
    StackPush(&obj->pushST,x);

}

int myQueuePop(MyQueue* obj) {
        if(StackEmpty(&obj->popST)!=0)
        {
            while(StackEmpty(&obj->pushST)==0)
            {
                StackPush(&obj->popST,StackTop(&obj->pushST));
                StackPop(&obj->pushST);
            }
        }
        int f = StackTop(&obj->popST);
        StackPop(&obj->popST);
        return f;
}

int myQueuePeek(MyQueue* obj) 
{
   if(StackEmpty(&obj->popST)!=0)
        {
            while(StackEmpty(&obj->pushST)==0)
            {
                StackPush(&obj->popST,StackTop(&obj->pushST));
                StackPop(&obj->pushST);
            }
        }
        return StackTop(&obj->popST);
}

bool myQueueEmpty(MyQueue* obj) 
{
    return StackEmpty(&obj->pushST)&&StackEmpty(&obj->popST);
}

void myQueueFree(MyQueue* obj) {
        StackDestory(&obj->popST);
        StackDestory(&obj->pushST);
        free(obj);
        
}

别看有点多,其实是前面有栈的实现,所以占了几十行。

2.用队列实现栈

用队列实现栈也是差不多的思路,用两个队列实现一个栈。
在这里插入图片描述

怎么让5先出去呢?队列是先入先出的,你转移到另一个队列也还是1 2 3 4 5的顺序,那么这回我们就让队列中只剩五,再出队列,不就可以先让5出去了?
在这里插入图片描述
然后再让1出栈,实现了出栈顶。
在这里插入图片描述
注意,一定要保证一个空的队列用于储存其他的元素。
代码:

typedef int QDataType;
typedef struct QlistNode
{
    struct QlistNode* _next;
    QDataType _data;
}QNode;

typedef struct Queue
{
    QNode* _front;//队列头
    QNode* _rear;//队列尾
}Queue;
QNode* BuyQueueNode(QDataType x)
{
    QNode* cur = (QNode*)malloc(sizeof(QNode));
    cur->_data = x;
    cur->_next = NULL;
    return cur;
}
void QueueInit(Queue* q)
{
    q->_front = NULL;
    q->_rear = NULL;
}
void QueuePush(Queue* q, QDataType data)
{
    QNode* cur = BuyQueueNode(data);
    if (q->_front == NULL)
    {
        q->_front = q->_rear = cur;
    }
    else//有节点就在队列最后尾插,让rear后移一位 
    {
        q->_rear->_next = cur;
        q->_rear = cur;
    }
}
void QueuePop(Queue* q)
{
    assert(q);//出队列就相当于链表的头删一样
    if (q->_front == NULL)
        return;
    QNode* cur = q->_front->_next;
    free(q->_front);
    q->_front = cur;
}

int QueueEmpty(Queue* q)
{
    if (q->_front == NULL)
        return 1;
    else return 0;
}

void QueueDestroy(Queue* q)
{
    if (q->_front == NULL)
        return;
    while (q->_front)
    {
        QueuePop(q);
    }
}

int QueueSize(Queue* q)
{
    assert(q);
    QNode* cur = q->_front;
    int count = 0;
    while (cur)
    {
        count++;
        cur = cur->_next;
    }
    return count;

}
QDataType QueueFront(Queue* q)
{
    assert(q);
    assert(!QueueEmpty(q));
    return q->_front->_data;

}

QDataType QueueBack(Queue* q)
{
    assert(q);
    assert(!QueueEmpty(q));
    return q->_rear->_data;
}
//上方为队列(C语言无队列封装)
//
//
//
//
typedef struct {
    Queue q1;//用两个队列实现栈
    Queue q2;
}MyStack;


MyStack* myStackCreate()
{
    MyStack* pst = (MyStack*)malloc(sizeof(MyStack));
    QueueInit(&pst->q1);
    QueueInit(&pst->q2);

    return pst;
}

void myStackPush(MyStack* obj, int x)
{
    //给非空队列入队
    if (QueueEmpty(&obj->q1) == 1 && QueueEmpty(&obj->q2) == 1)//如果队列确实是空的就入队
    {
        QueuePush(&obj->q1, x);
    }
    else if (QueueEmpty(&obj->q2) != 1)
    {
        QueuePush(&obj->q2, x);
    }
    else
    {
        QueuePush(&obj->q1, x);
    }
}

int myStackPop(MyStack* obj)
{
    //把非空队列的除最后一个元素之前的元素全都移动到另一个队列
    Queue* pEmpty = &obj->q1, * pNonEmpty = &obj->q2;
    if (QueueEmpty(&obj->q1) != 1)
    {
        pEmpty = &obj->q2;
        pNonEmpty = &obj->q1;//判断空队列
    }
    while (QueueSize(pNonEmpty) > 1)
    {
        QueuePush(pEmpty, QueueFront(pNonEmpty));
        QueuePop(pNonEmpty);//把非空队列其余元素移动到空队列
    }
    int top = QueueFront(pNonEmpty);
    QueuePop(pNonEmpty);
    return top;
}

int myStackTop(MyStack* obj) {
    //栈顶就是非空队列的队尾
    if (QueueEmpty(&obj->q1) != 1)
    {
        return QueueBack(&obj->q1);
    }
    else
    {
        return QueueBack(&obj->q2);
    }

}

bool myStackEmpty(MyStack* obj)
{
    return QueueEmpty(&obj->q1) && QueueEmpty(&obj->q2);

}

void myStackFree(MyStack* obj)
{
    QueueDestroy(&obj->q1);
    QueueDestroy(&obj->q2);
    free(obj);
}

3.循环队列

在这里插入图片描述
假如一个队列有40字节,那么我们储存10个数据就顶天了,这时候我们出队一个,还是不能入队了,因为队列已经满了,即使是没用的元素塞满了,你也不能再进去了。
我们采用数组的方式来实现这个循环队列:
在这里插入图片描述
这时候要是一般的队列,你就不能往下存了,可是我们是循环队列,我们要再存数据,就把tail往前移动就可以了。
在这里插入图片描述
继续存,什么时候存满呢?
在这里插入图片描述
当tail == head的时候就存满了,但是这和队列为空的条件冲突了,因此,我们要区分空和满,可以给他增加一个空间
在这里插入图片描述
这样,tail和head就会始终保持1距离,只有为空的时候才相等,满的时候,tail+1 == head。
具体还有一些细节需要说,直接先上代码:
代码:

typedef struct 
{
    int *a;
    int front;
    int tail;
    int k;
} MyCircularQueue;
bool myCircularQueueIsFull(MyCircularQueue* obj);
bool myCircularQueueIsEmpty(MyCircularQueue* obj);
MyCircularQueue* myCircularQueueCreate(int k) 
{
    MyCircularQueue * nq = (MyCircularQueue*)malloc(sizeof(MyCircularQueue));
    nq->a = malloc(sizeof(int)*(k+1));
    nq-> tail = 0;
    nq-> front = 0;
    nq-> k  = k;
    return nq;
}

bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) 
{
    //插入一个元素
    if(myCircularQueueIsFull(obj))
    {
        return false;   
    }
    else
    {
        obj->a[obj->tail] = value;
        obj->tail++;
        obj->tail %= (obj->k+1);
        return true;
    }
  
  
}

bool myCircularQueueDeQueue(MyCircularQueue* obj) 
{
        if(myCircularQueueIsEmpty(obj))
        return false;
        else
        {
            ++obj->front;
            obj->front %= (obj->k+1);
            return true;
        }
}

int myCircularQueueFront(MyCircularQueue* obj) 
{
    if(myCircularQueueIsEmpty(obj))
    return -1;
        return obj->a[obj->front];
}

int myCircularQueueRear(MyCircularQueue* obj) 
{
    if(myCircularQueueIsEmpty(obj))
    return -1;
    /*if(obj->tail==0)
    return obj->a[k];
    else
    return obj->a[obj->tail-1];
    */
    int i = (obj->tail+obj->k)%(obj->k+1);
    return obj->a[i];
}

bool myCircularQueueIsEmpty(MyCircularQueue* obj) 
{
        return obj->front == obj->tail;
}

bool myCircularQueueIsFull(MyCircularQueue* obj) 
{
        return obj->front == (obj->tail+1)%(obj->k+1);//为了防止tail转一圈回到前面,和front不一样
}

void myCircularQueueFree(MyCircularQueue* obj) {
        free(obj->a);
        free(obj);
}

k就是容量,我们每次插入让tail指针加1的同时还会让他 obj->tail %= (obj->k+1);这是为了防止特殊情况,假如tail到了数组的0位置,他的值不一定是0,可能是0+n*(k+1),所以我们采用取模的方法,让tail下标的范围一直在
数组的范围当中。同理,front指针也进行相应的操作

小结

这三道习题是帮助大家加深理解,有多种实现方法,尤其是循环队列(链表实现),建议大家敲一敲,锻炼一下。

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值