栈与队列的定义和区别
1.栈的定义
栈是一种只允许在一端进行插入、删除的线性表。
栈底是固定的,不允许插入和删除的一端。栈顶是能够进行插入和删除的一端。
所以栈是先进后出的线性表。
2.队列的定义
队列是一种一端进行插入,另一端进行删除的线性表。
队头是进行删除操作的一端。队尾是进行插入操作的一端。
所以队列是先进先出的线性表。
3.栈与队列的区别
栈:仅在栈顶进行插入、删除,栈底不进行操作。并且数据是先进后出。(像一个弹夹)
队列:队头进行删除,队尾进行插入。数据是先进先出。(像过隧道的车)
栈与队列的实现
1.栈的常见操作
- StackInit:初始化栈
- StackPush:入栈
- StackPop:出栈
- StackTop:获取栈顶元素
- StackEmpty:检测栈是否为空
- StackDestroy:销毁栈
2.栈的实现
栈的结构定义:
// 支持动态增长的栈
typedef int STDataType;
typedef struct Stack
{
STDataType* a; // 动态开辟空间的数组
int top; // 栈顶
int capacity; // 容量
}Stack;
栈的初始化:
void StackInit(Stack* ps)
{
assert(ps);
ps->a = NULL;
ps->capacity = 0;
//指向栈顶的下一个(即最后一个元素的下一个)
//同时也代表栈中的元素个数
ps->top = 0;
}
为什么是指向栈顶下一个: 防止产生歧义。若是代表指向栈顶,那么当top为0时是栈为空,还是栈顶在下标为0处。无法判断。
入栈:
void StackPush(Stack* ps, STDataType data)
{
assert(ps);
//判断是否需要扩容
if (ps->top == ps->capacity)
{
//若是原本容量为0则赋值4,反之则扩容至原来的两倍
int newcap = ps->capacity == 0 ? 4 : ps->capacity * 2;
STDataType* tmp = (STDataType*)realloc(ps->a, sizeof(STDataType) * newcap);
//用tmp接收,防止a被NULL覆盖
if (tmp == NULL)
{
perror("tmp realloc");
return;
}
ps->a = tmp;
//更新容量大小
ps->capacity = newcap;
}
//赋值后再指向栈顶下一个
ps->a[ps->top] = data;
ps->top++;
}
top指向栈顶的下一个入栈操作图:
ps->a[ps->top] = data;
ps->top++;
出栈:
void StackPop(Stack* ps)
{
assert(ps);
assert(ps->top > 0);
//让top--,下次入栈时将会覆盖原先的最后一个
ps->top--;
}
判断是否为空:
bool StackEmpty(Stack* ps)
{
assert(ps);
//定义是说过,top还代表了栈中元素个数
//若为空则返回真,反之为假
return ps->top == 0;
}
获取栈顶元素:
STDataType StackTop(Stack* ps)
{
assert(ps);
assert(ps->top > 0);
//因为top指向栈顶下一个,所以返回下标为top-1的元素
return ps->a[ps->top - 1];
}
栈的销毁(内存空间的回收):
void StackDestroy(Stack* ps)
{
assert(ps);
//释放空间,置零
free(ps->a);
ps->a = NULL;
ps->capacity = 0;
ps->top = 0;
}
3.队列的常见操作
- QueueInit:初始化队列
- QueuePush:队尾入队列
- QueuePop:队头出队列
- QueueFront:获取队列头部元素
- QueueBack:获取队列队尾元素
- QueueEmpty: 检测队列是否为空
- QueueDestroy:销毁队列
4.队列的实现
队列的结构定义:
typedef int QDataType;
// 链式结构:表示队列
typedef struct QListNode
{
struct QListNode* next;
QDataType data;
}QNode;
// 队列的结构
typedef struct Queue
{
//指向队头的指针
QNode* front;
//指向队尾的指针
QNode* rear;
//元素个数
int size;
}Queue;
队列的初始化:
void QueueInit(Queue* q)
{
assert(q);
q->front = q->rear = NULL;
q->size = 0;
}
入队列:
void QueuePush(Queue* q, QDataType data)
{
assert(q);
//创建新节点,并将要插入的数据保存
QNode* newnode = (QNode*)malloc(sizeof(QNode));
if (newnode == NULL)
{
perror("newnode malloc fault");
return;
}
newnode->data = data;
newnode->next = NULL;
//如果队尾为空代表队列为空,此时队尾队头指向同一个节点
if (q->rear == NULL)
{
q->front = q->rear = newnode;
}
//若队尾不为空则代表队列不为空,原队尾的next指针指向新的节点,然后队尾指向新节点
else
{
q->rear->next = newnode;
q->rear = newnode;
}
q->size++;
}
else
{
q->rear->next = newnode;
q->rear = newnode;
}
出队列:
void QueuePop(Queue* q)
{
assert(q);
assert(q->size > 0);
//存储原先的队头地址,然后队头指针指向下一个,成为新的队头
QNode* del = q->front;
q->front = q->front->next;
free(del);
del = NULL;
//删除完成后更新元素个数,并判断队列是否为空
//若为空给队尾指针赋值,防止出现野指针
q->size--;
if (q->size == 0)
q->rear = NULL;
}
获取队头元素:
QDataType QueueFront(Queue* q)
{
assert(q);
assert(q->size > 0);
return q->front->data;
}
获取队尾元素:
QDataType QueueBack(Queue* q)
{
assert(q);
assert(q->size > 0);
return q->rear->data;
}
判断是否为空:
bool QueueEmpty(Queue* q)
{
assert(q);
return q->size == 0;
}
销毁队列:
void QueueDestroy(Queue* q)
{
assert(q);
QNode* cur = q->front;
//从头开始一个一个节点free
while (cur)
{
QNode* next = cur->next;
free(cur);
cur = next;
}
cur = NULL;
q->front = q->rear = NULL;
q->size = 0;
}