【数据结构】栈和队列

1.栈

栈是数据结构中的一种线性结构,栈的出入数据只能从一端进行,所以栈的出入规律是先进后出(FILO, First In Last Out )。

栈的概念模型:

先进后出规律模型:

栈的应用:

  • 算法上的递归应用
  • 游戏上的多开界面,比如打开背包之后,又可以打开装备属性的界面,但是这时候背包界面 却没有关闭。

栈的实现

栈创建分析:

栈是一种只有一端出口,并且先进后出的数据结构,实现方面我们可以通过数组或者链表进行实现,他们两种实现各有优劣,小编这边使用的是数组实现,当然为了更加动态的表示栈,我们这边使用动态数组实现。

cpp 复制代码
#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <stdbool.h>

// 类型声明
typedef int STDataType;


typedef struct Stack
{
	STDataType* a;	// 动态数组
	int top;		// 栈顶
	int capacity;	// 表示空间容量
}ST;

我们需要实现的功能接口如下:

cpp 复制代码
// 栈的初始化
void StackInit(ST* pst);

// 入栈
void StackPush(ST* pst, STDataType x);

// 出栈
void StackPop(ST* pst);

// 查看栈顶元素
STDataType StackTop(ST* pst);

// 计算栈的元素个数
int StackSize(ST* pst);

// 判空
bool StackEmpty(ST* pst);

// 栈的销毁
void StackDestory(ST* pst);

栈的初始化

我们这边实现栈的初始化有一个分歧,就是我们的top初始化什么?如果初始化为0,那么top代表的就是栈的元素个数,初始化为-1,那么top代表的就是动态数组的下标。

我这边为了方便后续判断栈的满我的top就初始化为0。

代码实现:

cpp 复制代码
// 栈的初始化
void StackInit(ST* pst)
{
	// 判空
	assert(pst);

	// 初始化
	pst->a = NULL;
	pst->top = 0;
	pst->capacity = 0;
}

栈的入栈

栈为空时我们入栈的概念图,但是我们需要判断栈是否为满,如果满了就重新分配空间,没满就直接插入。

代码实现:

cpp 复制代码
// 入栈
void StackPush(ST* pst, STDataType x)
{
	// 判空
	assert(pst);

	// 判栈的满
	// 我这边定义的top为0,所以代表的是数据的个数,当个数和容量相等时为满
	if (pst->top == pst->capacity)
	{
		// 三目操作符
		// 判断原空间是否为0,为0则先分配4个空间,不为0则开辟两倍空间。
		int newcapacity = pst->capacity == 0 ? 4 : pst->capacity * 2;

		// 动态分配空间
		STDataType* tmp = (STDataType*)malloc(newcapacity * sizeof(STDataType));
		// 判断是否申请成功
		if (NULL == tmp)
		{
			perror("StackPush()::malloc fail");
			return 1;
		}
			pst->a = tmp;
			pst->capacity = newcapacity;
	}

	// 入栈
	pst->a[pst->top] = x;
	pst->top++;
}

栈的出栈

栈的出栈实现很简单,我们只需要将top--就行了,这样动态输入就访问不到原栈顶元素了,但是我们还是需要注意如果栈内的元素为0,我们是不可以执行出栈操作的。

代码实现:

cpp 复制代码
// 出栈
void StackPop(ST* pst)
{
	// 判空
	assert(pst);
	// 栈内数据不可以为0
	assert(pst->top > 0);

	// 出栈
	pst->top--;
}

查看栈顶元素

每次查看栈顶元素需要我们访问最上层数据,在每次访问后需要记录下来返回,过程中top代表的是数据个数,返回下标需要减1。

代码实现:

cpp 复制代码
// 查看栈顶元素
STDataType StackTop(ST* pst)
{
	// 判空
	assert(pst);
	// 栈内数据不可以为0
	assert(pst->top > 0);

	return pst->a[pst->top - 1];
}

计算栈的元素个数

由于我们前边的top定义是0,top代表的是栈内数据的个数,我们直接返回top即可。

代码实现:

cpp 复制代码
// 计算栈的元素个数
int StackSize(ST* pst)
{
	// 判空
	assert(pst);

	return pst->top;
}

判空

判空也很简单我们只需要验证top是否为0即可。

代码实现:

cpp 复制代码
// 判空
bool StackEmpty(ST* pst)
{
	// 判空
	assert(pst);

	return pst->top == 0;
}

栈的销毁

由于我们的栈内数据是由动态内存申请的,所以我们最后需要释放动态内存在堆区申请的空间,防止空间泄露。

