【数据结构】栈和队列

一.栈

1.定义

栈:一种特殊的线性表,其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端
称为栈顶,另一端称为栈底。栈中的数据元素遵守后进先出LIFO(Last In First Out)的原则。
特点:喝多了吐(先进先出,后进后出)。

图:
在这里插入图片描述

补充:在我们写代码用到的栈(堆栈),也是这一种结构,并且这个栈具有动态属性,所以我们一般的局部变量或者函数的调用都发生在这里,而且栈的空间使用是先使用高地址再使用低地址,所以我们也称堆栈是向下生长的。

图:
在这里插入图片描述
说明:这几个区并不是内存!而是进程地址空间(操作系统)。

2.实现

栈的实现一般可以使用数组或者链表实现,相对而言数组的结构实现更优一些。因为数组在尾上插入数据(先进先出)的代价比较小。
说明:
链表的尾插的时间复杂度为:O(N)
顺序表尾插的时间复杂度为:O(1)

静态和动态

动态:

typedef int SLDateType;
struct Stack
{
	SLDateType* arr;//通过指针变量管理开辟的空间
	int size;//1.当前所存数据的个数。2.下一个数据的下标。3.栈顶
	int capacity;//当前栈的容量,考虑是否进行扩容。
};

静态:

#define capacity 10
typedef int SLDateType;
struct Stack
{
	SLDateType arr[capacity];//数组的大小
	int size;//同上
};

3.函数

1.初始化栈

void StackInit(Stack* p)//修改栈的内容,需要传指针进行修改
{
	p->size = 0;
	p->arr = NULL;
	SLDateType* tmp = NULL;
	tmp = (SLDateType*)realloc(p->arr, 4 * sizeof(SLDateType));
	//realloc只能对初始化的指针进行开辟空间,未初始化的会进行报错
	if (tmp == NULL)//对返回值进行检查
	{
		perror("Stack:realloc fail");
		exit(-1);
	}
	p->arr = tmp;
	p->capacity = 4;
}

2.在栈顶压入元素

static void CheckCapacity(Stack *p)
{
	if (p->size == p->capacity)//说明栈的空间已满
	{
		SLDateType* tmp = NULL;
		tmp = (SLDateType*)realloc(p->arr, sizeof(SLDateType) * (p->capacity) * 2);
		if (tmp == NULL)
		{
			perror("CheckCapacity:realloc  fail");
			exit(-1);
		}
		p->capacity *= 2;
		p->arr = tmp;
	}
}
void StackPushBack(Stack* p, SLDateType x)
{
	assert(p);
	CheckCapacity(p);//检查当前栈的容量
	p->arr[p->size++] = x;
}

3.出栈

void StackPopBack(Stack* p)
{
	assert(p->size > 0);//出栈的前提是:栈里面得有元素。
	p->size--;
}

4.获取栈顶元素

SLDateType StackTop(Stack* p)
{
	assert(p->size > 0);//获取元素,前提也是
	return p->arr[p->size-1];
}

5.获取栈的元素个数

int StackSize(Stack* p)
{
	assert(p->size > 0);
	return p->size;
}

6.确认栈是否为空

int StackEmpty(Stack* p)
{
	if (p->size == 0)
	{
		return 1;
	}
	return 0;
}

7.销毁栈

void StackDestory(Stack* p)
{
	assert(p);
	free(p->arr);
	p->arr = NULL;
	p->size = 0;
	p->capacity = 0;
}

二.队列

1.定义

队列:只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表。
特点:先进先出(吃多了拉)
入队列:进行插入操作的一端称为队尾
说明:FIFO(First In First Out)——入队列
出队列:进行删除操作的一端称为队头

在这里插入图片描述

2.实现

队列也可以数组和链表的结构实现,使用链表的结构实现更优一些,因为如果使用数组的结构,出队列在数组头上出数据,效率会比较低。

在这里插入图片描述
结构:

typedef int QLNDateType;
//节点
typedef struct QListNode
{
	QLNDateType val;
	struct QListNode* next;
}QListNode;
//对列的结构
typedef struct Queue
{
	QListNode* Top;
	QListNode* Tail;
}Queue;

3.函数

1.初始化对列

void QueueInit(Queue* q)
{
	q->Tail = NULL;
	q->Top = NULL;
	//将头和尾都置为空
}

2.入对列

void QueuePushBack(Queue* q,QLNDateType x)
{
	//创建并初始化结点
	QListNode* NewNode = (QListNode*)malloc(sizeof(QListNode));
	if (NewNode == NULL)
	{
		perror("malloc fail");
		exit(-1);
	}
	NewNode->next = NULL;
	NewNode->val = x;
	//链表为空
	if (q->Top == NULL)
	{
		q->Tail = NewNode;
		q->Top = NewNode;
		return;
	}
	//链表不为空
	else
	{
		q->Tail->next = NewNode;
		q->Tail = NewNode;
	}
}

3.出队列

void QueuePopFront(Queue* q)
{
	//对列不能为空!
	assert(q->Top);
	//头删
	QListNode* next = q->Top->next;//保存下一个结点的位置
	if (q->Top == q->Tail)
	{
		q->Tail = NULL;
	}
	free(q->Top);
	q->Top = next;
}

4.获取队尾元素

QLNDateType QueueTail(Queue q)
{
	//对列不能为空
	assert(q.Top);
	return q.Tail->val;
}

5.获取队首元素

QLNDateType QueueTop(Queue q)
{
	//对列不能为空
	assert(q.Top);
	return q.Top->val;
}

6.获取有效元素个数

int QueueSize(Queue q)
{
	QListNode* cur = q.Top;
	int count = 0;
	while (cur)
	{
		count++;
		cur = cur->next;
	}
	return count;
}

7.检测队列是否为空

int QueueEmpty(Queue q)
{
	return q.Top == NULL;
}

8.销毁对列

void QueueDestory(Queue* q)
{
	//将对列的首结点保留下来即可完成对列的删除
	QListNode* cur = q->Top;
	q->Top = NULL;
	q->Tail = NULL;
	while (cur)
	{
		QListNode* next = cur->next;
		free(cur);
		cur = next;
	}
}

7.打印对列

void QueuePrint(Queue q)
{
	QListNode* cur = q.Top;
	while (cur)
	{
		printf("%d->", cur->val);
		cur = cur->next;
	}
	printf("NULL\n");
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值