栈和队列
栈
栈的定义
栈是一种限定性线性表,是将线性表的插入和删除限制在只能在一端操作,我们通俗的叫为栈顶(Top),通常只允许在栈顶进行插入、删除。
表的另一端我们通俗的较为栈底(Bottom),一般栈底不做任何操作。
栈的插入操作我们一般形象的称为入栈和进栈,删除操作形象的叫为出栈和退栈。
栈的操作顺序为先进后出,后进先出,一般采用数组来存储栈是较为方便的。
1 所在的位置为栈底,5 所在的位置为栈顶,入栈顺序为 1、2、3、4、5;由于栈顶只允许插入和删除,所以 出栈顺序为 5、4、3、2、1。
当先让1、2入栈,在把栈出空,然后再让3、4、5入栈,然后再出空,则最后出栈的先后顺序为 2、1、5、4、3。
代码实现栈
1、实现一个动态数组
用数组实现一个栈,也叫顺序栈,也就是在内存中申请一块连续的空间,依次存放自栈底到栈顶的数据元素,栈底也就是数组为0的位置,栈顶就是数组大小减1的位置。
typedef int STDataType;
typedef struct Stack
{
STDataType* a;//用于动态申请空间
int top;//用于记录栈里的元素个数,也就是数组的下标
int capacity;//记录数组允许的最大空间
}ST;
2、初始化栈
//初始化
void STInit(ST* ps)
{
assert(ps);//如果这里没有按照形参的要求传参会直接报错结束
ps->a = (STDataType*)malloc(sizeof(STDataType) * 4);//动态申请4个STDataType大小的空间
if (ps->a == NULL)
{
perror("malloc err");
return;//如果申请空间失败会直接结束程序,并且报错
}
ps->capacity = 4;//代码执行到这里说明空间申请成功,并且设置数组最大的空间为4
ps->top = 0;//初始化数组下标为0
}
3、销毁动态栈
//栈销毁
void STDestroy(ST* ps)
{
assert(ps);//如果这里没有按照形参的要求传参会直接报错结束
free(ps->a);//因为数组a申请的是动态的,所以使用完后需要进行内存释放,否则会发生内存泄漏,后果比较严重
ps->a = NULL;//释放空间后对a进行置空,防止野指针
ps->top = 0;//数组下标和数组允许的最大空间都设置为0
ps->capacity = 0;
}
4、栈的插入
//栈插入
void STPush(ST* ps, STDataType x)
{
assert(ps);//如果这里没有按照形参的要求传参会直接报错结束
if (ps->capacity == ps->top)
{//插入之前先判断一个数组存储空间是否到达了极限,如果空间马上不足就是用realloc进行原地或异地扩容
STDataType* new = (STDataType*)realloc(ps->a, sizeof(STDataType) * ps->capacity * 2);
if (new == NULL)
{
perror("realloc err");
return;//如果申请失败就结束程序,并输出我们的perror的错误语
}
ps->a = new;//进行地址赋值
ps->capacity = ps->capacity * 2;//因为我们扩容了二倍,所以capacity最大允许存放的数据元素空间也得扩容二倍
}
ps->a[ps->top] = x;//插入元素数据
ps->top++;//小标往后加一位
}
5、判断栈是否为空
//判断栈区是否为空
bool STEmpty(ST* ps)
{
assert(ps);//如果这里没有按照形参的要求传参会直接报错结束
return ps->top;//当栈区为空时,top为0,0为假,非0为真
}
6、栈的删除(从尾部删除)
//栈的删除
void STPop(ST* ps)
{
assert(ps);//如果这里没有按照形参的要求传参会直接报错结束
assert(STEmpty(ps));//如果栈为空就不会操作下面的程序
ps->top--;//元素下标减一,实现删除
}
7、返回栈内现有多少元素
//栈里有多少元素
int STSize(ST* ps)
{
assert(ps);//如果这里没有按照形参的要求传参会直接报错结束
return ps->top;//返回元素下标也就是现有的数据元素个数
}
8、返回栈顶元素
//访问栈顶的元素
STDataType STTop(ST* ps)
{
assert(ps);//如果这里没有按照形参的要求传参会直接报错结束
assert(STEmpty(ps));//如果栈为空就不会操作下面的程序
return ps->a[ps->top - 1];//因为每次插入数据后top都往后加了一位,所以在返回栈顶元素的时候需要减一
}
栈完整代码
头文件 Stack.h
#pragma once
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#include <stdbool.h>
typedef int STDataType;
typedef struct Stack
{
STDataType* a;
int top;
int capacity;
}ST;
//初始化
void STInit(ST* ps);
//栈销毁
void STDestroy(ST* ps);
//栈插入
void STPush(ST* ps, STDataType x);
//判断栈区是否为空
bool STEmpty(ST* ps);
//栈的删除
void STPop(ST* ps);
//栈里有多少元素
int STSize(ST* ps);
//访问栈顶的元素
STDataType STTop(ST* ps);
源文件 Stack.c
#define _CRT_SECURE_NO_WARNINGS 1
#include "Stack.h"
//初始化
void STInit(ST* ps)
{
assert(ps);
ps->a = (STDataType*)malloc(sizeof(STDataType) * 4);
if (ps->a == NULL)
{
perror("malloc err");
return;
}
ps->capacity = 4;
ps->top = 0;
}
//栈销毁
void STDestroy(ST* ps)
{
assert(ps);
free(ps->a);
ps->a = NULL;
ps->top = 0;
ps->capacity = 0;
}
//栈插入
void STPush(ST* ps, STDataType x)
{
assert(ps);
if (ps->capacity == ps->top)
{
STDataType* new = (STDataType*)realloc(ps->a, sizeof(STDataType) * ps->capacity * 2);
if (new == NULL)
{
perror("realloc err");
return;
}
ps->a = new;
ps->capacity = ps->capacity * 2;
}
ps->a[ps->top] = x;
ps->top++;
}
//判断栈区是否为空
bool STEmpty(ST* ps)
{
assert(ps);
return ps->top;
}
//栈的删除
void STPop(ST* ps)
{
assert(ps);
assert(STEmpty(ps));
ps->top--;
}
//栈里有多少元素
int STSize(ST* ps)
{
assert(ps);
return ps->top;
}
//访问栈顶的元素
STDataType STTop(ST* ps)
{
assert(ps);
assert(STEmpty(ps));
return ps->a[ps->top - 1];
}
源文件(用于测试的文件) test.c
#define _CRT_SECURE_NO_WARNINGS 1
#include "Stack.h"
void test()
{
ST st;
STInit(&st);
STPush(&st,1);
STPush(&st, 2);
printf("%d ", STTop(&st));
STPop(&st);
STPush(&st, 3);
STPush(&st, 4);
printf("%d ", STTop(&st));
STPop(&st);
STPush(&st, 5);
while (STEmpty(&st))
{
printf("%d ", STTop(&st));
STPop(&st);
}
STDestroy(&st);
}
int main()
{
test();
return 0;
}
队列
队列的定义
队列是只允许在一端进行插入操作,而在另一端进行删除输出操作的线性表。只允许插入的一端叫做队尾,只允许删除输出的一端叫做队头。
队的特点是先进先出,而栈是先进后出
由于队是先进先出,队头和队尾都会有操作,所以用数组来实现队列是不合实际的,我们在这里使用链表来完成,一个记录队头,一个记录队尾
代码实现栈
1、定义一个单链表和能记录队头和队尾的结构体
typedef int QDatatype;
typedef struct QueueNode
{
struct QueueNode* next;
QDatatype data;
}QNode;
typedef struct Queue
{
QNode* head;
QNode* tail;
int size;
}Queue;
关系图
2、队列初始化
//初始化队列
void QueueInit(Queue* pq)
{
assert(pq);//如果这里没有按照形参的要求传参会直接报错结束
pq->head = pq->tail = NULL;//将head和tail的初始指认状态都设置为空
pq->size = 0;//size代表目前的数据元素个数,
}
3、队列销毁
//销毁队列
void QueueDestroy(Queue* pq)
{
assert(pq);//如果这里没有按照形参的要求传参会直接报错结束
QNode* cur = pq->head;//用于记录头的位置
while (cur)
{
QNode* next = cur->next;//保存头的下一个位置
free(cur);//因为刚开始就把head的位置给了cur,所以这里只free cur就好了
cur = next;//对cur重新赋值
}
pq->head = pq->tail = NULL;//设置为空,防止野指针
pq->size = 0;//数据个数设置为空
}
4、队列插入数据
//队列插入数据
void QueuePush(Queue* pq,QDatatype x)
{
QNode* newnode = (QNode*)malloc(sizeof(QNode));//malloc一个结点,用于链接队尾
if (newnode == NULL)
{
perror("Push malloc err");//如果申请失败,打印perror提示
}
else
{
newnode->data = x;//申请空间成功,将数据赋值进去
newnode->next = NULL;//这里必须要把next设置为空
}
if (pq->head == NULL && pq->tail == NULL)
{
//如果头和尾都指向空,说明现在的队列还是空的,所以直接赋值给头和尾就行
pq->head = pq->tail = newnode;
}
else
{
pq->tail->next = newnode;//程序走到这里说明队列不为空,再队尾插入数据就行
pq->tail = pq->tail->next;//更新队尾的指向
}
pq->size++;//每插入一个数据就让数据的元素个数加一个
}
5、出队操作
//队列删除
void QueuePop(Queue* pq)
{
assert(pq);//如果这里没有按照形参的要求传参会直接报错结束
assert(pq->head!=NULL);//如果head为空说明数据元素为0,不可以再删除,所以这里直接断言结束程序
/*if (pq->head->next == NULL)
{
free(pq->head);
pq->head = pq->tail = NULL;
}
else
{
QNode* next = pq->head->next;
free(pq->head);
pq->head = next;
}*/
QNode* next = pq->head->next;//记录队头的下一个位置
free(pq->head);//释放删除队头
pq->head = next;//队头重新赋值
if (pq->head == NULL)//如果head为空证明队列已经出空,并把tail也设置为空
pq->tail = NULL;
pq->size--;//出一个数据,数据元素个数减一个
}
6、返回目前队列的大小
//返回队列现在的大小
int QueueSize(Queue* pq)
{
assert(pq);//如果这里没有按照形参的要求传参会直接报错结束
//这里不需要判断队列是否为空,因为当队列为空时元素个数尾0
return pq->size;//返回队列数据元素个数
}
7、返回队列是否为空(空位假)
//队列是否为空
bool QueueEmpty(Queue* pq)
{
assert(pq);//如果这里没有按照形参的要求传参会直接报错结束
return pq->size;//0为假,非0为真
}
8、返回队列的队头元素
//返回队列的第一个元素
QDatatype QueueFront(Queue* pq)
{
assert(pq);//如果这里没有按照形参的要求传参会直接报错结束
assert(QueueEmpty(pq));//如果队列为空就不会操作下面的程序
return pq->head->data;//返回队头指向的数据元素
}
9、返回队尾元素
//返回队列的最后一个元素
QDatatype QueueBack(Queue* pq)
{
assert(pq);//如果这里没有按照形参的要求传参会直接报错结束
assert(QueueEmpty(pq));//如果队列为空就不会操作下面的程序
return pq->tail->data;//返回队尾指向的数据元素
}
队列完整代码
头文件 Queue.h
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<stdbool.h>
#include<assert.h>
typedef int QDatatype;
typedef struct QueueNode
{
struct QueueNode* next;
QDatatype data;
}QNode;
typedef struct Queue
{
QNode* head;
QNode* tail;
int size;
}Queue;
//初始化队列
void QueueInit(Queue* pq);
//销毁队列
void QueueDestroy(Queue* pq);
//队列输入数据
void QueuePush(Queue* pq,QDatatype x);
//队列删除
void QueuePop(Queue* pq);
//返回队列现在的大小
int QueueSize(Queue* pq);
//队列是否为空
bool QueueEmpty(Queue* pq);
//返回队列的第一个元素
QDatatype QueueFront(Queue* pq);
//返回队列的最后一个元素
QDatatype QueueBack(Queue* pq);
源文件 Queue.c
#define _CRT_SECURE_NO_WARNINGS 1
#include "Queue.h"
//初始化队列
void QueueInit(Queue* pq)
{
assert(pq);
pq->head = pq->tail = NULL;
pq->size = 0;
}
//销毁队列
void QueueDestroy(Queue* pq)
{
assert(pq);
QNode* cur = pq->head;
while (cur)
{
QNode* next = cur->next;
free(cur);
cur = next;
}
pq->head = pq->tail = NULL;
pq->size = 0;
}
//队列插入数据
void QueuePush(Queue* pq,QDatatype x)
{
QNode* newnode = (QNode*)malloc(sizeof(QNode));
if (newnode == NULL)
{
perror("Push malloc err");
}
else
{
newnode->data = x;
newnode->next = NULL;
}
if (pq->head == NULL && pq->tail == NULL)
{
pq->head = pq->tail = newnode;
}
else
{
pq->tail->next = newnode;
pq->tail = pq->tail->next;
}
pq->size++;
}
//队列删除
void QueuePop(Queue* pq)
{
assert(pq);
assert(pq->head!=NULL);
/*if (pq->head->next == NULL)
{
free(pq->head);
pq->head = pq->tail = NULL;
}
else
{
QNode* next = pq->head->next;
free(pq->head);
pq->head = next;
}*/
QNode* next = pq->head->next;
free(pq->head);
pq->head = next;
if (pq->head == NULL)
pq->tail = NULL;
pq->size--;
}
//返回队列现在的大小
int QueueSize(Queue* pq)
{
assert(pq);
return pq->size;
}
//队列是否为空
bool QueueEmpty(Queue* pq)
{
assert(pq);
return pq->size;
}
//返回队列的第一个元素
QDatatype QueueFront(Queue* pq)
{
assert(pq);
assert(QueueEmpty(pq));
return pq->head->data;
}
//返回队列的最后一个元素
QDatatype QueueBack(Queue* pq)
{
assert(pq);
assert(QueueEmpty(pq));
return pq->tail->data;
}
源文件(用于测试) text.c
#define _CRT_SECURE_NO_WARNINGS 1
#include "Queue.h"
void test1()
{
Queue pq;
//修改结构体只需要传结构体的地址
QueueInit(&pq);
QueuePush(&pq, 1);
QueuePush(&pq, 2);
QueuePush(&pq, 3);
QueuePush(&pq, 4);
QueuePush(&pq, 5);
while (QueueEmpty(&pq))
{
printf("%d ", QueueFront(&pq));
QueuePop(&pq);
}
//printf("\n%d", QueueSize(&pq));
}
int main()
{
test1();
return 0;
}