目录
栈
引言
现实生活中,有很多先进后出的例子,比如说弹夹,一端进出子弹,先进弹夹的子弹后发射,后进弹夹的子弹先发射;再比如一摞盘子,一端摆放盘子先放到下面的盘子后拿出;再比如烤串,一般不从用手抓的一端串肉,先穿进去的食物后被吃到。
这些物品都遵循先进后出的原则
而我们的栈就是一种数据在固定一端 先进后出(LIFO)的线性表
出入数据的一端叫栈顶,另一端叫栈底
入数据称为压栈或者入栈,出数据称为出栈
栈的实现
思考用数组还是链表的底层结构实现?
数组:我们要保证先进后出的原则,那么数组的尾部当栈顶,利用尾插和尾删操作就可以实现数据的进入和弹出了
分析:尾插和尾删操作时间复杂度都为O(1),十分快捷
链表:链表的尾当栈顶,尾插和尾删实现数据的进入和弹出
分析:尾插和尾删操作需要找尾,时间复杂度O(N)
若想实现O(1),则需要在接口函数参数中创建尾指针控制尾节点
综合比较,数组更简便,所以我们选择数组来实现栈
接口实现
//初始化栈
void StackInit(Stack* ps) {
//检查指针有效性
assert(ps);
ps->arr = NULL;
ps->capacity = ps->top = 0;//top指向栈顶的后一个元素
}
//数据入栈
void StackPush(Stack* ps, STDataType val) {
//检查指针有效性
assert(ps);
//检查容量并扩容
if (ps->top == ps->capacity) {
//原始容量可能为0,需要利用三目操作符判断并扩容
size_t newCapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;
//在堆上开辟新的数组
STDataType* tmp = (STDataType*)malloc(newCapacity * sizeof(STDataType));
//检查malloc是否成功
if (tmp == NULL) {
perror("malloc err!");
return;
}
ps->capacity = newCapacity;
ps->arr = tmp;
free(tmp);
}
ps->arr[ps->top++] = val;
}
//数据出栈
void StackPop(Stack* ps) {
//检查指针有效性
assert(ps);
if (!StackEmpty(ps)) {
ps->top--;
}
}
//查找栈顶元素
STDataType StackTopVal(Stack* ps) {
//检查指针有效性
assert(ps);
if (!StackEmpty(ps)) {
return ps->arr[ps->top - 1];
}
}
//检查栈的有效数据个数
size_t StackSize(Stack* ps) {
//检查指针有效性
assert(ps);
return ps->top;
}
//检查栈是否为空
bool StackEmpty(Stack* ps) {
//检查指针有效性
assert(ps);
return ps->top == 0;
}
//销毁栈
void StackDestory(Stack* ps) {
//检查指针有效性
assert(ps);
free(ps->arr);
ps->arr = NULL;
ps->capacity = ps->top = 0;
}
队列
引言
队列(queue)是一种遵循先入先出(FIFO)规则的线性数据结构。顾名思义,队列模拟了排队现象,即新来的人不断加入队列尾部,而位于队列头部的人逐个离开。
出数据的一端叫队首,入数据的一端叫队尾
出数据称为出队,入数据称为入队
队列的实现
思考用数组还是链表的底层结构实现?
数组:
入队:尾插O(1)
出队:头删,要依次挪动数据O(N)
链表:
入队:尾插找尾O(N),函数接口多一个尾指针控制尾节点的参数->O(1)
出队:头删O(1)
综合对比:数组出队依次挪动数据容易出错,我们可以采用链表的底层结构实现队列
用单链表即可,复杂链表实现队列好比大炮打蚊子了,没必要。
接口实现
#include "Queue.h"
//初始化队列
void QueueInit(Queue* pq) {
//检查指针有效性
assert(pq);
pq->phead = pq->ptail = NULL;
pq->size = 0;
}
//数据入队列
void QueuePush(Queue* pq, QDataType val) {
//检查指针有效性
assert(pq);
//创建新节点
QNode* newNode = (QNode*)malloc(sizeof(QNode));
//检查内存是否申请成功
if (newNode == NULL) {
perror("malloc err!");
return;
}
//初始化newNode
newNode->data = val;
newNode->next = NULL;
//队列为空的情况
if (pq->phead == NULL) {
pq->phead = pq->ptail = newNode;
}
//队列不为空,尾插
pq->ptail->next = newNode;
pq->ptail = newNode;
//队列有效数据增加
pq->size++;
}
//数据出队列
void QueuePop(Queue* pq) {
//检查指针有效性
assert(pq);
//队列保证不能为空,暴力检查
assert(!QueueEmpty(pq));
//队列只有一个节点
if (pq->phead->next == NULL) {
pq->phead = pq->ptail = NULL;
}
//队列有多个节点,头删
else {
QNode* next = pq->phead->next;
free(pq->phead);
pq->phead = next;
}
//队列有效元素个数减少
pq->size--;
}
//获取队列有效元素个数
size_t QueueSize(Queue* pq){
//检查指针有效性
assert(pq);
return pq->size;
}
//获取队首元素值
QDataType QueueFrontVal(Queue* pq) {
//检查指针有效性
assert(pq);
// 暴力检查
assert(pq->phead != NULL);
return pq->phead->data;
}
//获取队尾元素值
QDataType QueueBackVal(Queue* pq) {
//检查指针有效性
assert(pq);
// 暴力检查
assert(pq->ptail != NULL);
return pq->ptail->data;
}
//检查队列是否为空
bool QueueEmpty(Queue* pq) {
//检查指针有效性
assert(pq);
return pq->size == 0;
}
//销毁队列
void QueueDestory(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;
}