室友一把王者的时间——偷偷学会栈与队列

目录

栈的概念与结构

栈的定义

栈的初始化

栈的销毁

入栈

出栈

 获取栈顶元素

获取栈中有效数据的个数

判断栈是否为空

栈的完整代码

Stack.h

Stack.c

test.c

队列的概念与结构

 队列的定义

队列的初始化

入队

出队

获取队头元素

获取队尾元素

获取队列的有效数据个数

判断队列是否为空

销毁队列

队列的完整代码

Queue.h

Queue.c

test.c


栈的概念与结构

栈是一种特殊的线性存储结构,特点是只允许在一端进行插入与删除数据,也被称为“后进先出”。删除与插入的那一段被称为栈顶,另一端被称为栈底。

出栈:删除数据在栈顶进行。

压栈:插入数据在栈顶进行。

具体如图所示:

栈的定义

栈的实现用数组和链表均能实现,在这里我们使用时数组进行实现。

1、首先定义一个数组。

2、我们需要对栈顶的数据进行删除与插入,所以还要定义一个数来存放栈顶的位置。

3、由于是数组的实现所以我们需要考虑增容的情况,那么还要定义一个容量

综上所述:我们如果用一个结构体将他们封装起来就更好了,因此我们这里的定义运用结构体进行定义。 代码如下:

typedef int STDataType;//将int重命名 便于对数据类型的转换
typedef struct Stack
{
	STDataType* a;//栈
	STDataType top;//栈顶
	STDataType capacity;//容量
}Stack;

栈的初始化

分别将定义中的数据进行初始化,代码如下:

// 初始化栈 
void StackInit(Stack* ps)
{
	assert(ps);
	ps->a = NULL;
	ps->top = 0;
	ps->capacity = 0;
}

栈的销毁

为了不造成空间的浪费,当我们所开辟的空间不用时,我们应该将他们给释放掉。

代码如下:

// 销毁栈 
void StackDestroy(Stack* ps)
{
	assert(ps);
	free(ps->a);
	ps->a = NULL;
	ps->capacity = 0;
	ps->top = 0;
}

入栈

注意:如果栈满我们需要动态开辟空间。

图解如下:

代码如下:


// 入栈 
void StackPush(Stack* ps, STDataType x)
{
	assert(ps);
	if (ps->top == ps->capacity)
	{
		int newCapacity = ps->capacity == 0 ? 4 : 2 * (ps->capacity);
		STDataType* tmp = realloc(ps->a, sizeof(STDataType) * newCapacity);
		if (tmp == NULL)
		{
			printf("realloc fail\n");
			exit(-1);
		}
		ps->capacity = newCapacity;
		ps->a = tmp;
	}
	ps->a[ps->top] = x;
	ps->top++;
}

出栈

出栈相对于入栈来说就轻松多了,只需要判断一下是否为空栈后,直接将top--就可以了。

图解如下:

大家可以发现栈中的1并没有删除,但是我们已经访问不到了,并且如果再次入栈,将会覆盖之前的1,所以不必担心。

代码如下:


// 出栈 
void StackPop(Stack* ps)
{
	assert(ps);
	assert(!StackEmpty(ps));//这里用到的是后面会提到的判断栈是否为空的函数
	ps->top--;
}

 获取栈顶元素

由于top的值刚好是我们需要获取栈顶元素的下标,所以只要先判断栈是否为空,后返回top-1为下标的数就可以了。

代码如下:

// 获取栈顶元素 
STDataType StackTop(Stack* ps)
{
	assert(ps);
	assert(!StackEmpty(ps));//这里用到的是后面会提到的判断栈是否为空的函数
	return ps->a[ps->top-1];
}

获取栈中有效数据的个数

直接返回top就行了,因为top的值就是栈中数据的元素个数。

代码如下:


// 获取栈中有效元素个数 
int StackSize(Stack* ps)
{
	assert(ps);

	return ps->top;
}

判断栈是否为空

我们这里引用一个bool函数。bool函数的返回值非真既假,也就是他们的返回值就是用来判断真假的,当我们需要返回真假而不需要返回特定的值时来使用它。

代码如下:

// 检测栈是否为空,如果为空返回非零结果,
//如果不为空返回0 
bool StackEmpty(Stack* ps)
{
	assert(ps);

	return ps->top == 0;
}

栈的完整代码

Stack.h

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

typedef int STDataType;

typedef struct Stack
{
	STDataType* a;//栈
	STDataType top;//栈顶
	STDataType capacity;//容量
}Stack;

// 初始化栈 
void StackInit(Stack* ps);

// 销毁栈 
void StackDestroy(Stack* ps);

// 入栈 
void StackPush(Stack* ps, STDataType x);

// 出栈 
void StackPop(Stack* ps);