cpp 复制代码
// 栈的销毁
void StackDestory(ST* pst)
{
	// 判空
	assert(pst);

	free(pst->a);
	pst->a = NULL;
	pst->capacity = pst->top = 0;
}

栈整个代码实现和测试文件:

Stack.h

cpp 复制代码
#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <stdbool.h>

// 类型声明
typedef int STDataType;


typedef struct Stack
{
	STDataType* a;	// 动态数组
	int top;		// 栈顶
	int capacity;	// 表示空间容量
}ST;

// 栈的初始化
void StackInit(ST* pst);

// 栈的销毁
void StackDestory(ST* pst);

// 入栈
void StackPush(ST* pst, STDataType x);

// 出栈
void StackPop(ST* pst);

// 查看栈顶元素
STDataType StackTop(ST* pst);

// 计算栈的元素个数
int StackSize(ST* pst);

// 判空
bool StackEmpty(ST* pst);

Stack.c

cpp 复制代码
#include "Stack.h"

// 栈的初始化
void StackInit(ST* pst)
{
	// 判空
	assert(pst);

	// 初始化
	pst->a = NULL;
	pst->top = 0;
	pst->capacity = 0;
}

// 入栈
void StackPush(ST* pst, STDataType x)
{
	// 判空
	assert(pst);

	// 判栈的满
	// 我这边定义的top为0,所以代表的是数据的个数,当个数和容量相等时为满
	if (pst->top == pst->capacity)
	{
		// 三目操作符
		// 判断原空间是否为0,为0则先分配4个空间,不为0则开辟两倍空间。
		int newcapacity = pst->capacity == 0 ? 4 : pst->capacity * 2;

		// 动态分配空间
		STDataType* tmp = (STDataType*)malloc(newcapacity * sizeof(STDataType));
		// 判断是否申请成功
		if (NULL == tmp)
		{
			perror("StackPush()::malloc fail");
			return 1;
		}
			pst->a = tmp;
			pst->capacity = newcapacity;
	}

	// 入栈
	pst->a[pst->top] = x;
	pst->top++;
}

// 出栈
void StackPop(ST* pst)
{
	// 判空
	assert(pst);
	// 栈内数据不可以为0
	assert(pst->top > 0);

	// 出栈
	pst->top--;
}

// 查看栈顶元素
STDataType StackTop(ST* pst)
{
	// 判空
	assert(pst);
	// 栈内数据不可以为0
	assert(pst->top > 0);

	return pst->a[pst->top - 1];
}

// 计算栈的元素个数
int StackSize(ST* pst)
{
	// 判空
	assert(pst);

	return pst->top;
}

// 判空
bool StackEmpty(ST* pst)
{
	// 判空
	assert(pst);

	return pst->top == 0;
}

// 栈的销毁
void StackDestory(ST* pst)
{
	// 判空
	assert(pst);

	free(pst->a);
	pst->a = NULL;
	pst->capacity = pst->top = 0;
}

Text.c

cpp 复制代码
#include "Stack.h"

int main()
{
	ST s1;

	// 栈的初始化
	StackInit(&s1);

	// 入栈
	StackPush(&s1, 1);
	StackPush(&s1, 2);
	StackPush(&s1, 3);
	StackPush(&s1, 4);

	// 出栈
	/*printf("%d ", StackTop(&s1));
	StackPop(&s1);
	printf("%d ", StackTop(&s1));
	StackPop(&s1);
	printf("%d ", StackTop(&s1));
	StackPop(&s1);
	printf("%d ", StackTop(&s1));*/
	// 报错
	/*StackPop(&s1);
	printf("%d ", StackTop(&s1));*/

	// 如果栈的元素不为0,则打印,然后删除
	/*while (!StackEmpty(&s1))
	{
		printf("%d ", StackTop(&s1));
		StackPop(&s1);
	}*/

	// 计算栈的元素个数
	int ret = StackSize(&s1);
	printf("栈的数据个数为:%d", ret);

	// 栈的销毁
	StackDestory(&s1);

	return 0;
}

2.队列

队列是数据结构中的一种线性结构,队列的出入数据只能从队尾进行,出数据只能从队首进行,所以队列的出入规律是先进先出(FIFO, First In First Out )。

队列的概念模型:

先进先出的规律:

队列的数据是由一端对尾进入,队首出去,所以是先进先出的规律。

栈的应用:

  • 我们平时点奶茶时会在小程序上获得一个排序号,这就是通过队列实现的。
  • 为了保证公平性的抽号机器

队列的实现

