前言
作者:小蜗牛向前冲
名言:我可以接收失败,但我不能接收放弃
如果觉的博主的文章还不错的话,还请
点赞,收藏,关注👀支持博主。如果发现有问题的地方欢迎❀大家在评论区指正。
为了提高自己编写代码的能力,特意开设刷题专题,记录自己刷题的思路和理解。欢迎❀大家的指正。
在下面的解题中我们都用C语言实现。
1用队列实现栈
在这里我们就是要将队列模拟实现成栈,我们知道队列是先进先出,栈是先进后出;那我们又如何实现呢?其实我们可以借助二个队列实现。下面我们借助图像来理解一下:
首先我们建立二个队列p1,p2,我们可能理解为入栈我们就将入数据到p1处。
其次,我们可以认为p2队列是用来中间存放数据的。
这样我们就成功将5出栈了,如果要将4出栈,那么我们又得需要将p2中的数据倒入到p1中:
这样我们就完成了4出栈。
下面我们简单总结一下思路:
1我们要建立二个队列经行模拟。
2始终保持一个队列为空,在出栈时,将将n-1个数据都倒入到空队列中,那么留在另个队列的数据就可以直接出栈了。
下面是完成的解题代码:
typedef int QDataType;
//定义队列
typedef struct QueueNode
{
QDataType data;//数据类型
struct QueueNode* next;
}QNode;
//定义指向头和尾的二个指针
typedef struct Queue
{
QNode* head;
QNode* tail;
int size;
}Queue;
//初始化
void QueueInit(Queue* pq);
//销毁
void QueueDestroy(Queue* pq);
//入队
void QueuePush(Queue* pq, QDataType x);
//出队
void QueuePop(Queue* pq);
//返回指向队头的数据的指针
QDataType QueueFront(Queue* pq);
//返回指向队尾的数据的指针
QDataType QueueBack(Queue* pq);
//判断队列是否为空
bool QueueEmpty(Queue* pq);
//返回队列的大小
int QueueSize(Queue* pq);
//初始化
void QueueInit(Queue* pq)
{
assert(pq);
pq->head = pq->tail = NULL;
pq->size = 0;
}
//销毁
void QueueDestroy(Queue* pq)
{
assert(pq);
QNode* cur = pq->head;
while (cur)
{
QNode* del = cur;
cur = cur->next;//指向下个节点
free(del);
}
pq->head = pq->tail = NULL;//防止出现野指针
pq->size = 0;
}
//入队
void QueuePush(Queue* pq, QDataType x)
{
assert(pq);
//申请节点
QNode* newNode = (QNode*)malloc(sizeof(QNode));
if (newNode==NULL)
{
perror("malloc fail");
exit(-1);
}
else
{
newNode->data = x;
newNode->next = NULL;
}
//队列为空
if (pq->tail == NULL)
{
pq->head = pq->tail = newNode;
}
//不为空
else
{
pq->tail->next = newNode;
pq->tail = newNode;//tail指针指向newNode
}
pq->size++;
}
//出队
void QueuePop(Queue* pq)
{
assert(pq);
//断言队列是否为空
assert(!QueueEmpty(pq));
//当队列中就一个数据时
if (pq->head->next == NULL)
{
free(pq->head);
pq->head = pq->tail = NULL;
}
else
{
QNode* del = pq->head;
pq->head = pq->head->next;//头变为下个节点
free(del);
del = NULL;
}
pq->size--;
}
//判断队列是否为空
bool QueueEmpty(Queue* pq)
{
assert(pq);
return pq->tail == NULL;
}
//返回指向队头的数据的指针
QDataType QueueFront(Queue* pq)
{
assert(pq);
//断言队列是否为空
assert(!QueueEmpty(pq));
return pq->head->data;
}
//返回指向队尾的数据的指针
QDataType QueueBack(Queue* pq)
{
assert(pq);
//断言队列是否为空
assert(!QueueEmpty(pq));
return pq->tail->data;
}
//返回队列的大小
int QueueSize(Queue* pq)
{
return pq->size;
}
typedef struct {
Queue p1;//入栈
Queue p2;//出栈
} MyStack;
MyStack* myStackCreate() {
MyStack* obj = (MyStack*)malloc(sizeof(MyStack));
//对栈初始化
QueueInit(&obj->p1);
QueueInit(&obj->p2);
return obj;
}
void myStackPush(MyStack* obj, int x) {
//当p1为空时就将数据倒入
if(!QueueEmpty(&obj->p1))
{
QueuePush(&obj->p1,x);
}
else
{
QueuePush(&obj->p2,x);
}
}
int myStackPop(MyStack* obj) {
Queue* empty = &obj->p1;//假设p1为空
Queue* noEmpty = &obj->p2;
//如果p1队列为非空
if(!QueueEmpty(&obj->p1))
{
empty = &obj->p2;
noEmpty = &obj->p1;
}
//将非空队列中的N-1的数据都倒入到空队列中,那么原队列还剩的1个数据就是要返回的栈顶数据
while(QueueSize(noEmpty)>1)
{
QueuePush(empty,QueueFront(noEmpty));//入栈
QueuePop(noEmpty);//出栈
}
int top = QueueFront(noEmpty);
QueuePop(noEmpty);//出栈
return top;
}
int myStackTop(MyStack* obj) {
if(!QueueEmpty(&obj->p1))
{
return QueueBack(&obj->p1);
}
else
{
return QueueBack(&obj->p2);
}
}
bool myStackEmpty(MyStack* obj) {
return QueueEmpty(&obj->p1) && QueueEmpty(&obj->p2);
}
void myStackFree(MyStack* obj) {
QueueDestroy(&obj->p1);
QueueDestroy(&obj->p2);
free(obj);
}
2 栈实现队列
要用二个栈实现队列,这是很好实现的。
为什么这么说呢?我们可以用栈PushST来模拟入队列,栈PopST模拟出队列。
下面我们顺着这个思路来,来图解一下 1,2,3,4的入队列和出队列的过程。
1数据入队列
2 要出队列时,当PopST为空的时候,就将PushST的数据倒入到PopST中,然后直接出就行。
完整代码:
typedef int STDataType;
//定义栈
typedef struct Stack
{
STDataType* arr;//数据类型
int pos;//数组下标
int capacity;//栈的容量
}ST;
//初始化
void StackInit(ST* ps);
//销毁
void StackDestroy(ST* ps);
//入栈
void StackPush(ST* ps, STDataType x);
//出栈
void StackPop(ST* ps);
显示返回栈顶数据
STDataType StackTop(ST* ps);
//判断栈是否为空
bool StackEmpty(ST* ps);
//返回栈的长度//初始化
void StackInit(ST* ps)
{
assert(ps);
ps->arr = NULL;//初始数组为空
ps->pos = ps->capacity = 0;//初始为0
}
//销毁
void StackDestroy(ST* ps)
{
assert(ps);
free(ps->arr);//arr是整个栈的地址
ps->arr = NULL;
ps->capacity = ps->pos = 0;
}
//入栈
void StackPush(ST* ps, STDataType x)
{
assert(ps);
//判断栈的空间是否满
if (ps->pos == ps->capacity)
{
//扩容
int newCapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;//扩2倍
STDataType* tmp = (STDataType*)realloc(ps->arr, newCapacity * sizeof(STDataType));
if (tmp == NULL)
{
perror("reamlloc fail");
exit(-1);
}
//跟新容量
ps->arr = tmp;
ps->capacity = newCapacity;
}
//入栈
ps->arr[ps->pos] = x;
ps->pos++;//下标++
}
//出栈
void StackPop(ST* ps)
{
assert(ps);
//断言栈是否为空
assert(!StackEmpty(ps));
--ps->pos;
}
//判断栈是否为空
bool StackEmpty(ST* ps)
{
assert(ps);
return ps->pos == 0;
}
//显示返回栈顶数据
STDataType StackTop(ST* ps)
{
assert(ps);
//断言栈是否为空
assert(!StackEmpty(ps));
return ps->arr[ps->pos - 1];//下标已经前移
}
//返回栈的长度
int StackSize(ST* ps)
{
assert(ps);
return ps->pos;
}
int StackSize(ST* ps);
typedef struct {
ST PushST;//入队列
ST PopST;//出队列
} MyQueue;
MyQueue* myQueueCreate() {
//为模拟的队列开辟空间
MyQueue* obj = (MyQueue*)malloc(sizeof(MyQueue));
//初始化
StackInit(&obj->PushST);
StackInit(&obj->PopST);
return obj;
}
void myQueuePush(MyQueue* obj, int x) {
StackPush(&obj->PushST, x);
}
void PushSTTOPopST(MyQueue* obj)
{
//判断PopST是否为空
if (StackEmpty(&obj->PopST))
{
while (!(StackEmpty(&obj->PushST)))
{
StackPush(&obj->PopST, StackTop(&obj->PushST));
StackPop(&obj->PushST);
}
}
}
int myQueuePop(MyQueue* obj) {
//将PuahST中的元素都倒入PopST中
PushSTTOPopST(obj);
int fornt = StackTop(&obj->PopST);
StackPop(&obj->PopST);
return fornt;
}
int myQueuePeek(MyQueue* obj) {
//将PuahST中的元素都倒入PopST中
PushSTTOPopST(obj);
return StackTop(&obj->PopST);
}
bool myQueueEmpty(MyQueue* obj) {
return StackEmpty(&obj->PushST) && StackEmpty(&obj->PopST);
}
void myQueueFree(MyQueue* obj) {
//销毁空间
StackDestroy(&obj->PushST);
StackDestroy(&obj->PushST);
free(obj);
}
3 设计循环队列
对于设计循环队列来说有二种思路:
1 用数组实现
2 用链表实现
对于这二种思路,我会首选思路1,为什么会这么说呢?
因为用链表实现循环队列,首先你要malloc多份空间,而且这样你还不好判断循环队列是否已满!
所以:我会选择用思路1来为大家解题。
此题有个重点:那就是如何判断队列是否以满。
因为在对于空和满来说,队列中指针fornt和back都是指向同一个位置。
那么我们这么去解决呢?
我们有二个思路:
1多开辟一块空间
2用size标记队列的长度,当size==k时就满
下面我们用思路1来解决:
代码转换:
bool myCircularQueueIsFull(MyCircularQueue* obj) {
return (obj->back + 1) % obj->n == obj->fornt;
}
完整代码:
typedef struct {
int* arr;
int fornt;
int back;
int n;
} MyCircularQueue;
MyCircularQueue* myCircularQueueCreate(int k) {
MyCircularQueue* obj = (MyCircularQueue*)malloc(sizeof(MyCircularQueue));
obj->arr = (int*)malloc(sizeof(int) * (k + 1));
obj->fornt = obj->back=0;
obj->n = k + 1;
return obj;
}
bool myCircularQueueIsEmpty(MyCircularQueue* obj) {
return obj->fornt == obj->back;
}
bool myCircularQueueIsFull(MyCircularQueue* obj) {
return (obj->back + 1) % obj->n == obj->fornt;
}
bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {
if (myCircularQueueIsFull(obj))
{
return false;
}
obj->arr[obj->back] = value;
obj->back++;
//如果back指针已经指向尾,需要重新指向头
obj->back %= obj->n;
return true;
}
bool myCircularQueueDeQueue(MyCircularQueue* obj) {
if (myCircularQueueIsEmpty(obj))
{
return false;
}
obj->fornt++;
//如果fornt指针已经指向尾,需要重新指向头
obj->fornt %= obj->n;
return true;
}
int myCircularQueueFront(MyCircularQueue* obj) {
if (myCircularQueueIsEmpty(obj))
{
return -1;
}
return obj->arr[obj->fornt];
}
int myCircularQueueRear(MyCircularQueue* obj) {
if (myCircularQueueIsEmpty(obj))
{
return -1;
}
else if(obj->back==0)
return obj->arr[obj->n-1];
else
return obj->arr[obj->back-1];
// return obj->arr[(obj->back -1 + obj->n) % obj->n];
}
void myCircularQueueFree(MyCircularQueue* obj) {
free(obj->arr);
free(obj);
}