// 获取栈顶元素 
STDataType StackTop(Stack* ps);

// 获取栈中有效元素个数 
int StackSize(Stack* ps);

// 检测栈是否为空,如果为空返回非零结果,如果不为空返回0 
bool StackEmpty(Stack* ps);

Stack.c

#include"Stack.h"
// 初始化栈 
void StackInit(Stack* ps)
{
	assert(ps);
	ps->a = NULL;
	ps->top = 0;
	ps->capacity = 0;
}

// 销毁栈 
void StackDestroy(Stack* ps)
{
	assert(ps);
	free(ps->a);
	ps->a = NULL;
	ps->capacity = 0;
	ps->top = 0;
}

// 入栈 
void StackPush(Stack* ps, STDataType x)
{
	assert(ps);
	if (ps->top == ps->capacity)
	{
		int newCapacity = ps->capacity == 0 ? 4 : 2 * (ps->capacity);
		STDataType* tmp = realloc(ps->a, sizeof(STDataType) * newCapacity);
		if (tmp == NULL)
		{
			printf("realloc fail\n");
			exit(-1);
		}
		ps->capacity = newCapacity;
		ps->a = tmp;
	}
	ps->a[ps->top] = x;
	ps->top++;
}

// 出栈 
void StackPop(Stack* ps)
{
	assert(ps);
	assert(!StackEmpty(ps));
	ps->top--;
}

