一、何谓栈
1.栈的概念
栈是一种基本的数据结构,一般通过顺序表的结构实现,所以栈在物理和逻辑上都是连续的,但它又不同于顺序表,其只允许数据在的一端进行插入和删除,这一端称为栈顶,另一端则称为栈底,插入操作叫做压栈,删除叫做出栈。无论压栈还是出栈,入/出的数据都在栈顶。
因此,栈内存储的数据具有后进先出(last in first out)的特点。
2.后进先出
形象地说,后进先出类似于往弹夹里压子弹,先进去的子弹会被压到后面,而后进去的子弹则在前面,在射击时后进去的子弹会被先打出去。
图解:
可以看到,入栈时顺序为1、2,而出栈时则是先出2,再出1,这就是后进先出
但是在实际操作时能够实现边入边出,即入栈时顺序为1、2,出栈时顺序也可能为1、2
因此,入栈顺序为1,而出栈顺序可以有很多
3.栈的实现
stack.h
#pragma once
#include<stdlib.h>
#include<stdbool.h>
#include<assert.h>
#include<stdio.h>
typedef int STDataType;
typedef struct Stack
{
STDataType* arr;
int top;//栈中元素个数
int capacity;//栈容量
}Stack;
void InitStack(Stack* p);//初始化栈
void PushStack(Stack* p,STDataType x);//入栈
void PopStack(Stack* p);//出栈
STDataType TopStack(Stack* p);//取栈顶元素
int SizeStack(Stack* p);//获取栈中有效元素个数
bool EmptyStack(Stack* p);//判断栈是否为空
void DestroyStack(Stack* p);//销毁栈
stack.c
#define _CRT_SECURE_NO_WARNINGS 1
#include"stack.h"
int main()
{
Stack s;
InitStack(&s);//初始化栈
PushStack(&s,4);//入栈
PopStack(&s);//出栈
TopStack(&s);//取栈顶元素
SizeStack(&s);//获取栈中有效元素个数
EmptyStack(&s);//判断栈是否为空
DestroyStack(&s);//销毁栈
return 0;
}
//扩容函数
void Exp(Stack* p)
{
assert(p);
int new_capacity = p->capacity == 0 ? 4 : p->capacity*2;//三目运算,注意这里十分关键,是==,不是=
STDataType* tem = (STDataType*)realloc(p->arr, new_capacity * sizeof(STDataType));
if (tem==NULL)//对返回值判断
{
perror(realloc);
exit(1);
}
p->capacity = new_capacity;
p->arr = tem;
}
//初始化栈
void InitStack(Stack* p)
{
assert(p);
p->arr = NULL;
p->top = 0;
p->capacity = 0;
}
//入栈
void PushStack(Stack* p,STDataType x)
{
assert(p);
Exp(p);
p->arr[p->top] = x;//元素入栈顶
p->top++;
}
//出栈
void PopStack(Stack* p)
{
assert(p);
assert(p->top > 0);//有元素才能出
p->top--;
}
//取栈顶元素
STDataType TopStack(Stack* p)
{
assert(p);
assert(p->top > 0);
return p->arr[p->top-1];//top相当于size,而数组下标是从0开始的
}
//获取栈中有效元素个数
int SizeStack(Stack* p)
{
assert(p);
return p->top;
}
//判空
bool EmptyStack(Stack* p)
{
assert(p);
//确实栈中元素为0则为true,否则为false
return !p->top;
}
void DestroyStack(Stack* p)
{
assert(p);
if (p->arr)//arr非空指针时才存在释放问题
{
free(p->arr);//释放掉动态申请的内存
p->arr = NULL;//置空
p->capacity = p->top = 0;
}
}
二、何谓队列
1.队列的概念
队列也是一种基本的数据结构,线性表的一种,其特点是只允许数据在一端进入,而在另一端出去,进的一端叫做队尾,出的一端叫做队头。
因此其与栈不同,它的特点是先进先出(first in first out)其入队顺序确定了,那它的出队顺序也就确定了。即入队为1、2、3,则出队也必为1、2、3.
显然,我们不能通过数组顺序表实现队列,但是单链表可以,其尾插和头删与队列的入队和出队是相符合的。
图解:
2.代码:
Queue.h:
#pragma once
#include<assert.h>
#include<stdlib.h>
#include<stdio.h>
typedef int QDataType;
typedef struct QListNode
{
struct QListNode* next;
QDataType data;
}QNode;
//定义一个包含队头和队尾的结构体,此步目的是对队列进行入队和出队操作时,显然头尾指针需要改动,传址调用需要用到二级指针,此处就是为了避免二级指针麻烦
typedef struct Queue
{
QNode* front;//指向队头/头节点
QNode* rear;//指向尾节点/队尾
int size;//标记队列中元素个数
}Queue;
// 初始化队列
void QueueInit(Queue* q);
// 队尾入队列
void QueuePush(Queue* q, QDataType data);
// 队头出队列
void QueuePop(Queue* q);
// 获取队列头部元素
QDataType QueueFront(Queue* q);
// 获取队列队尾元素
QDataType QueueBack(Queue* q);
// 获取队列中有效元素个数
int QueueSize(Queue* q);
// 检测队列是否为空,如果为空返回非零结果,如果非空返回0
int QueueEmpty(Queue* q);
// 销毁队列
void QueueDestroy(Queue* q);
Queue.c
#include"Queue.h"
// 初始化队列
void QueueInit(Queue* q)
{
assert(q);
/*QNode* tem = (QNode*)malloc(sizeof(QNode));
if (tem == NULL)
{
perror("malloc");
return;
}*/
q->front = q->rear = NULL;
//q->front->next = NULL;
//q->front->data = 0;
q->size = 0;
}
//通过尾插在队尾入队列
void QueuePush(Queue* q,QDataType data)
{
assert(q);
QNode* tem = (QNode*)malloc(sizeof(QNode));
if (tem == NULL)
{
perror("malloc");
return;
}
if (q->front == NULL)//第一个节点
{
q->front = q->rear = tem;
q->front->data = data;
tem->next=NULL;//别忘了将tem的next置空
}
else//第二及以后节点
{
q->rear->next = tem;
q->rear = tem;
q->rear->next = NULL;
q->rear->data = data;
}
q->size++;
}
//通过头删实现在队头出队列
void QueuePop(Queue* q)
{
assert(q);
assert(q->front);//头指针不为空才存在删
QNode* tem = q->front;
q->front = q->front->next;
if (q->front == NULL)
q->rear = NULL;//避免尾指针变成野指针
free(tem);
q->size--;
}
//获取队列头部元素
QDataType QueueFront(Queue* q)
{
assert(q);
assert(q->front);
return q->front->data;
}
//获取队列尾部元素
QDataType QueueBack(Queue* q)
{
assert(q);
assert(q->rear);
return q->rear->data;
}
//获取队列中有效元素个数
int QueueSize(Queue* q)
{
assert(q);
return q->size;
}
// 检测队列是否为空,如果为空返回非零结果,如果非空返回0
int QueueEmpty(Queue* q)
{
if (q->size == 0)
return 1;
else
return 0;
}
//销毁队列
void QueueDestroy(Queue* q)
{
assert(q);
while (q->front)
{
QNode* tem = q->front;
free(tem);
q->front = q->front->next;
}
q->rear=NULL;
q->size=0;
}
test.c
写好咱们的队列后,调用几个接口测试一下功能
#include"Queue.h"
int main()
{
Queue* q=(Queue*)malloc(sizeof(Queue));
// 初始化队列
QueueInit(q);
QueuePush(q,1);
QueuePush(q,2);
QueuePush(q,3);
for (int i = 0; i < 3; i++)
{
printf("%d\n", QueueFront(q));
QueuePop(q);//每取一个队头元素,就删除一个
}
printf("%d", QueueSize(q));
return 0;
}
我们可以看到,入队时1、2、3,出队时也为1、2、3,这就是先进先出