目录
栈
概念与结构
栈:一种特殊的线性表,其只允许在固定的一段进行插入和删除元素操作。
进行数据插入和删除操作的一端称为栈顶,另一端称为栈底。
栈中的数据元素遵守后进先出LIFO(Last In First Out)的原则。
进栈:栈的插入操作叫做进栈/压栈/入栈,进栈从栈顶进。
出栈:栈的删除操作叫出栈。出数据从栈顶出。
栈底层结构选型
栈一般可以用数组或链表实现,相对而言数组的结构实现更优一些。
因为数组在尾上插入数据的代价比较小。
栈的实现
stack.h
#pragma once
//栈的模拟实现
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <stdbool.h>
//栈的结构
typedef int STDataType;
typedef struct Stack
{
STDataType* data;
int top;//有效元素个数
int capacity;//容量
}ST;
//初始化
void STInit(ST* ps);
//进栈
void STPush(ST* ps, STDataType x);
//判空
bool STEmpty(ST* ps);
//出栈
void STPop(ST* ps);
//查看栈顶元素
STDataType STTop(ST* ps);
//有效元素个数
int STSize(ST* ps);
//销毁
void STDestroy(ST* ps);
stack.c和test.c
初始化
//初始化
void STInit(ST* ps)
{
//防止ps为NULL
assert(ps);
//初始化
ps->data = NULL;
ps->top = ps->capacity = 0;
}
test.c
ST st;
STInit(&st);
进栈
//进栈
void STPush(ST* ps, STDataType x)
{
//防止ps为NULL
assert(ps);
//判断增容
if (ps->top == ps->capacity)
{
int newcapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;
STDataType* tmp = (STDataType*)realloc(ps->data, newcapacity * sizeof(STDataType));
if (tmp == NULL)
{
perror("realloc fail");
exit(1);
}
ps->data = tmp;
ps->capacity = newcapacity;
}
//进栈+更新有效元素个数
ps->data[ps->top++] = x;
}
test.c
//测试进栈
STPush(&st, 1);
STPush(&st, 2);
STPush(&st, 3);
STPush(&st, 4);
STPush(&st, 5);
判空
//判空
bool STEmpty(ST* ps)
{
//防止ps为NULL
assert(ps);
//有效元素个数为0时,栈为空
return ps->top == 0;
}
test.c
//测试判空
if (STEmpty(&st))
printf("栈为空\n");
else
printf("栈非空\n");
出栈
//出栈
void STPop(ST* ps)
{
//防止ps为NULL,且防止栈为空
assert(!STEmpty(ps));
//更新有效元素个数,达到出栈效果
ps->top--;
}
test.c
//进栈
STPush(&st, 1);
STPush(&st, 2);
STPush(&st, 3);
STPush(&st, 4);
STPush(&st, 5);
//测试出栈
STPop(&st);
STPop(&st);
STPop(&st);
STPop(&st);
STPop(&st);
STPop(&st);
执行5次出栈操作,此时有效元素个数为0
再次执行出栈操作,断言报错。
查询栈顶元素
//查看栈顶元素
STDataType STTop(ST* ps)
{
//防止ps为NULL,且防止栈为空
assert(!STEmpty(ps));
//返回栈顶元素
return ps->data[ps->top - 1];
}
test.c
//进栈
STPush(&st, 1);
STPush(&st, 2);
STPush(&st, 3);
STPush(&st, 4);
STPush(&st, 5);
//测试查看栈顶元素
STDataType find = STTop(&st);
printf("%d\n", find);
有效元素个数
//有效元素个数
int STSize(ST* ps)
{
//防止ps为NULL
assert(ps);
//返回有效元素个数
return ps->top;
}
test.c
//进栈
STPush(&st, 1);
STPush(&st, 2);
STPush(&st, 3);
STPush(&st, 4);
STPush(&st, 5);
//测试有效元素个数
int size = STSize(&st);
printf("%d\n", size);
销毁
//销毁
void STDestroy(ST* ps)
{
//防止ps为NULL
assert(ps);
//ps->data不为NULL,说明申请空间了,需要释放
if (ps->data)
free(ps->data);
//还原
ps->data = NULL;
ps->capacity = ps->top = 0;
}
test.c
//进栈
STPush(&st, 1);
STPush(&st, 2);
STPush(&st, 3);
STPush(&st, 4);
STPush(&st, 5);
//测试销毁
STDestroy(&st);
队列
概念与结构
队列:只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表,
队列中的数据元素遵守先进先出FIFO(First In First Out)的原则。
入队列:进行插入操作的一端称为队尾。
出队列:进行删除操作的一端称为队头。
队列底层结构选型
队列可以用数组和链表的结构实现,使用链表的结构实现更优一些,因为如果使用
数组的结构,出队列在数组头上出数据效率会比较低。
队列的实现
Queue.h
#pragma once
//队列的模拟实现
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <stdbool.h>
//队列的节点的结构
typedef int QDataType;
typedef struct QueueNode
{
QDataType data;//节点中的有效数据
struct QueueNode* next;//指向下一个节点的指针
}QN;
//队列的结构
typedef struct Queue
{
QN* phead;//队头
QN* ptail;//队尾
int size;//有效元素个数
}Q;
//初始化
void QInit(Q* pq);
//入队
void QPush(Q* pq, QDataType x);
//判空
bool QEmpty(Q* pq);
//出队
void QPop(Q* pq);
//查询队尾元素
QDataType QBack(Q* pq);
//查询队头元素
QDataType QFront(Q* pq);
//有效元素个数
int QSize(Q* pq);
//销毁
void QDestroy(Q* pq);
queue.c和test.c
初始化
//初始化
void QInit(Q* pq)
{
assert(pq);//防止pq尾NULL
//初始化
pq->phead = pq->ptail = NULL;
pq->size = 0;
}
test.c
Q q;
QInit(&q);
入队
//入队
void QPush(Q* pq, QDataType x)
{
assert(pq);//防止pq为NULL
//申请新节点
QN* newnode = (QN*)malloc(sizeof(QN));
if (newnode == NULL)
{
perror("malloc fail");
exit(1);
}
newnode->data = x;
newnode->next = NULL;
if (pq->phead == NULL)//队列为空
pq->phead = pq->ptail = newnode;
else//队列非空
{
pq->ptail->next = newnode;
pq->ptail = pq->ptail->next;
}
pq->size++;//更新有效元素个数
}
test.c
//测试入队
QPush(&q, 1);
QPush(&q, 2);
QPush(&q, 3);
判空
//判空
bool QEmpty(Q* pq)
{
assert(pq);防止pq为NULL
//队头为空时,队列一定为空
return pq->phead == NULL;
}
test.c
//入队
QPush(&q, 1);
QPush(&q, 2);
QPush(&q, 3);
//测试判空
printf("%d\n", QEmpty(&q));
空为真,非空为假。
出队
//出队
void QPop(Q* pq)
{
//防止pq为NULL,且队列为空
assert(!QEmpty(pq));
//出队——从队头出
QN* next = pq->phead->next;
free(pq->phead);
pq->phead = next;
//如果队列中只有一个元素,
//出队后,pq->ptail要置空,防止其成为野指针
if (pq->phead == NULL)
pq->ptail = NULL;
pq->size--;//更新有效元素个数
}
test.c
//入队
QPush(&q, 1);
QPush(&q, 2);
QPush(&q, 3);
//测试出队
QPop(&q);
QPop(&q);
QPop(&q);
QPop(&q);
出队三次,此时队列为空。
再次出队,断言报错。
查询队尾元素
//查询队尾元素
QDataType QBack(Q* pq)
{
//防止pq为NULL,且队列为空
assert(!QEmpty(pq));
//返回队尾元素
return pq->ptail->data;
}
test.c
//入队
QPush(&q, 1);
QPush(&q, 2);
QPush(&q, 3);
//测试查询队尾元素
printf("%d\n", QBack(&q));
查询队头元素
//查询队头元素
QDataType QFront(Q* pq)
{
//防止pq为NULL,且队列为空
assert(!QEmpty(pq));
//返回队头元素
return pq->phead->data;
}
test.c
//入队
QPush(&q, 1);
QPush(&q, 2);
QPush(&q, 3);
//测试查询队头元素
printf("%d\n", QFront(&q));
有效元素个数
//有效元素个数
int QSize(Q* pq)
{
//防止pq为NULL
assert(pq);
//返回有效元素个数
return pq->size;
}
test.c
//入队
QPush(&q, 1);
QPush(&q, 2);
QPush(&q, 3);
//测试有效元素个数
printf("%d\n", QSize(&q));
销毁
//销毁
void QDestroy(Q* pq)
{
assert(pq);//防止pq为NULL
//销毁
QN* pcur = pq->phead;
while (pcur)
{
QN* next = pcur->next;
free(pcur);
pcur = next;
}
//销毁后,把头、尾也置空,防止野指针
pq->phead = pq->ptail = NULL;
//更新有效元素个数
pq->size = 0;
}
test.c
//入队
QPush(&q, 1);
QPush(&q, 2);
QPush(&q, 3);
//测试销毁
QDestroy(&q);