// 获取栈顶元素 
STDataType StackTop(Stack* ps)
{
	assert(ps);
	assert(!StackEmpty(ps));

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

// 获取栈中有效元素个数 
int StackSize(Stack* ps)
{
	assert(ps);

	return ps->top;
}

// 检测栈是否为空,如果为空返回非零结果,
//如果不为空返回0 
bool StackEmpty(Stack* ps)
{
	assert(ps);

	return ps->top == 0;
}

test.c

#include"Stack.h"

void TestStack1()
{
	Stack ST;
	StackInit(&ST);
	StackPush(&ST, 1);
	StackPush(&ST, 2);
	StackPush(&ST, 3);
	StackPush(&ST, 4);
	printf("栈顶元素为:\n");
	printf("%d\n", StackTop(&ST));
	while(!StackEmpty(&ST))
	{
		printf("%d ", StackTop(&ST));
		StackPop(&ST);
	}
	StackDestroy(&ST);
}

int main()
{
	TestStack1();
	return 0;
}

 测试代码结果如图所示:

队列的概念与结构

队列类似于排队一样,从一端入数据,另一端出数据。入数据的那一端被称为队尾,出数据的那一段被称为队头。

入队:在队尾进行入数据。

出队:在队头进行出数据。

如图所示:

 队列的定义

1、我们使用链表来定义队列。

2、设置一个结构体来定义队头和队尾。

代码如下:

typedef int QDataType;

typedef struct QListNode
{
	struct QListNode* next;
	QDataType data;
}QNode;

// 队列的结构 
typedef struct Queue
{
	QNode* front;
	QNode* rear;
}Queue;

队列的初始化

代码如下:

// 初始化队列 
void QueueInit(Queue* q)
{
	q->front = NULL;
	q->rear = NULL;
}

入队

由于使用的是链表,其操作类似单链表里面的尾插。如果单链表里面的尾插还不熟悉的请看我之前的博客。 如图所示:

 代码如下:

// 队尾入队列 
void QueuePush(Queue* q, QDataType x)
{
	assert(q);
	QNode* cur = BuyQueueNode(x);//这里定义一个函数来创建新结点
	if (q->front== NULL)
	{
		q->front = q->rear = cur;
	}
	else
	{
		q->rear->next = cur;
		q->rear = cur;
	}
}

QNode* BuyQueueNode(QDataType x)
{
	QNode* cur = (QNode*)malloc(sizeof(QNode));
	cur->next = NULL;
	cur->data = x;
	return cur;
}

出队

由于是链表,所以操作时将front指向下一个,并且释放front之前指向的那个。

如图所示:

 代码如下:

// 队头出队列 
void QueuePop(Queue* q)
{
	assert(q);

	assert(!QueueEmpty(q));

	QNode* next = q->front->next;
	free(q->front);
	q->front = next;
	if (q->front==NULL)
	{
		q->rear = NULL;//防止野指针
	}
}

注意:

如果只剩最后一个元素的时候,我们释放完front后需要将rear也置空,因为此时rear指向的数据已经被释放,如果不置空的话,rear将是野指针。

获取队头元素

只需要判断一下队是否为空后,返回front指向的数据即可。

代码如下:

// 获取队列头部元素 
QDataType QueueFront(Queue* q)
{
	assert(q);
	assert(!QueueEmpty(q));//这个是后面即将提到的判空函数
	return q->front->data;
}

获取队尾元素

同上获取队头元素类似。

代码如下:

// 获取队列队尾元素 
QDataType QueueBack(Queue* q)
{
	assert(q);
	assert(!QueueEmpty(q));//此函数是后面要提到的判空函数
	return q->rear->data;
}

获取队列的有效数据个数

此操作类似于计算单链表的长度,只要逐个遍历从front到rear就可以了。

代码如下:

// 获取队列中有效元素个数 
int  QueueSize(Queue* q)
{
	assert(q);
	int n = 0;
	QNode* cur = q->front;
	while (cur)
	{
		cur = cur->next;
		n++;
	}
	return n;
}

判断队列是否为空

在此同样用到了判断栈是否为空的那个bool函数,这里不过多解释。

代码如下:

// 检测队列是否为空,如果为空返回非零结果,
//如果非空返回0 
bool QueueEmpty(Queue* q)
{
	assert(q);
	return q->rear == NULL;
}

销毁队列

由于是链表,所以销毁时应该逐个结点释放。

代码如下:

// 销毁队列 
void QueueDestroy(Queue* q)
{
	assert(q);
	QNode* cur = q->front;
	while (cur)
	{
		/*QNode* next = cur->next;
		free(cur);
		cur->next;
		next = next->next;
		cur = next;*/
		QueuePop(q);
	}
	q->front = q->rear = NULL;
}

队列的完整代码

Queue.h

#pragma once
// 链式结构:表示队列 
#include<stdio.h>
#include<stdbool.h>
#include<assert.h>

typedef int QDataType;

typedef struct QListNode
{
	struct QListNode* next;
	QDataType data;
}QNode;

// 队列的结构 
typedef struct Queue
{
	QNode* front;
	QNode* rear;
}Queue;

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

//创建一个新的结点
QNode* BuyQueueNode(QDataType x);

// 队尾入队列 
void QueuePush(Queue* q, QDataType x);

// 队头出队列 
void QueuePop(Queue* q);

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

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

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

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

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

Queue.c

#include"Queue.h"


// 初始化队列 
void QueueInit(Queue* q)
{
	q->front = NULL;
	q->rear = NULL;
}

// 队尾入队列 
void QueuePush(Queue* q, QDataType x)
{
	assert(q);
	QNode* cur = BuyQueueNode(x);
	if (q->front== NULL)
	{
		q->front = q->rear = cur;
	}
	else
	{
		q->rear->next = cur;
		q->rear = cur;
	}
}

QNode* BuyQueueNode(QDataType x)
{
	QNode* cur = (QNode*)malloc(sizeof(QNode));
	cur->next = NULL;
	cur->data = x;
	return cur;
}
// 队头出队列 
void QueuePop(Queue* q)
{
	assert(q);

	assert(!QueueEmpty(q));

	QNode* next = q->front->next;
	free(q->front);
	q->front = next;
	if (q->front==NULL)
	{
		q->rear = NULL;//防止野指针
	}
}

// 获取队列头部元素 
QDataType QueueFront(Queue* q)
{
	assert(q);
	assert(!QueueEmpty(q));
	return q->front->data;
}

// 获取队列队尾元素 
QDataType QueueBack(Queue* q)
{
	assert(q);
	assert(!QueueEmpty(q));
	return q->rear->data;
}

// 获取队列中有效元素个数 
int  QueueSize(Queue* q)
{
	assert(q);
	int n = 0;
	QNode* cur = q->front;
	while (cur)
	{
		cur = cur->next;
		n++;
	}
	return n;
}

// 检测队列是否为空,如果为空返回非零结果,
//如果非空返回0 
bool QueueEmpty(Queue* q)
{
	assert(q);
	return q->rear == NULL;
}

// 销毁队列 
void QueueDestroy(Queue* q)
{
	assert(q);
	QNode* cur = q->front;
	while (cur)
	{
		/*QNode* next = cur->next;
		free(cur);
		cur->next;
		next = next->next;
		cur = next;*/
		QueuePop(q);
	}
	q->front = q->rear = NULL;
}

test.c

#include"Queue.h"

void TestQueue()
{
	Queue Q;
	QueueInit(&Q);
	QueuePush(&Q, 1);
	QueuePush(&Q, 2);
	QueuePush(&Q, 3);
	QueuePush(&Q, 4);

	printf("队头是:\n");
	printf("%d\n", QueueFront(&Q));
	printf("队尾是:\n");
	printf("%d\n", QueueBack(&Q));
	while (!QueueEmpty(&Q))
	{
		printf("%d ", QueueFront(&Q));
		QueuePop(&Q);
	}
	QueueDestroy(&Q);
}
int main()
{
	TestQueue();
	return 0;
}

测试结果:

猜你喜欢

转载自blog.csdn.net/m0_57249790/article/details/124316916