队列创建分析:

同栈一样,队列也属于线性的数据结构,实现队列我们也可以通过数组或者链表来实现。

数组实现:

找首位数据很容易,但是入队列和出队列都要对整体的数据进行挪动太过于麻烦。

链表实现:

入队列和出队列十分方便,但是每次都需要找队尾,我们可以提前定义队尾指针解决这个问题。

综上我决定使用链表实现队列。

队列节点:

cpp 复制代码
#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <stdbool.h>

// 类型声明
typedef int QDataType;

// 每个队列数据的节点创建
typedef struct QueueNode
{
	QDataType data;		// 存放数据
	struct QueueNode* next; // 指向下一个节点的指针
}QueueNode;

为了方便使用头尾指针我们创建一个存放头尾指针的结构体,同时也具有避免传二级指针的担忧,然后再定义一个size用于记录数据的个数。

cpp 复制代码
// 存放头尾指针的结构体
typedef struct Queue
{
	QueueNode* head;
	QueueNode* tail;
    int size;
}Queue;

我们需要实现的功能接口如下:

cpp 复制代码
// 队列的初始化
void QueueInit(Queue* pq);

// 队列的插入
void QueuePush(Queue* pq, QDataType x);

// 队列的删除
void QueuePop(Queue* pq);

// 获取队列头部元素
QDataType QueueFront(Queue* pq);

// 获取队列队尾元素
QDataType QueueBack(Queue* pq);

// 获取队列中有效元素个数
int QueueSize(Queue* pq);

// 检测队列是否为空,如果为空返回非零结果,如果非空返回0
bool QueueEmpty(Queue* pq);

// 销毁队列
void QueueDestroy(Queue* pq);

队列的初始化

头尾指针指向NULL,将size定义为。

代码实现:

cpp 复制代码
// 队列的初始化
void QueueInit(Queue* pq)
{
	// 判空
	assert(pq);

	pq->head = pq->tail = NULL;
	pq->size = 0;
}

队列的插入

代码实现:

cpp 复制代码
// 队列的插入
void QueuePush(Queue* pq, QDataType x)
{
	// 判空
	assert(pq);

	// 插入
	QueueNode* newnode = (QueueNode*)malloc(sizeof(QueueNode));
	if (NULL == newnode)
	{
		preeor("QueuePush()::malloc fail");
		return 1;
	}

	newnode->next = NULL;
	newnode->data = x;

	// head为空
	if (pq->head == NULL)
	{
		pq->head = pq->tail = newnode;
	}
	else {
		pq->tail->next = newnode;
		pq->tail = newnode;
	}

	pq->size++;
}

队列的删除

代码实现:

cpp 复制代码
void QueuePop(Queue* pq)
{
	// 判空
	assert(pq);
	assert(pq->head != NULL);

	// 删除
	// 只有一个节点
	if (pq->head->next == NULL)
	{
		free(pq->head);
		pq->head = pq->tail = NULL;
	}
	else {
		Queue* next = pq->head->next;
		free(pq->head);
		pq->head = next;
	}

	pq->size--;
}

获取队列头部元素

代码实现:

cpp 复制代码
QDataType QueueFront(Queue* pq)
{
	// 判空
	assert(pq);
	assert(pq->head != NULL);

	return pq->head->data;
}

获取队列队尾元素

代码实现:

cpp 复制代码
QDataType QueueBack(Queue* pq)
{
	// 判空
	assert(pq);
	assert(pq->head != NULL);

	return pq->tail->data;
}

获取队列中有效元素个数

代码实现:

cpp 复制代码
int QueueSize(Queue* pq)
{
	// 判空
	assert(pq);

	return pq->size;
}

检测队列是否为空,如果为空返回非零结果,如果非空返回0

代码实现:

cpp 复制代码
bool QueueEmpty(Queue* pq)
{
	// 判空
	assert(pq);

	return pq->size == 0;
}

销毁队列

代码实现:

cpp 复制代码
void QueueDestroy(Queue* pq)
{
	QueueNode* cur = pq->head;
	while (cur)
	{
		QueueNode* next = cur->next;
		free(cur);
		cur = next;
	}

	pq->head = pq->tail = NULL;
	pq->size = 0;
}

队列整个代码实现和测试文件:

Queue.h

cpp 复制代码
#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <stdbool.h>

// 类型声明
typedef int QDataType;

// 每个队列数据的节点创建
typedef struct QueueNode
{
	QDataType data;		// 存放数据
	struct QueueNode* next; // 指向下一个节点的指针
}QueueNode;

