数据结构初阶:栈与队列

1栈

1.1栈的定义

栈是限定仅在表尾进行插入和删除操作的线性表。

1.2栈的结构

把允许插入和删除操作的一端称为栈顶,另一端称为栈底。栈又称为后进先出的线性表,简称为LIFO结构。(Last In First Out)
在这里插入图片描述

1.3栈的结构体定义

和线性表一样,栈也分为顺序栈和链栈,顺序栈尾插尾删的效率都较高,而链栈如果用单链表实现,则尾删效率低,除非使用双向链表。这里我们采用顺序栈的方式实现栈的基本操作。

typedef struct stack
{
	STDataType* a;//指向一块数组空间
	int top;//记录当前栈顶的位置
	int capacity;//记录顺序栈的最大容量
}ST;

1.4顺序栈的实现

1.4.1顺序栈的接口函数

void StackInit(ST* ps);//初始化栈
void StackDestroy(ST* ps);//销毁栈
void StackPush(ST* ps, STDataType x);//入栈
void StackPop(ST* ps);//出栈
STDataType StackTop(ST* ps);//返回栈顶元素
bool StackEmpty(ST* ps);//判断栈是否为空
int StackSize(ST* ps);//返回栈中元素的个数

1.4.2初始化栈

void StackInit(ST* ps)
{
	assert(ps);
	ps->a = (STDataType*)malloc(sizeof(STDataType));
	ps->top = 0;
	ps->capacity = 0;
}

top和capacity初始化为0,其中top初始化为0,top指向栈顶元素的下一个位置,top也可初始化为-1,此时top指向当前的栈顶元素。

1.4.3栈的销毁

void StackDestroy(ST* ps)
{
	assert(ps);
	free(ps->a);
	ps->top = 0;
	ps->capacity = 0;
}

将malloc出的数组空间释放掉,top和capacity都置为0

1.4.4 入栈

void StackPush(ST* ps,STDataType x)
{
	assert(ps);
	if (ps->top == ps->capacity)//栈满,考虑扩容
	{
		int newcapacity=ps->capacity == 0 ? 4 : ps->capacity * 2;
		STDataType* tmp = (STDataType*)realloc(ps->a, newcapacity * sizeof(int));
		if (tmp == NULL)
		{
			printf("realloc fail\n");
			exit(-1);
		}
		ps->a = tmp;//重新指向扩容好的空间
		ps->capacity = newcapacity;//更新栈的最大容量
	}
	ps->a[ps->top] = x;//因为top指向的是栈顶元素的下一个,所以直接将top位置的值赋值为x
	ps->top++;//top++,继续指向栈顶元素的下一个

}

这里扩容和顺序表的扩容一样,需要单独考虑第一次扩容的问题,因为第一次capacity的值为0,如果扩大二倍还是0,所以第一次直接赋值为4。

1.4.5出栈

void StackPop(ST* ps)
{
	assert(ps);
	assert(!StackEmpty(ps));//栈不能为空
	ps->top--;
}

1.4.6 返回栈顶元素

STDataType StackTop(ST* ps)
{
	assert(ps);
	assert(!StackEmpty(ps));//栈不能为空
	return ps->a[ps->top - 1];
}

1.4.7判断是否为空

bool StackEmpty(ST* ps)
{
	assert(ps);
	return ps->top == 0;
}

top值若为0,则是空栈,bool值为true,反之则为false

1.4.8 栈中元素的个数

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

因为top指向栈顶元素的下一个位置,所以top的值即为当前栈中元素的个数

1.4.9测试顺序栈的函数

void test1()
{
	ST ps;
	StackInit(&ps);
StackPush(&ps,1);
StackPush(&ps, 2); 
StackPush(&ps, 3);//入栈1,2,3
printf("%d\n", StackTop(&ps));//返回栈顶元素
StackPop(&ps);//出栈
StackPush(&ps, 4);
StackPush(&ps, 5);//入栈4,5
printf("%d\n", StackSize(&ps));//栈中元素的个数
while (!StackEmpty(&ps))//依次打印栈中的元素
{
	printf("%d ", StackTop(&ps));
	StackPop(&ps);
}
StackDestroy(&ps);//销毁栈
}

由于对栈的操作都只能在栈顶进行,所以要依次打印栈中的元素,应用循环(只要栈不为空就一直打印),不断返回栈顶元素,并将栈顶元素出栈。
运行结果
在这里插入图片描述

