数据结构:栈和队列

一、何谓栈

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,这就是先进先出

评论 33
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

好想当只懒羊羊

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值