// 存放头尾指针的结构体
typedef struct Queue
{
	QueueNode* head;
	QueueNode* tail;
	int size;
}Queue;

// 队列的初始化
void QueueInit(Queue* pq);

// 队列的插入
void QueuePush(Queue* pq, QDataType x);

// 队列的删除
void QueuePop(Queue* pq);

// 获取队列头部元素
QDataType QueueFront(Queue* pq);

// 获取队列队尾元素
QDataType QueueBack(Queue* pq);

// 获取队列中有效元素个数
int QueueSize(Queue* pq);

// 检测队列是否为空,如果为空返回非零结果,如果非空返回0
bool QueueEmpty(Queue* pq);

// 销毁队列
void QueueDestroy(Queue* pq);

Queue.c

cpp 复制代码
#include "Queue.h"

// 队列的初始化
void QueueInit(Queue* pq)
{
	// 判空
	assert(pq);

	pq->head = pq->tail = NULL;
	pq->size = 0;
}

// 队列的插入
void QueuePush(Queue* pq, QDataType x)
{
	// 判空
	assert(pq);

	// 插入
	QueueNode* newnode = (QueueNode*)malloc(sizeof(QueueNode));
	if (NULL == newnode)
	{
		preeor("QueuePush()::malloc fail");
		return 1;
	}

	newnode->next = NULL;
	newnode->data = x;

	// head为空
	if (pq->head == NULL)
	{
		pq->head = pq->tail = newnode;
	}
	else {
		pq->tail->next = newnode;
		pq->tail = newnode;
	}

	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 {
		Queue* next = pq->head->next;
		free(pq->head);
		pq->head = next;
	}

	pq->size--;
}

// 获取队列头部元素
QDataType QueueFront(Queue* pq)
{
	// 判空
	assert(pq);
	assert(pq->head != NULL);

	return pq->head->data;
}

// 获取队列队尾元素
QDataType QueueBack(Queue* pq)
{
	// 判空
	assert(pq);
	assert(pq->head != NULL);

	return pq->tail->data;
}

// 获取队列中有效元素个数
int QueueSize(Queue* pq)
{
	// 判空
	assert(pq);

	return pq->size;
}

// 检测队列是否为空,如果为空返回非零结果,如果非空返回0
bool QueueEmpty(Queue* pq)
{
	// 判空
	assert(pq);

	return pq->size == 0;
}

// 销毁队列
void QueueDestroy(Queue* pq)
{
	QueueNode* cur = pq->head;
	while (cur)
	{
		QueueNode* next = cur->next;
		free(cur);
		cur = next;
	}

	pq->head = pq->tail = NULL;
	pq->size = 0;
}

Text.c

cpp 复制代码
#include "Queue.h"


int main()
{
	Queue q;
	QueueInit(&q);
	QueuePush(&q, 1);
	QueuePush(&q, 2);

	printf("%d ", QueueFront(&q));
	QueuePop(&q);

	QueuePush(&q, 3);
	QueuePush(&q, 4);

	while (!QueueEmpty(&q))
	{
		printf("%d ", QueueFront(&q));
		QueuePop(&q);
	}

	QueueDestroy(&q);


	return 0;
}
相关推荐
passer__jw76721 分钟前
【LeetCode】【算法】283. 移动零
数据结构·算法·leetcode
爱吃生蚝的于勒1 小时前
深入学习指针(5)!!!!!!!!!!!!!!!
c语言·开发语言·数据结构·学习·计算机网络·算法
羊小猪~~1 小时前
数据结构C语言描述2(图文结合)--有头单链表,无头单链表(两种方法),链表反转、有序链表构建、排序等操作,考研可看
c语言·数据结构·c++·考研·算法·链表·visual studio
脉牛杂德2 小时前
多项式加法——C语言
数据结构·c++·算法
一直学习永不止步2 小时前
LeetCode题练习与总结:赎金信--383
java·数据结构·算法·leetcode·字符串·哈希表·计数
wheeldown10 小时前
【数据结构】选择排序
数据结构·算法·排序算法
躺不平的理查德14 小时前
数据结构-链表【chapter1】【c语言版】
c语言·开发语言·数据结构·链表·visual studio
阿洵Rain14 小时前
【C++】哈希
数据结构·c++·算法·list·哈希算法
Leo.yuan15 小时前
39页PDF | 华为数据架构建设交流材料(限免下载)
数据结构·华为
半夜不咋不困15 小时前
单链表OJ题(3):合并两个有序链表、链表分割、链表的回文结构
数据结构·链表