深入了解栈和队列

小伙伴们,今天我们来继续学习数据结构的第二部分内容,就是栈和队列了。那么栈和队列有什么用呢?

栈和队列是两种重要的线性结构。从数据结构的角度看,栈和队列也是线性表,其特殊性在于栈和队列的基本操作是线性表操作的子集,它们是操作受限的线性表,因此,可称为具有限定性的数据结构。但从数据类型角度看,它们是和线性表不同的两类重要的抽象数据类型。

首先,我们先来学习栈

1.栈

1.1栈的概念及结构
栈:一种特殊的线性表,其只允许在固定的一端进行插入和删除元素操作。 进行数据插入和删除操作的一端
称为栈顶,另一端称为栈底。 栈中的数据元素遵守 后进先出 LIFO Last In First Out )的原则。
压栈:栈的插入操作叫做进栈 / 压栈 / 入栈, 入数据在栈顶
出栈:栈的删除操作叫做出栈。 出数据也在栈顶
1.2栈的实现
栈的实现一般可以使用 数组或者链表实现 ,相对而言数组的结构实现更优一些。因为数组在尾上插入数据的代价比较小。

单链表形式

 还是像链表一样,创建3个文件,一个头文件,两个.c文件。我们通过数组来实现栈的创建。

首先我们要想的一个问题是初始化时top是为0还是-1?(不要把top理解为数组下标)

 先来看一下静态的栈

// 下面是定长的静态栈的结构,实际中一般不实用,所以我们主要实现下面的支持动态增长的栈
typedef int STDataType;
#define N 10
typedef struct Stack
{
     STDataType _a[N];
     int _top; // 栈顶
}Stack;

静态的栈一般很少用到,所以我们对动态的栈进行书写

stack.h

#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 STInit(ST* pst);
//销毁
void STDestroy(ST* pst);
//入栈
void STPush(ST* pst, STDataType x);
//出栈
void STPop(ST* pst);
//检查是否为空
bool STEmpty(ST* pst);
//获取有效元素个数
int STSize(ST* pst);
//获取栈顶元素
STDataType STTop(ST* pst);

stack.c

#define _CRT_SECURE_NO_WARNINGS 1
#include"stack.h"

//初始化
void STInit(ST* pst)
{
	assert(pst);
	pst->a = NULL;
	pst->top = 0;//0时是指向栈顶数据的下一个位置
	//pst->top = -1;//-1时是指向栈顶数据
	pst->capacity = 0;
}

