一. 栈
1.什么是栈
- 栈是一种特殊的线性表,栈有两端可以分为栈顶和栈底,栈只允许在栈顶进行元素的插入和删除,栈的元素要遵循后进先出的原则
- 栈的插入操作叫做进栈/入栈/压栈,栈的删除操作叫做出栈,入数据和出数据都在栈顶
2.栈的实现
- 栈的实现可以使用数组或者链表实现,使用数组实现栈更加的合理,因为数组在尾插时消耗更小
- 栈实现使用结构体,结构体成员有指定类型( StackDataType)的指针a相当于数组,成员还有top来记录栈顶的位置和capacity记录数据的最大容量
typedef int StackDataType;
typedef struct Stack
{
StackDataType* a;
int top;
int capacity;}Stack;
//类似链表的结构
- 初始化栈:将指针a初始化为空指针,栈顶位置top初始化为0,栈的最大容量capacity初始化为0
void StackInit(Stack* pc)
//初始化栈
{
assert(pc);
pc->a = NULL;
pc->top = 0;
pc->capacity = 0;
}
- 入栈:首先检查栈顶的位置top是否已经增长到最大容量(capacity(pc->capacity == pc->top),如果相等,使用realloc函数扩容到NewCapacity个StackDataType类型的空间。再将栈顶元素设置为x,栈顶的位置( pc->top++)
- 在给NewCapacity赋值的时候使用了三目运算符(NewCapacity = pc->capacity == 0 ? 4 : pc->capacity * 2 ),这个代码的意思是如果最大容量Capacity==0,也就是第一次入栈时,把NewCapacity赋值为4,之后每次扩容NewCapacity每次变成二倍
void StackPush(Stack* pc, StackDataType x)
//入栈
{
assert(pc);//CapacityCheck函数
if (pc->capacity == pc->top)
{
int NewCapacity = pc->capacity == 0 ? 4 : pc->capacity * 2;
StackDataType* tmp = (StackDataType*)realloc(pc->a,sizeof(StackDataType) * NewCapacity);
if (tmp == NULL)
{
perror("realloc");
exit(-1);
}
pc->a = tmp;
pc->capacity = NewCapacity;
}
pc->a[pc->top] = x;
pc->top++;}
- 出栈:这里出栈不需要真正的把栈顶的元素删除掉,直接让栈顶的位置top向下移动一个数据的位置即可,之后就算要入栈时添加的数据也会把原来没有删除的数据覆盖掉
void StackPop(Stack* pc)
//出栈
{
assert(pc);
assert(pc->top > 0);
pc->top--;
}
- 获取栈顶元素:要注意栈顶的位置会比对应的数组下标大一,比如:第一个数入栈的时候( pc->a[pc->top] = x),此时top的值为0,然后pc->top++,top变成了1。所以栈顶的位置会比对应的数组下标大一
StackDataType StackTop(Stack* pc)
//获取栈顶元素
{
assert(pc);
assert(pc->top > 0);
return pc->a[pc->top-1];
}
- 检测栈是否为空 :直接返回pc->top == 0即可
int StackEmpty(Stack* pc)
//检测栈是否为空//为空返回非零结果,不为空则返回0
{
assert(pc);return pc->top == 0;
}
- 销毁栈:使用free函数释放a指向的申请的空间,把top和capacity全部初始化为0
void StackDestory(Stack* pc)
//销毁栈
{
assert(pc);
free(pc->a);
pc->top = pc->capacity = 0;}
二. 队列
1.什么是队列
- 队列也是一种特殊的线性表,队列有两端叫做队头和队尾,队列只允许在队尾插入数据,在队头进行数据删除,队列具有先进先出的特性
2.队列的实现
- 队列也可以使用数组和链表结构实现,但是用链表使用更合适。因为如果使用数组,在出队列时把第一个元素拿出去后,后面的数据要依次向前挪动,使用数组性能不高,所以要使用链表实现
- 队列使用链表实现,链表的每一个节点都是结构体,结构体成员Data存储QlistData类型的数据,成员next存储该节点指向下一个节点的地址(如果该节点是最后一个节点,那么该节点next=NULL)。队列还需要指向QlistNode类型的队首指针front和指向队尾的指针rear,还有记录数据个数的变量size
typedef int QlistData;
typedef struct QlistNode
{
QlistData Data;
struct QlistNode* next;}QlistNode;
//每一个节点的类型typedef struct Queue
{
QlistNode* front;
QlistNode* rear;
int size;
}Queue;
//定义队尾和队首
- 队列初始化:将指向队首和队尾的指针front和rear置为空指针,队列的数据个数size=0
void QlistInit(Queue* pc)
//队列初始化
{
assert(pc);
pc->front = pc->rear = NULL;
pc->size = 0;
}
- 入队列:首先创建一个新节点(使用动态内存开辟),把节点的值设置为x( NewNode->Data = x),再把节点的next指针置成空指针,如果是第一次入队列要把rear和front指针都指向新节点,如果不是第一次入队列,让原队列队尾指针rear指向的节点指向新节点(pc->rear->next = NewNode),再让队尾指针rear指向新节点
void QlistPush(Queue* pc, QlistData x)
//入队列
{
assert(pc);
//这里-----------------------------------------------------
QlistNode* NewNode = (QlistNode*)malloc(sizeof(QlistNode));
if (NewNode == NULL)
{
perror("QlistPush");
exit(-1);
}
NewNode->Data = x;
NewNode->next = NULL;
//从上到下其实是QlistBuy创建一个新的节点-------------------
if (pc->rear == NULL)//此时队列为空
{
pc->rear = pc->front = NewNode;
}
else
{
pc->rear->next = NewNode;
pc->rear = pc->rear->next;
}
++pc->size;
}
- 出队列:断言队列不为空(assert(!QlistEmpty(pc))),如果队列中只有一个节点,那么释放该节点时,将队首和队尾指针置空(pc->rear = pc->front = NULL),如果队列中不止一个节点时,使用一个指针cur存储队首指针,队首指针向后一个节点,然后释放cur指向的节点并将cur置为空指针,最后别忘了zise--
void QlistPop(Queue* pc)
//出队列
{
assert(pc);
assert(!QlistEmpty(pc));
if (pc->rear == pc->front)
{
free(pc->front);
pc->rear = pc->front = NULL;
}
else
{
QlistNode* cur = pc->front;
pc->front = pc->front->next;
free(cur);
cur = NULL;
}
--pc->size;
}
- 判断队列是否为空:直接返回pc->front == NULL即可
bool QlistEmpty(Queue* pc)
//判断队列是否为空
{
assert(pc);
return pc->front == NULL;
}
- 获取队首元素:直接返回pc->front->Data即可
QlistData QlistFront(Queue* pc)
//获取队首元素
{
assert(pc);
assert(!QlistEmpty(pc));return pc->front->Data;
}
- 获取队尾元素:直接返回pc->rear->Data即可
QlistData QlistBack(Queue* pc)
//获取队尾元素
{
assert(pc);
assert(!QlistEmpty(pc));return pc->rear->Data;
}
- 获取有效元素的个数:直接返回pc->size即可
int QlistSize(Queue* pc)
//获取有效元素的个数
{
assert(pc);
return pc->size;
}
- 销毁节点:使用指针cur存储队首指针front,队首front指针一直向后移动一个节点,cur指针释放指向节点,cur指针再指向front,循环往复直到(front==NULL),最后把指针cur和front置空,size=0即可
void QlistDestory(Queue* pc)
//销毁节点
{
assert(pc);
QlistNode* cur = pc->front;
while (pc->front)
{
pc->front = pc->front->next;
free(cur);
cur = pc->front;
}
//空间释放后指针置空
pc->front = pc->rear = NULL;
pc->size = 0;
}