目录
1.栈
1.1什么是栈
栈是一种特殊的线性表,其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端 称为栈顶,另一端称为栈底。栈中的数据元素遵守后进先出LIFO(Last In First Out)的原则。如图所示:
1.2栈的实现
如上图所示,栈的操作与顺序表的操作类似,所以我们可以采用顺序表来实现栈。
1.2.1栈类型的定义
typedef int TypeData ; //方便更改栈中存储的数据类型,增加代码的复用性
typedef struct Stack //使用顺序表实现栈
{
TypeData* a; //实现栈所开辟的动态数组
int top; //栈顶的位置
int capacity; //当前栈所能容纳的最大容量
}Stack;
1.2.2栈的初始化
void Init(Stack* pr)
{
assert(pr); //断言语句,如果括号中式子逻辑判断为假,就直接报错。
//这里断言pr,在C语言的逻辑判断中,0为假,1为真,
//所以在这个语句中只有pr==0也就是pr不为空,才不报错。
pr->top = 0;
pr->capacity = 10; //设置初始最大数据量
int* pa = (int*)malloc(sizeof(int) * pr->capacity); //动态开辟内存
if (pa == NULL)
{
perror("malloc"); //这句话就是报错语句
return;
}
pr->a = pa;
}
1.2.3入栈(Push)与出栈(Pop)
void push(Stack* pr,TypeData x)
{
assert(pr);
check(pr); //检查当前栈是否已满
pr->a[pr->top] = x; //在数组的末尾添加数据
pr->top++; //栈顶往后移动一个位置
}
这里入栈需要考虑扩容问题,所以这里我增加了一个函数“check”来进行检查和扩容。
void check(Stack* pr)
{
assert(pr);
if (pr->top == pr->capacity)
{
pr->capacity *= 2;
Stack* ptr = realloc(pr, sizeof(Stack) * pr->capacity);
if (ptr == NULL)
{
perror("realloc");
return;
}
pr = ptr;
}
}
void pop(Stack* pr)
{
assert(pr);
if(pr->top!=0) //如果栈为空,就不能进行出栈操作
pr->top--;
}
1.2.4返回栈顶元素与判空
TypeData top(Stack* pr)
{
assert(pr);
if (top != 0) //如果栈为空,就不能返回元素。
return pr->a[pr->top - 1];
}
int is_empty(Stack* pr)
{
if (pr->top == 0) //如果栈顶在0位置,说明还没有进行过Push操作,所以栈为空
return 1;
else
return 0;
}
1.2.5栈的销毁
void destory(Stack* pr)
{
assert(pr);
free(pr->a);
pr->capacity = 0;
pr->top = 0;
free(pr);
}
2.队列
2.1什么是队列
队列只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表,队列具有先进先出 FIFO(First In First Out) 入队列:进行插入操作的一端称为队尾 出队列:进行删除操作的一端称为队头。
2.2队列的实现
这里我们可以看到,入队是在队尾操作,出队在队头操作。这和链表的尾插和头删操作类似,所以我们可以使用单链表进行实现队列。
2.2.1队列的定义
typedef int TypeData; //方便更改数据类型增加复用性
typedef struct Que //栈的定义
{
struct Que* next; //指针指向下一个数据
TypeData data; //存储的数据
}Que;
typedef struct Queue
{
struct Que* head; //头指针,方便头删操作
struct Que* tail; //尾指针,方便尾删操作
int size; //记录数据个数
}Queue;
2.2.2队列的初始化
void Init(Queue* pr)
{
assert(pr);
pr->size = 0;
Que* ptr = (Que*)malloc(sizeof(Que)); //创建哨兵位,方便头删。
if (ptr == NULL)
{
perror("malloc");
return;
}
ptr->next = NULL;
pr->head = pr->tail = ptr; //让头指针和尾指针都指向哨兵位。
}
2.2.3入队(Push)和出队(Pop)
void push(Queue* pr, TypeData x)
{
assert(pr);
Que* ptr = (Que*)malloc(sizeof(Que)); //创建节点进行数据存储
if (ptr == NULL)
{
perror("Push:malloc");
return;
}
ptr->data = x; //存储数据
ptr->next = NULL; //初始化下一个指针为空
pr->tail->next = ptr; //将该节点连接到队尾
pr->tail = ptr; //尾指针向后更新位置
pr->size++; //数据个数加一
}
TypeData pop(Queue* pr)
{
assert(pr);
if (pr->size == 0) //如果队列为空,不执行Pop操作
return 0; //因为这里不能报错,所以随便返回一个数字
TypeData tmp = pr->head->next->data; //哨兵位的下一位就是队头,获取队头数据
if (pr->head->next == pr->tail) //如果只有一个数据,单独处理,否则tail会变成野指针
{
free(pr->tail);
pr->tail = pr->head;
}
else
{
Que* ptr = pr->head->next->next; //保存头节点的下一个节点的地址
free(pr->head->next); //头删
pr->head->next = ptr; //更新队头的位置
}
pr->size--; //数据个数减一
return tmp; //返回对头数据
}
2.2.4获取队头数据与判空
TypeData peek(Queue* pr)
{
if(pr->size!=0) //如果队列为空,就不能返回数字
return pr->head->next->data;
}
int is_Empty(Queue* pr)
{
return (pr->size == 0);
}
2.2.5队列的销毁
void destory(Queue* pr)
{
assert(pr);
while (pr->head != pr->tail) //从头开始,挨个释放
{
Que* ptr = pr->head->next;
free(pr->head);
pr->head = ptr;
}
free(pr->head);
}
3.总结
栈和队列到这里就介绍完啦,有什么问题欢迎评论区讨论呀。