//销毁
void STDestroy(ST* pst)
{
	assert(pst);

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

//入栈
void STPush(ST* pst, STDataType x)
{
	if (pst->top == pst->capacity)
	{
		int newcapacity = pst->capacity == 0 ? 4 : pst->capacity * 2;
		STDataType* tmp = (STDataType*)realloc(pst->a, newcapacity * sizeof(STDataType));
		if (tmp == NULL)
		{
			perror("realloc fail");
			return;
		}
		pst->a = tmp;
		pst->capacity = newcapacity;
	}
	pst->a[pst->top] = x;
	pst->top++;
}

//出栈
void STPop(ST* pst)
{
	assert(pst);
    //如果栈为空时,是true,再取非,说明栈内没有元素,无法出栈就会报错
	assert(!STEmpty(pst));
	pst->top--;
}

//获取栈顶元素
STDataType STTop(ST* pst)
{
	assert(pst);
	assert(!STEmpty(pst));

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

//检查是否为空
bool STEmpty(ST* pst)
{
	assert(pst);
	//方法1
	/*if (pst->top == 0)
	{
		return true;
	}
	else
	{
		return false;
	}*/

	//方法2
	return pst->top == 0; //等于0返回true,不等于返回false
}

//获取有效元素个数
int STSize(ST* pst)
{
	assert(pst);
	return pst->top;
}



test.c

#define _CRT_SECURE_NO_WARNINGS 1
#include"stack.h"
#include<stdio.h>
void TestStack()
{
	ST st;
	STInit(&st);
	STPush(&st, 1);
	STPush(&st, 2);
	STPush(&st, 3);
	STPush(&st, 4);
	while (!STEmpty(&st))
	{
		printf("%d ", STTop(&st));
		STPop(&st);
	}

	STDestroy(&st);
}

int main()
{
	TestStack();

	return 0;
}

2.队列

2.1 队列的结构及概念
队列:只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表,队列具有先进先出
FIFO(First In First Out) 入队列:进行插入操作的一端称为 队尾 出队列:进行删除操作的一端称为 队头
2.2 队列的实现

我们这次用单链表来实现队列的实现,当然也可以通过数组来实现。

我们可以将链表的“头节点”和“尾节点”分别视为“队首”和“队尾”,规定队尾仅可添加
节点,队首仅可删除节点。(图片来源:hello算法
链接: hello‑algo.com
首先我们要先定义队列,再定义队列的结构。
//链式结构:表示队列
typedef int QDataType;
typedef struct QueueNode
{
	struct QueueNode* next;
	QDataType data;
}QNode;

//队列的结构
typedef struct Queue
{
	QNode* phead;
	QNode* ptail;
	int size;
}Queue;

另外我们不需要对开辟节点重新写一个函数,因为只需要插入才会开辟新的节点,所以可以直接在插入队列的函数里面开辟节点。

Queue.h

#pragma once

#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>

typedef int QDataType;
typedef struct QueueNode
{
	struct QueueNode* next;
	QDataType data;
}QNode;

typedef struct Queue
{
	QNode* phead;
	QNode* ptail;
	int size;
}Queue;

//初始化
void QueueInit(Queue* pq);
//销毁
void QueueDestroy(Queue* pq);
//插入队列
void QueuePush(Queue* pq, QDataType x);
//出队列
void QueuePop(Queue* pq);
//寻找队头
QDataType QueueFront(Queue* pq);
//寻找队尾
QDataType QueueBack(Queue* pq);
//计算个数
int QueueSize(Queue* pq);
//检查队列是否为空
bool QueueEmpty(Queue* pq);

Queue.c

#define _CRT_SECURE_NO_WARNINGS 1
#include"Queue.h"

//初始化
void QueueInit(Queue* pq)
{
	assert(pq);
	pq->phead = NULL;
	pq->ptail = NULL;
	pq->size = 0;
}

//销毁
void QueueDestroy(Queue* pq)
{
	assert(pq);
	QNode* cur = pq->phead;
	while (cur)
	{
		QNode* next = cur->next;
		free(cur);
		cur = next;
	}
	pq->phead = pq->ptail = NULL;
	pq->size = 0;
}

//插入队列
void QueuePush(Queue* pq, QDataType x)
{
	assert(pq);
	QNode* newnode = (QNode*)malloc(sizeof(QNode));

	if (newnode == NULL)
	{
		perror("malloc fail");
	}
	newnode->data = x;
	newnode->next = NULL;

	if (pq->ptail == NULL)
	{
		assert(pq->phead == NULL);
		pq->phead = pq->ptail = newnode;
	}
	else
	{
		pq->ptail->next = newnode;
		pq->ptail = newnode;
	}
	pq->size++;

}

//出队列
void QueuePop(Queue* pq)
{
	assert(pq);
	assert(!QueueEmpty(pq));

	//分为一个节点和多个节点
	if (pq->phead->next == NULL)
	{
		free(pq->phead);
		pq->phead = pq->ptail == NULL;
	}
	else
	{
		QNode* next = pq->phead->next;
		free(pq->phead);
		pq->phead = next;
	}
	pq->size--;
}

//寻找队头
QDataType QueueFront(Queue* pq)
{
	assert(pq);
	assert(!QueueEmpty(pq));

	return pq->phead->data;
}

//寻找队尾
QDataType QueueBack(Queue* pq)
{
	assert(pq);
	assert(!QueueEmpty(pq));

	return pq->ptail->data;
}

//计算个数
int QueueSize(Queue* pq)
{
	assert(pq);
	return pq->size;
}

//检查队列是否为空
bool QueueEmpty(Queue* pq)
{
	assert(pq);
	return pq->size == 0;
}

tect.c

void TestQueue()
{
	Queue q;
	QueueInit(&q);
	QueuePush(&q, 1);
	QueuePush(&q, 2);
	QueuePush(&q, 3);
	QueuePush(&q, 4);
	while (!QueueEmpty(&q))
	{
		printf("%d ", QueueFront(&q));
		QueuePop(&q);
	}
	printf("\n");
	QueueDestroy(&q);
}

好了,小伙伴们,这次我们对栈和队列先学到这里,以后我会继续补充一些知识点在这里,一定要记得常回来看看。

  • 20
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是栈的基本操作实现代码,使用顺序结构表示栈: ```c #include <stdio.h> #define MAX_SIZE 100 typedef struct { int data[MAX_SIZE]; int top; // 栈顶指针,指向栈顶元素 } Stack; void initStack(Stack* s) { s->top = -1; // 初始化栈顶指针为-1,表示栈为空 } void push(Stack* s, int x) { if (s->top == MAX_SIZE - 1) { printf("栈已满,无法入栈\n"); return; } s->top++; // 栈顶指针加1 s->data[s->top] = x; // 入栈 } int pop(Stack* s) { if (s->top == -1) { printf("栈已空,无法出栈\n"); return 0; // 出错返回0 } int x = s->data[s->top]; // 取出栈顶元素 s->top--; // 栈顶指针减1 return x; } int main() { Stack s; initStack(&s); push(&s, 1); push(&s, 2); push(&s, 3); printf("%d\n", pop(&s)); // 3 printf("%d\n", pop(&s)); // 2 printf("%d\n", pop(&s)); // 1 printf("%d\n", pop(&s)); // 栈已空,无法出栈,返回0 return 0; } ``` 以下是队列的基本操作实现代码,使用顺序结构表示队列: ```c #include <stdio.h> #define MAX_SIZE 100 typedef struct { int data[MAX_SIZE]; int head; // 队头指针,指向队头元素 int tail; // 队尾指针,指向队尾元素的下一个位置 } Queue; void initQueue(Queue* q) { q->head = q->tail = 0; // 初始化队头队尾指针为0 } void enQueue(Queue* q, int x) { if ((q->tail + 1) % MAX_SIZE == q->head) { printf("队列已满,无法入队\n"); return; } q->data[q->tail] = x; // 入队 q->tail = (q->tail + 1) % MAX_SIZE; // 队尾指针加1,需要取模 } int deQueue(Queue* q) { if (q->head == q->tail) { printf("队列已空,无法出队\n"); return 0; // 出错返回0 } int x = q->data[q->head]; // 取出队头元素 q->head = (q->head + 1) % MAX_SIZE; // 队头指针加1,需要取模 return x; } int main() { Queue q; initQueue(&q); enQueue(&q, 1); enQueue(&q, 2); enQueue(&q, 3); printf("%d\n", deQueue(&q)); // 1 printf("%d\n", deQueue(&q)); // 2 printf("%d\n", deQueue(&q)); // 3 printf("%d\n", deQueue(&q)); // 队列已空,无法出队,返回0 return 0; } ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值