数据结构第六讲:栈和队列
- 1.栈
- 1.1概念与结构
- 1.2栈的实现
- 1.2.1栈的基础框架
- 1.2.2栈的初始化
- 1.2.3栈的销毁
- 1.2.4栈的插入(压栈)
- 1.2.5栈的删除(出栈)
- 1.2.6获取栈顶元素
- 1.2.7获取栈中有效数据的个数
 
 
- 2.队列
- 2.1概念与结构
- 2.2队列的实现
- 2.2.1队列的基础框架
- 2.2.2队列的初始化
- 2.2.3入队列
- 2.2.4出队列
- 2.2.5销毁队列
- 2.2.6取队列头节点数据
- 2.2.7取队列尾节点数据
- 2.2.8队列中有效元素的个数
 
 
1.栈
1.1概念与结构
栈:栈是一种特殊的线性表,只允许在固定的一端进行插入和删除操作,遵循LIFO(last in first out)的原则
 压栈:栈的插入操作成为压栈/入栈/进栈,插入操作在栈顶
 出栈:栈的删除成为出栈,删除操作也在栈顶
 
 栈可以通过使用数组或链表来实现,相对而言使用数组实现较好,因为数组实现尾插的时间复杂度为O(1)
1.2栈的实现
栈的实现过程与顺序表的实现方法非常相似,所以这里就不着重阐述了
1.2.1栈的基础框架
与顺序表不同的是,这里没有用来存储有效数据的变量,变成了一个栈顶变量,大小也是有效变量的个数
typedef int StackDataType;
typedef struct Stack
{
	StackDataType* arr;//使用一个指针来指向开辟的数组
	int capacity;//保存数组的空间大小
	int top;//指向栈顶
}Stack;
1.2.2栈的初始化
直接将变量进行初始化即可
//栈的初始化
void Init(Stack* ps)
{
	assert(ps);
	ps->arr = NULL;
	ps->capacity = ps->top = 0;
}
1.2.3栈的销毁
直接销毁即可
//栈的销毁
void Destory(Stack* ps)
{
	assert(ps);
	//注意:这个要加上if进行判断,为了确保arr数组不是指向NULL
	if(ps->arr)
		free(ps->arr);
	ps->arr = NULL;
	ps->capacity = ps->top = 0;
}
1.2.4栈的插入(压栈)
//栈的插入
void StackPush(Stack* ps, StackDataType x)
{
	assert(ps);
	//因为只有栈的插入才需要开辟空间,所以开辟结点空间不必封装成一个函数
	if (ps->top == ps->capacity)
	{
		//空间不足,需要开辟
		int newcapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;
		StackDataType* parr = (StackDataType*)realloc(ps->arr, newcapacity * sizeof(StackDataType));
		if (parr == NULL)
		{
			perror("malloc faile!");
			exit(1);
		}
		ps->arr = parr;
		ps->capacity = newcapacity;
	}
	ps->arr[ps->top++] = x;
}
1.2.5栈的删除(出栈)
//栈的删除
void StackPop(Stack* ps)
{
	//栈为NULL时不能删除,栈中没有数据时不能删除
	assert(ps && ps->top);
	--ps->top;
}
1.2.6获取栈顶元素
//取栈顶元素
StackDataType StackPrint(Stack* ps)
{
	assert(ps && ps->top);
	return ps->arr[ps->top-1];
}
1.2.7获取栈中有效数据的个数
有效数据的个数就是top的大小
//获取栈中有效元素个数
int StackSize(Stack* ps)
{
	assert(ps);
	return ps->top;
}
2.队列
2.1概念与结构
队列也是一种特殊的线性表,与栈不同的是,队列只允许在一端进行插入数据操作,在另一端进行数据删除操作,遵循的是FIFO(first in first out)原则
 入队列:进行插入的一端称为队尾
 出队列:进行删除的一端称为队头
 
 队列也可以使用数组和链表两种方法完成,但是这时链表要更好一点,因为链表的删除只需要改变指针的指向即可
2.2队列的实现
2.2.1队列的基础框架
队列的实现不同于单链表,它需要两个结构体变量
//结点结构体
typedef int QueueDataType;
typedef struct QueueNode
{
	//和链表一样,也需要结点进行链接
	QueueDataType val;
	struct QueueNode* next;
}QueueNode;
//队列结构体
typedef struct Queue
{
	QueueNode* head;//指向队列的头部,方便删除数据
	QueueNode* tail;//指向队列的尾部,方便插入数据
	int size;//用来记录有效数据的个数
}Queue;
2.2.2队列的初始化
直接进行初始化即可
//队列初始化
void Init(Queue* pq)
{
	assert(pq);
	pq->head = pq->tail = NULL;
	pq->size = 0;
}
2.2.3入队列
也就是插入队列,在队列的末尾进行插入即可
//入队列
void QueuePush(Queue* pq, QueueDataType x)
{
	assert(pq);
	//只有入队列需要开辟结点空间
	QueueNode* newnode = (QueueNode*)malloc(sizeof(QueueNode));
	if (newnode == NULL)
	{
		perror("malloc faile!");
		exit(1);
	}
	newnode->val = x;
	newnode->next = NULL;
	//要分情况讨论:当队列一开始没有一个结点时
	if (pq->head == NULL)
	{
		pq->head = pq->tail = newnode;
	}
	else
	{
		//直接插入到末尾即可
		//head ... tail newnode
		pq->tail->next = newnode;
		pq->tail = newnode;
	}
	pq->size++;
}
2.2.4出队列
改变头结点的指针,然后释放空间即可
//出队列
void QueuePop(Queue* pq)
{
	assert(pq && pq->head);
	//出队列要求从队列开头进行删除
	//此时要分情况讨论:当只具有一个结点时
	if (pq->head == pq->tail)
	{
		free(pq->head);
		pq->head = pq->tail == NULL;
	}
	else
	{
		//pq->head pq->head->next
		QueueNode* tmp = pq->head->next;
		free(pq->head);
		pq->head = tmp;
	}
	--pq->size;
}
2.2.5销毁队列
与链表的销毁相同,需要通过两个指针进行循环销毁
//销毁队列
void Destory(Queue* pq)
{
	assert(pq && pq->head);
	QueueNode* prev = pq->head;
	QueueNode* next = pq->head->next;
	while (prev)
	{
		free(prev);
		prev = next;
		if(next)
			next = next->next;
	}
	pq->head = pq->tail = NULL;
	pq->size = 0;
}
2.2.6取队列头节点数据
头节点数据直接提取即可
//取队列头结点数据
QueueDataType QueueFront(Queue* pq)
{
	assert(pq && pq->head);
	return pq->head->val;
}
2.2.7取队列尾节点数据
//取队列尾节点数据
QueueDataType QueueBack(Queue* pq)
{
	assert(pq && pq->head && pq->tail);
	return pq->tail->val;
}
2.2.8队列中有效元素的个数
有效元素个数在结构中进行保存,直接提取即可
//队列有效元素的个数
int QueueSize(Queue* pq)
{
	assert(pq);
	return pq->size;
}



















