栈和队列(C语言实现)

目录

1.栈

1.1什么是栈

​1.2栈的实现

1.2.1栈类型的定义

1.2.2栈的初始化

1.2.3入栈(Push)与出栈(Pop)

1.2.4返回栈顶元素与判空

1.2.5栈的销毁

2.队列

2.1什么是队列

2.2队列的实现

         2.2.1队列的定义

2.2.2队列的初始化

2.2.3入队(Push)和出队(Pop)

2.2.4获取队头数据与判空

2.2.5队列的销毁

3.总结


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.总结

        栈和队列到这里就介绍完啦,有什么问题欢迎评论区讨论呀。

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值