2队列

2.1队列的定义

队列是只允许在一端进行插入操作,而在另一端进行删除操作的线性表。

2.2队列的结构

队列是一种先进先出的线性表(First In First Out),允许插入的一端称为队尾,允许删除的一端称为队头。
在这里插入图片描述

2.3队列的结构体定义

typedef struct QNode
{
	struct QNode* next;
	QDataType data;
}QNode;
typedef struct Queue
{
	QNode* head;
	QNode* tail;
}Queue;//头指针和尾指针封装成一个结构体

队列用链式结构实现,因为只能在队头删除,队尾插入,所以应定义两个指针指向单链表,一个实现删除操作,一个实现插入操作。同时将两个指针封装成一个新的结构体,可以避免在接口函数传二级指针。
在这里插入图片描述

2.4队列的实现

2.4.1队列的接口函数

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);//返回队尾元素
bool QueueEmpty(Queue* pq);//判断队列是否为空
int QueueSize(Queue* pq);//返回队列中的元素个数

2.4.2队列的初始化

void QueueInit(Queue* pq)
{
	assert(pq);//pq为指向Queue结构体的指针,一定不为空
	pq->head = NULL;
	pq->tail = NULL;

}

尾指针和头指针都先置空

2.4.3队列的销毁

void QueueDestroy(Queue* pq)
{
	assert(pq);
	QNode* cur = pq->head;
	while (cur)//依次free掉链表的每个结点
	{
		QNode* next = cur->next;
		free(cur);
		cur = next;
	}
	pq->head = pq->tail = NULL;
}

释放链表中的结点应注意先保存链表下个位置的结点,然后再free,最后头指针和尾指针置NULL

2.4.4入队

void QueuePush(Queue* pq, QDataType x)
{
	assert(pq);
	QNode* newnode = (QNode*)malloc(sizeof(QNode));
	if (newnode == NULL)
	{
		printf("malloc fail\n");
		exit(-1);
	}
	newnode->data = x;
	newnode->next = NULL;
	if (pq->head == NULL)//第一次入队,直接将头尾指针指向新结点
	{
		pq->head = pq->tail = newnode;
	}
	else//正常尾插
	{
		pq->tail->next = newnode;
		pq->tail = newnode;
	}
}

这里要单独考虑第一次入队的情况,入队时,尾指针指向新的结点,然后更新尾指针

2.4.5出队

void QueuePop(Queue* pq)
{
	assert(pq);
	assert(!QueueEmpty(pq));//队列不能为空
	QNode* next = pq->head->next;
	free(pq->head);
	pq->head = next;

	
}

出队时先提前保存头结点的下一个结点,然后释放头结点,将头指针指向新的头结点

2.4.6返回队头,队尾元素

QDataType QueueFront(Queue* pq)
{
	assert(pq);
	assert(!QueueEmpty(pq));
	return pq->head->data;
}
QDataType QueueBack(Queue* pq)
{
	assert(pq);
	assert(!QueueEmpty(pq));
	return pq->tail->data;
}

头指针指向的即为队头,尾指针指向的即为队尾

2.4.7判断队列是否为空

bool QueueEmpty(Queue* pq)
{
	assert(pq);
	return pq->head == NULL;
}

头指针指向空即为空队列

2.4.8返回队列中元素的个数

int QueueSize(Queue* pq)
{
	int size = 0;
	QNode* cur = pq->head;
	while (cur)//遍历整个链表,记录个数
	{
		size++;
		cur = cur->next;
	}
	return size;
}

2.4.9测试队列的函数

void test1()
{
	Queue pq;
	QueueInit(&pq);
	QueuePush(&pq, 1);
	QueuePush(&pq, 2);
	QueuePush(&pq, 3);
	QueuePush(&pq, 4);
    QueuePush(&pq, 5);
	QueuePush(&pq, 6);//入队1,2,3,4,5,6
	printf("%d\n", QueueSize(&pq));//打印队列中元素的个数6
	while (!QueueEmpty(&pq))
	{
		printf("%d ", QueueFront(&pq));//依次打印队头元素1,2,3,4,5,6
		QueuePop(&pq);//队头元素出队
	}
	printf("\n");

	QueueDestroy(&pq);
}

测试结果
在这里插入图片描述

  • 6
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值