【数据结构与算法】数据结构初阶:栈和队列

一、栈

概念:栈是一种特殊的线性表,只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端称为栈顶,另一端称为栈底。栈中的数据元素遵守后进先出的原则 

压栈:栈的插入操作叫做进栈/压栈/入栈,入数据在栈顶。

出栈:栈的删除操作叫做出栈。

注:出数据也在栈顶。

 栈的逻辑结构是线性的,物理结构因种类而决定。

这里我们是用数组来实现栈

栈结构的实现

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

typedef int STDatatype;
typedef struct Stack {
	STDatatype* arr;
	int top;//指向栈顶的数据
	int capacity;//栈的容量
}ST;

初始化

void STInit(ST* ps)
{
	assert(ps);
	ps->arr = NULL;
	ps->top = ps->capacity = 0;
}

入栈--栈顶

因为栈插入数据的函数不多,所以就不单独封装增容函数了

void StackPush(ST* ps, STDatatype x)
{
	//要插入数据,首先容量够不够
	if (ps->top ==ps->capacity)
	{
		//增容
		int newcapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;
		STDatatype* tmp = (STDatatype*)realloc(ps->arr, sizeof(STDatatype) * newcapacity);
		if (tmp == NULL)
		{
			perror("realloc failed");
			exit(1);
		}
		ps->arr = tmp;
		ps->capacity = newcapacity;
	}
	ps->arr[ps->top++] = x;


}

出栈---栈顶(后入先出)

要有提前判空:

判空

bool STEmpty(ST* ps)
{
	assert(ps);
	return ps->top==0;
}
void StackPop(ST* ps)
{
	assert(!STEmpty(ps));
	ps->top--;
}

取栈顶元素(只取不删)

STDatatype StackTop(ST* ps)
{
	assert(!STEmpty(ps));
	return ps->arr[ps->top - 1];
}

获取栈的有效的元素个数

int STSize(ST* ps)
{
	assert(ps);
	return ps->top;
}

销毁

void STDestory(ST* ps)
{
	assert(ps);
	if (ps->arr != NULL)
		free(ps->arr);
	ps->arr = NULL;
	ps->top = ps->capacity = 0;

}

最后送一个栈的打印

void STPrint(ST* ps)
{
	assert(ps && !STEmpty(ps));
	/*while (!STEmpty(ps))
	{
		int top = StackTop(ps);
		StackPop(ps);
		printf("%d ", top);
	}*/
	for (int i = ps->top - 1; i >= 0; i--)
	{
		printf("%d ", ps->arr[i]);
	}
	printf("\n");
}

注释掉的代码也是打印,更体现后入先出的逻辑,但是用一次栈的结构就会被破坏

所以小编这里还是用for循环来实现

       测试代码和展示

#include"Stack.h"
void test01()
{
	ST st;
	STInit(&st);
	StackPush(&st, 1);
	StackPush(&st, 2);
	StackPush(&st, 3);
	StackPush(&st, 4);
	StackPush(&st, 5);
	StackPush(&st, 6);
	int size = STSize(&st);
	printf("size:%d\n", size);
	STPrint(&st);
	STDestory(&st);
}
int main()
{
	test01();
	return 0;
}

二、队列

(一)队列的概念和结构

1、概念

概念:只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表,队列具有先进先出

 

       入队列:进行插入操作的一端是队尾

        出队列:进行删除操作的一端是队头

这里我们用单链表来实现队列,比较如下

1.用数组来实现,插入数据,时间复杂度O(1),删除数据,时间复杂度O(n)

2.用链表来实现,插入数据,时间复杂度O(n),删除数据,时间复杂度O(1)

3.虽然双向链表可以做到二者都为O(1),但结构复杂开辟的内存多,

4.我们可以在单链表的基础上定义一个ptail指针就能做到相同的效果进行优化

5.数组删除数据是要全部前移,不前移又会影响后面插入数据,

综上用单链表来实现

队列结构的实现

先实现结点的结构,再实现队列的结构


typedef int QDatatype;
//队列结点的结构
typedef struct Queuenode {
	QDatatype data;
	struct Queuenode* next;
}Queuenode;
//队列的结构
typedef struct Queue {
	Queuenode* phead;
	Queuenode* ptail;
}Queue;

初始化

void QueueInit(Queue* ps)
{
	assert(ps);
	ps->phead = ps->ptail = NULL;
}

判空

bool QueueEmpty(Queue* ps)
{
	assert(ps);
	return ps->phead == NULL;
}

入队----队尾


void QueuePush(Queue* ps,QDatatype x)
{
	assert(ps);
	//申请结点
	Queuenode* newnode = (Queuenode*)malloc(sizeof(Queuenode));
	newnode->data = x;
	newnode->next = NULL;//新插入的结点下一个指针默认指向null
	//队列为空
	if (ps->phead == NULL)
	{
		ps->phead = ps->ptail = newnode;
	}
	else {
		//队列不为空
		ps->ptail->next = newnode;
		ps->ptail = newnode;
	}
}

入队要注意两种情况

1.队列为空

2.队列不为空

出队-----队头

void QueuePop(Queue* ps)
{
	assert(ps && !QueueEmpty(ps));
	//如果队列只有一个结点
	if (ps->phead == ps->ptail)
	{
		free(ps->phead);
		ps->phead = ps->ptail = NULL;
	}
	else {
		Queuenode* next = ps->phead->next;
		free(ps->phead);
		ps->phead = next;
	}
	
}

出数据,要先确认队列不能为空,两种情况

1.只有一个结点

2.其他情况

分开处理防止对空指针的解引用,详细见上代码

取对头数据

QDatatype QueueFront(Queue* ps)
{
	assert(ps && !QueueEmpty(ps));
	return ps->phead->data;
}

取队尾数据

QDatatype QueueBack(Queue* ps)
{
	assert(ps && !QueueEmpty(ps));
	return ps->ptail->data;
}

取队列中有效的元素

int QueueSize(Queue* ps)
{
	assert(ps);
	Queuenode* pcur = ps->phead;
		int size = 0;
	while (pcur)
	{
		size++;
		pcur = pcur->next;
	}
	return size;
}

遍历队列,然后size计数器,返回size

销毁

void QueueDestory(Queue* ps)
{
	assert(ps);
	Queuenode* pcur = ps->phead;
	while (pcur)
	{
		Queuenode* next = pcur->next;
		free(pcur);
		pcur = next;
	}
	ps->phead = ps->ptail = NULL;
}

销毁要遍历队列,就是遍历链表,要注意在free结点时,要提前保存其下一个结点,进行循环销毁

测试代码和展示

#include"Queue.h"
void test01()
{
	Queue q;
	QueueInit(&q);
	QueuePush(&q, 1);
	QueuePush(&q, 2);
	QueuePush(&q, 3);
	QueuePush(&q, 4);
	int x = QueueFront(&q);//获取队首元素
	printf("x=%d\n", x);
	int y = QueueBack(&q);//获取队尾元素
	printf("y=%d\n", y);
	//队列
	QueueDestory(&q);//销毁队列
}
int main()
{
	test01();
	return 0;
}

结尾:

今天的内容就到这里了,希望大家给个三连支持一下,后续会继续更新,感谢阅读!

【封面】

 

 

 

 

 

评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值