目录
栈和队列是数据结构中比较基础的两种数据结构,它们都是线性结构,但是在数据存储和操作方式上有一些区别。本文将介绍栈和队列的基本概念、存储结构和操作方法。
一、栈
1.1 基本概念
栈是一种后进先出(Last In First Out,LIFO)的线性数据结构,它的特点是只能在固定的一端进行插入和删除操作。这一端被称为栈顶,另一端被称为栈底。
1.2 存储结构
栈的存储结构有两种:顺序栈和链式栈。
1.2.1 顺序栈
顺序栈是利用数组实现的栈,它的操作比较简单,但是需要事先确定栈的大小。
#include <stdio.h>
#include <stdbool.h>
//定义:
#define MAXSIZE 100
typedef struct {
int data[MAXSIZE]; // 存放栈中元素
int top; // 栈顶指针
} SqStack;
//初始化:
void InitStack(SqStack* S) {
S->top = -1; // 初始化栈顶指针为-1
}
//入栈:
bool Push(SqStack* S, int x) {
if (S->top == MAXSIZE - 1) { // 栈满
return false;
}
S->data[++S->top] = x; // 栈顶指针加1,将元素x入栈
return true;
}
//出栈:
bool Pop(SqStack* S, int* x) {
if (S->top == -1) { // 栈空
return false;
}
*x = S->data[S->top--]; // 将栈顶元素弹出,栈顶指针减1
return true;
}
1.2.2 链式栈
链式栈是利用链表实现的栈,它的操作相对复杂,但是可以动态分配内存。
//定义:
typedef int STDataType;
typedef struct Stack
{
STDataType* arr;
int top;
int capacity;
}Stack;
//初始化:
void StackInit(Stack* ps)
{
STDataType* tmp = (STDataType*)malloc(sizeof(STDataType) * 4);
if (tmp == NULL)
{
perror("malloc error");
}
ps->arr = tmp;
ps->top = 0;
ps->capacity = 4;
return;
}
//入栈:
void StackPush(Stack* ps, STDataType x)
{
assert(ps);
assert(ps->arr);
if (ps->top == ps->capacity)
{
STDataType* tmp = (STDataType*)realloc(ps->arr, sizeof(STDataType) * ps->capacity * 2);
if (tmp == NULL)
{
perror("realloc error");
}
else
{
ps->arr = tmp;
ps->capacity *= 2;
}
}
ps->arr[ps->top] = x;
ps->top++;
}
//获取栈顶元素
STDataType StackTop(Stack* ps)
{
assert(ps);
assert(ps->arr);
assert(!StackEmpty(ps));
return ps->arr[ps->top - 1];
}
//检测栈是否为空
bool StackEmpty(Stack* ps)
{
assert(ps);
assert(ps->arr);
return (ps->top == 0);
}
//栈中有效元素个数
int StackSize(Stack* ps)
{
assert(ps);
assert(ps->arr);
return (ps->top);
}
// 销毁栈
void StackDestroy(Stack* ps)
{
assert(ps);
assert(ps->arr);
free(ps->arr);
free(ps);
}
二、队列
2.1 基本概念
队列是一种先进先出(First In First Out,FIFO)的线性数据结构,它的特点是只允许在一端(队尾)进行插入数据操作,在另一端(队头)进行删除数据操作。
2.2 存储结构
队列的存储结构有两种:顺序队列和链式队列。
2.2.1 顺序队列
顺序队列是利用数组实现的队列,它的操作比较简单,但是需要事先确定队列的大小。
//定义
#define MAXSIZE 100
typedef struct {
int data[MAXSIZE]; // 存放队列中元素
int front; // 队头指针
int rear; // 队尾指针
} SqQueue;
//初始化
void InitQueue(SqQueue *Q) {
Q->front = Q->rear = 0; // 初始化队头和队尾指针为0
}
//入队
bool EnQueue(SqQueue *Q, int x) {
if ((Q->rear + 1) % MAXSIZE == Q->front) { // 队满
return false;
}
Q->data[Q->rear] = x; // 将元素x入队
Q->rear = (Q->rear + 1) % MAXSIZE;
return true;
}
//出队
bool DeQueue(SqQueue *Q, int *x) {
if (Q->front == Q->rear) { // 队空
return false;
}
*x = Q->data[Q->front]; // 将队头元素出队
Q->front = (Q->front + 1) % MAXSIZE;
return true;
}
2.2.2 链式队列
链式队列是利用链表实现的队列,它的操作相对复杂,但是可以动态分配内存。
typedef int QDataType;
typedef struct QNode
{
QDataType data;
struct QNode* next;
}QNode;
typedef struct Queue
{
QNode* head;
QNode* tail;
int size;
}Queue;
//队列初始化
void QInit(Queue* pq)
{
pq->head = NULL;
pq->tail = NULL;
pq->size = 0;
}
//入队
void QPush(Queue* pq, QDataType x)
{
assert(pq);
QNode* tmp = (QNode*)malloc(sizeof(QNode));
if (tmp == NULL)
{
perror("malloc error");
}
tmp->data = x;
tmp->next = NULL;
if (QEmpty(pq))
{
pq->head = pq->tail = tmp;
}
else
{
pq->tail->next = tmp;
pq->tail = tmp;
}
pq->size++;
}
//出队
void QPop(Queue* pq)
{
assert(pq);
assert(!QEmpty(pq));
QNode* tmp = pq->head->next;
free(pq->head);
pq->head = tmp;
pq->size--;
}
//队是否为空
bool QEmpty(Queue* pq)
{
assert(pq);
return (pq->size == 0);
}
//获取队头元素
QDataType QHeadData(Queue* pq)
{
assert(pq);
assert(pq->head);
assert(!QEmpty(pq));
return pq->head->data;
}
//获取队尾元素
QDataType QTailData(Queue* pq)
{
assert(pq);
assert(pq->tail);
assert(!QEmpty(pq));
return pq->tail->data;
}
//有效元素个数
int QSize(Queue* pq)
{
assert(pq);
return pq->size;
}
//队列销毁
void QDestroy(Queue* pq)
{
assert(pq);
assert(pq->head);
while (pq->head)
{
QPop(pq);
}
free(pq);
}
三、总结
栈和队列是数据结构中比较基础的两种数据结构,它们都是线性结构,但是在数据存储和操作方式上有一些区别。
栈的特点是后进先出,只能在一端进行插入和删除操作,存储结构有顺序栈和链式栈两种。
队列的特点是先进先出,只能在队尾插入元素,在队头删除元素,存储结构有顺序队列和链式队列两种。
在实际应用中,栈和队列都有广泛的应用,例如程序的函数调用、表达式求值、图的遍历等。掌握栈和队列的基本概念和操作方法,对于学习其他数据结构和算法具有重要的意义。