引言
在数据结构的学习中,栈(Stack)和队列(Queue)是两个非常重要的概念。它们分别遵循着后进先出(LIFO)和先进先出(FIFO)的原则。在某些情况下,我们可能需要通过栈来模拟队列,或者通过队列来模拟栈的行为。本文将详细介绍这两种数据结构,并提供相应的C语言实现代码和图解。
一、栈(Stack)
栈是一种后进先出(LIFO)的数据结构,只允许在一端进行操作,这一端被称为栈顶(top)。栈的基本操作包括入栈(push)和出栈(pop)。
C语言实现栈:
// 支持动态增长的栈
typedef int STDataType;
typedef struct Stack
{STDataType* _a;int _top; // 栈顶int _capacity; // 容量
}Stack;// 初始化栈
void StackInit(Stack* ps)
{assert(ps);ps->_a = NULL;//top指向栈顶数据的下一个位置ps->_top = 0;//top指向栈顶数据//ps->_top = -1;ps->_capacity = 0;
}
// 入栈
void StackPush(Stack* ps, STDataType data)
{assert(ps);if (ps->_top == ps->_capacity){int newcapacity = ps->_capacity == 0 ? 4 : ps->_capacity * 2;STDataType* tmp = (STDataType*)realloc(ps->_a, newcapacity * (sizeof(STDataType)));if (tmp == NULL){perror("realloc fail");return;}ps->_a = tmp;ps->_capacity = newcapacity;}ps->_a[ps->_top] = data;ps->_top++;
}
// 出栈
void StackPop(Stack* ps)
{assert(ps);assert(ps->_top > 0);ps->_top--;
}
// 获取栈顶元素
STDataType StackTop(Stack* ps)
{assert(ps);assert(ps->_top > 0);return ps->_a[ps->_top - 1];
}
// 获取栈中有效元素个数
int StackSize(Stack* ps)
{assert(ps);return ps->_top;
}
// 检测栈是否为空,如果为空返回非零结果,如果不为空返回0
int StackEmpty(Stack* ps)
{assert(ps);assert(ps);return ps->_top == 0;
}
// 销毁栈
void StackDestroy(Stack* ps)
{assert(ps);free(ps->_a);ps->_a = NULL;ps->_top = ps->_capacity = 0;
}
栈的图解:
(可以想象一个竖直的容器,新元素从顶部加入,也是从顶部取出。)
二、队列(Queue)
队列是一种先进先出(FIFO)的数据结构,只允许在两端进行操作,一端为队头(front),另一端为队尾(rear)。队列的基本操作包括入队(enqueue)和出队(dequeue)。C语言实现队列:
#include <stdio.h>
#include <stdlib.h>
#define MAX_SIZE 100 typedef struct
{ int data[MAX_SIZE]; int front, rear;
} Queue; void initQueue(Queue *q)
{ q->front = q->rear = -1;
} int isFull(Queue *q)
{ return (q->rear + 1) % MAX_SIZE == q->front;
} int isEmpty(Queue *q)
{ return q->front == -1;
} void enqueue(Queue *q, int value)
{ if (!isFull(q)) { q->data[++q->rear] = value; } else { printf("Queue is full!\n"); }
} int dequeue(Queue *q)
{ if (!isEmpty(q)) { int value = q->data[q->front++]; if (q->front > q->rear) q->front = q->rear = -1; // 处理队列为空的情况 return value; } else { printf("Queue is empty!\n"); return -1; }
} // ... 其他队列操作函数 ...
队列的图解:
(可以想象一个水平的容器,新元素从尾部加入,从头部取出。)
三、循环队列
循环队列是对普通队列的一种改进,通过取模运算实现队首和队尾的循环,从而更高效地利用存储空间。相当于队列头尾相接,同时容量固定.
(代码与上面的队列实现类似,主要区别在于isFull
和isEmpty
的判断,以及front
和rear
的更新方式。)
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <assert.h>typedef struct
{int* a;int head; //指向头int tail; //指向尾下一个int k; //容量
} MyCircularQueue;MyCircularQueue* myCircularQueueCreate(int k)
{MyCircularQueue* obj = (MyCircularQueue*)malloc(sizeof(MyCircularQueue));if (obj == NULL){return;}//多开一个解决假溢出问题obj->a = (int*)malloc(sizeof(int)*(k + 1));obj->head = 0;obj->tail = 0;obj->k = k;return obj;
}bool myCircularQueueIsEmpty(MyCircularQueue* obj)
{return obj->head == obj->tail;
}bool myCircularQueueIsFull(MyCircularQueue* obj)
{return (obj->tail + 1) % (obj->k + 1) == obj->head;
}bool myCircularQueueEnQueue(MyCircularQueue* obj, int value)
{if (myCircularQueueIsFull(obj)){return false;}obj->a[obj->tail] = value;obj->tail++;obj->tail %= (obj->k + 1);return true;
}bool myCircularQueueDeQueue(MyCircularQueue* obj)
{if (myCircularQueueIsEmpty(obj)){return false;}++obj->head;obj->head %= (obj->k + 1);return true;
}int myCircularQueueFront(MyCircularQueue* obj)
{if (myCircularQueueIsEmpty(obj)){return -1;}else{return obj->a[obj->head];}
}int myCircularQueueRear(MyCircularQueue* obj)
{if (myCircularQueueIsEmpty(obj)){return -1;}else{return obj->a[(obj->tail + obj->k) % (obj->k + 1)];}
}void myCircularQueueFree(MyCircularQueue* obj)
{free(obj->a);free(obj);
}
四、栈实现队列
虽然栈是后进先出的数据结构,但我们可以通过两个栈(一个作为输入栈,一个作为输出栈)来模拟队列的先进先出特性。
代码实现:
(主要涉及两个栈的push和pop操作,以及如何在适当的时候交换两个栈的角色。)
// 支持动态增长的栈
typedef int STDataType;
typedef struct Stack
{STDataType* _a;int _top; // 栈顶int _capacity; // 容量
}Stack;
// 初始化栈
void StackInit(Stack* ps)
{assert(ps);ps->_a = NULL;//top指向栈顶数据的下一个位置ps->_top = 0;//top指向栈顶数据//ps->_top = -1;ps->_capacity = 0;
}
// 入栈
void StackPush(Stack* ps, STDataType data)
{assert(ps);if (ps->_top == ps->_capacity){int newcapacity = ps->_capacity == 0 ? 4 : ps->_capacity * 2;STDataType* tmp = (STDataType*)realloc(ps->_a, newcapacity * (sizeof(STDataType)));if (tmp == NULL){perror("realloc fail");return;}ps->_a = tmp;ps->_capacity = newcapacity;}ps->_a[ps->_top] = data;ps->_top++;
}
// 出栈
void StackPop(Stack* ps)
{assert(ps);assert(ps->_top > 0);ps->_top--;
}
// 获取栈顶元素
STDataType StackTop(Stack* ps)
{assert(ps);assert(ps->_top > 0);return ps->_a[ps->_top - 1];
}
// 获取栈中有效元素个数
int StackSize(Stack* ps)
{assert(ps);return ps->_top;
}
// 检测栈是否为空,如果为空返回非零结果,如果不为空返回0
int StackEmpty(Stack* ps)
{assert(ps);assert(ps);return ps->_top == 0;
}
// 销毁栈
void StackDestroy(Stack* ps)
{assert(ps);free(ps->_a);ps->_a = NULL;ps->_top = ps->_capacity = 0;
}//leetcodetypedef struct
{Stack pushst;Stack popst;
} MyQueue;MyQueue* myQueueCreate()
{MyQueue* obj = (MyQueue*)malloc(sizeof(MyQueue)); if (obj == NULL){return;}StackInit(&(obj->popst));StackInit(&(obj->pushst));return obj;
}void myQueuePush(MyQueue* obj, int x)
{StackPush(&(obj->pushst), x);
}int myQueuePeek(MyQueue* obj)
{if (StackEmpty(&(obj->popst))){//倒数据while (!StackEmpty(&(obj->pushst))){int top = StackTop(&(obj->pushst));StackPush(&(obj->popst), top);StackPop(&(obj->pushst));}}return StackTop(&(obj->popst));
}int myQueuePop(MyQueue* obj)
{int front = myQueuePeek(obj);StackPop(&(obj->popst));return front;
}bool myQueueEmpty(MyQueue* obj)
{return StackEmpty(&(obj->popst)) && StackEmpty(&(obj->pushst));
}void myQueueFree(MyQueue* obj)
{StackDestroy(&(obj->popst));StackDestroy(&(obj->pushst));free(obj);
}
五、队列实现栈
队列是先进先出的数据结构,但通过两个队列(或者一个队列和一个辅助栈)也可以模拟栈的后进先出特性。
代码实现:
typedef char QDataType;
typedef struct QueueNode
{struct QueueNode* next;QDataType val;
}QNode;typedef struct Queue
{QNode* phead;QNode* ptail;int size;
}Queue;
void QueueInit(Queue* pq)
{assert(pq);pq->phead = NULL;pq->ptail = NULL;pq->size = 0;
}void QueueDestroy(Queue* pq)
{assert(pq);QNode* cur = pq->phead;while (cur){QNode* next = cur->next;free(cur);cur = next;}pq->phead = pq->ptail = NULL;pq->size = 0;
}// 队尾插入
void QueuePush(Queue* pq, QDataType x)
{assert(pq);QNode* newnode = (QNode*)malloc(sizeof(QNode));if (newnode == NULL){perror("malloc fail");return;}newnode->next = NULL;newnode->val = x;if (pq->ptail == NULL){pq->phead = pq->ptail = newnode;}else{pq->ptail->next = newnode;pq->ptail = newnode;}pq->size++;
}// 队头删除
void QueuePop(Queue* pq)
{assert(pq);assert(pq->size != 0);/*QNode* next = pq->phead->next;free(pq->phead);pq->phead = next;if (pq->phead == NULL)pq->ptail = NULL;*/// 一个节点if (pq->phead->next == NULL){free(pq->phead);pq->phead = pq->ptail = NULL;}else // 多个节点{QNode* next = pq->phead->next;free(pq->phead);pq->phead = next;}pq->size--;
}QDataType QueueFront(Queue* pq)
{assert(pq);assert(pq->phead);return pq->phead->val;
}QDataType QueueBack(Queue* pq)
{assert(pq);assert(pq->ptail);return pq->ptail->val;
}int QueueSize(Queue* pq)
{assert(pq);return pq->size;
}bool QueueEmpty(Queue* pq)
{assert(pq);return pq->size == 0;
}
typedef struct
{Queue q1;Queue q2;
} MyStack;
//leetcode
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))){QueuePush(&(obj->q1), x);}else{QueuePush(&(obj->q2), x);}
}int myStackPop(MyStack* obj)
{//假设法Queue* empty = &(obj->q1);Queue* nonempty = &(obj->q2);if (!QueueEmpty(&(obj->q1))){nonempty = &(obj->q1);empty = &(obj->q2);}//不为空前size-1导走,删除最后一个就是栈顶数据while (QueueSize(nonempty) > 1){QueuePush(empty, QueueFront(nonempty));QueuePop(nonempty);}int top = QueueFront(nonempty);QueuePop(nonempty);return top;
}int myStackTop(MyStack* obj)
{if (!QueueEmpty(&(obj->q1))){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);
}
分享到这里,欢迎翻阅我的文章.