栈与队列的相关操作

栈是一种特殊的线性表,其中只允许在一端进行插入及删除元素操作称为栈顶端,另一端称为栈底。

栈的基本操作

进栈操作:栈的插入元素操作称为进栈/入栈,入栈的数据保存在栈顶
出栈:栈的删除操作称为出栈,出栈数据也在栈顶

一、栈的基本知识

入栈过程与出栈过程:
在这里插入图片描述
进栈:修改栈顶指针,将需要入栈元素放入栈顶指针指向空间
出栈:保存栈顶指针所指空间元素,修改栈顶指针

二、栈的操作

栈只能在栈顶端进行插入/删除操作,具有“先进后出(FILO)”特性
利用顺序表来实现栈操作:

数据类型定义:

typedef int STdatatype;

1、定义栈结构:

typedef struct stack {
	STdatatype *_data;              //栈的数据域
	int _size;                                //记录栈中有效元素的个数
	int _capacity;                        //栈的容量
}stack;

首先需要队栈进行初始化操作,预防野指针的出现:

void stack_init(stack* st)
{
	if (st == NULL)
		return;
	st->_data = NULL;
	st->_size = st->_capacity = 0;
}

2、入栈操作:
首先要进行栈空间的判断,若空间足够则直接入栈,空间不足需要为新元素开辟新空间。
修改栈顶指针,即栈顶指针加一

void stack_push(stack* st, STdatatype val)      //进栈
{
	if (st == NULL)           // 判空栈
		return;
	checkcapacity(st);            //容量的检查-----扩容
	st->_data[st->_size++] = val;        //修改栈顶指针并在其指向空间放入新元素val
}
//扩容操作
void checkcapacity(stack* st)
{
	if (st->_capacity == st->_size) {
		int newcapacity = st->_capacity == 0 ? 1 : 2 * st->_capacity;
		st->_data = (STdatatype*)realloc(st->_data, sizeof(STdatatype)*newcapacity);
		st->_capacity = newcapacity;
	}
}

3、出栈:
出栈在栈顶端操作,栈顶指针减一

void stack_pop(stack* st)             //删除栈顶元素----出栈
{
	if (st == NULL || st->_size == 0)
		return;
	--st->_size;                            //修改有效元素个数,修改栈顶指针指向
}

4、获取栈顶元素
直接返回栈顶指针指向空间的内容即可

STdatatype stack_top(stack*st)         //获取栈顶元素
{
	return st->_data[st->_size - 1];
}

5、判栈空
如果栈中有效元素个数为0 ,说明栈为空,否则栈不空

int stack_empty(stack *st)
{
	if (st == NULL || st->_size == 0)
		return 1;
	else
		return 0;
}

6、栈的销毁

void stack_destory(stack* st)       //销毁
{
	free(st->_data);
	st->_capacity = st->_size = 0;
}

队列

队列只允许在一端进行插入数据操作,而在另一端进行删除数据操作的特殊线性表,队列具有先进先出(FIFO) 特点,允许进行插入的一端称为队尾,允许删除的一端称为队头。

队列的基本操作

入队: 允许进行插入数据的一端-----------队尾
出队: 允许进行删除数据的一端-----------队头

一、队列的基本知识

在这里插入图片描述
定义队列时需要定义两个指针:队头指针、队尾指针

二、队列的操作

队列在队头进行删除元素操作,队尾进行插入元素操作,拥有“先进先出(FIFO)”特性,使用带尾指针的单链表来实现。

定义数据类型:

typedef int Qdatatype;

1、创建一个队列

typedef struct QNode {
	Qdatatype _data;              //队列元素
	struct QNode* _next;        //指向下一个元素
}QNode;
typedef struct Queue {
	QNode* _head;                 //队头指针
	QNode* _tail;                    //队尾指针
	int _size;                           //记录队列中元素的个数
}Queue;

首先对创建好的队列进行初始化操作:

void Queue_init(Queue * q)
{
	if (q == NULL)
		return;
	q->_head = q->_tail = NULL;
	q->_size = 0;
}

2、入队操作:队尾操作
入队之前首先要开辟一个新节点的空间来保存新的元素:

QNode* creatNode(Qdatatype val)              //创建新节点
{
	QNode* node = (QNode*)malloc(sizeof(QNode));
	node->_data = val;
	node->_next = NULL;
	return node;
}

在进行新元素的插入,以及队尾指针的更新操作:

void Queue_push(Queue* q, Qdatatype val)     //插入------队尾
{
	QNode* node = creatNode(val);
	if (q->_head == NULL)       //若当前队列为空即插入元素为当前队列的第一个元素时
		q->_head = q->_tail = node;
	else {
		q->_tail->_next = node;                  //当前队列插入前非空,直接修改队尾指针
		q->_tail = node;
	}
	++q->_size;
} 

3、出队操作:对头操作

void Queue_pop(Queue* q)                 //删除------队头
{
	if (q == NULL || q->_head == NULL) 
		return;
	QNode* next = q->_head->_next;            //保存队头指针的下一个节点内容
	free(q->_head);                                      //删除队头元素
	q->_head = next;                                   //修改队头指针指向
	
	//如果队列中只有一个数据
	if (q->_head == NULL)
		q->_tail = NULL;
	--q->_size;
}

4、获取队头队尾元素

Qdatatype Queue_front(Queue* q)      //取队头
{
	return q->_head->_data;
}
Qdatatype Queue_back(Queue* q)      //取队尾
{
	return q->_tail->_data;
}

5、队列的判空

int Queue_empty(Queue* q)                //判空
{
	/*if (q == NULL || q->_head == NULL)
		return 1;
	return 0;*/
	return q->_head == NULL;
}

6、队列的销毁

void Queue_destory(Queue* q)
{
	QNode* cur = q->_head;
	while (cur) {
		QNode* next = cur->_next;
		free(cur);
		cur = next;
	}
	q->_head = q->_tail = NULL;
}

三、循环队列

分析:

循环队列的引入:
在这里插入图片描述

为了防止产生假溢出问题,提升空间利用率,我们可以采用循环队列的方式来进行存储:
在这里插入图片描述
但是直接来使用又会存在一个新的问题:
怎样判断循环队列中所有空间都被有效使用了?

按照普通想法,如果一个队列的队尾指针达到了队列最大容量的位置,我们将传统的队列的队尾指针可以折回来指向队头
在这里插入图片描述
指针以上疑问,我们采取了三种方法来进行解决:

方法一:牺牲一个空间的位置
在这里插入图片描述

方法二:采用一个标记 flag
起初设置 flag=0;

空间满的情况:入队导致队满,因此每入队一个元素将 flag 置为 1
空间空的情况:出队导致队空,因此每出队一个元素将 flag 置为 0

在这里插入图片描述

方法三:采用一个计数器 count
起始将 count=0;

每入队一个元素 count++
每出队一个元素 count–

在这里插入图片描述

针对上述情况,下面来实现一下循环队列的一些基本操作吧:

typedef struct {
	int* array;
	int capacity;
	int size;   //有效元素个数
	int front;  //队头
 	int back;  //队尾
}MyCircularQueue;

//创建一个队列
MyCircularQueue* myCircularQueueCreat(int k)    //循环队列最终存储 k  个元素
{
	MyCircularQueue* mq = (MyCircularQueue*)malloc(sizeof(MyCircularQueue));
	if (NULL == mq)    //申请空间失败
		return NULL;

	mq->array = (int*)malloc(sizeof(int)*k);
	mq->capacity = k;
	mq->size = 0;
	mq->back = mq->front = 0;

	return mq;
}

//判满
bool myCircularQueueIsFull(MyCircularQueue* obj)
{
	if (obj->size == obj->capacity)        //如果此时有效元素个数=最大容量则满
		return true;
	else
		return false;
}

//判空
bool myCircularQueueIsEmpty(MyCircularQueue* obj)
{
	if (obj->size == 0)
		return true;
	else
		return false;
}


bool myCircularQueueEnQueue(MyCircularQueue* obj, int val)
{
	//入队
	if (myCircularQueueIsFull(obj))
		return false;   //循环链表满不可再入

	//存在空间可以入,则在队尾入 并将队尾指针后移
	obj->array[obj->back++] = val;

	//当移动至末尾时,需要将队尾指针循环到起始位置
	if (obj->back == obj->capacity)
		obj->back = 0;
	
	obj->size++;        //有效元素个数加一
	return true;
}

//出队

bool myCircularQueueDeQueue(MyCircularQueue* obj)
{
	if (myCircularQueueIsEmpty(obj))     //队空不能出
		return false;

	//可以出队,队头出队
	obj->front++;

	//当移动到空间末尾时,需要将队头指针循环到起始位置
	if (obj->front == obj->capacity)
		obj->front = 0;

	obj->size--;   //出队有效元素个数减一
	return true;
}


//获取队头元素
int myCircularQueueFront(MyCircularQueue* obj)
{
	if (!myCircularQueueIsEmpty(obj))    //非空队列取队头
		return obj->array[obj->front];
	else
		return -1;          //空队列返回 -1
}

//获取队尾元素
int myCircularQueueRear(MyCircularQueue* obj)
{
//非空队列取队尾,注意队尾指针 back 指向的是有效队尾元素的下一个空间
	if (!myCircularQueueIsEmpty(obj))    
		return obj->array[(obj->back - 1 + obj->capacity) % obj->capacity];
	
	return -1;         //空队列返回 -1
}

//销毁
void myCircularQueueFree(MyCircularQueue* obj)
{
	free(obj->array);     //首先销毁有效元素的空间
	free(obj);       //其次销毁整个队列空间
}

用栈来实现队列

栈:先进后出,栈顶操作
队列:先进先出,队头删,队尾入

思路:使用两个栈来实现:入队栈、出队栈
入队栈:只进行入队操作
出队栈:只进行出队操作

实现过程

入队:
在这里插入图片描述
出队:
在这里插入图片描述
再入栈:
在这里插入图片描述

代码:

1、调用栈的实现过程:

typedef int STdatatype;                              //调用栈的实现过程

typedef struct stack {
	STdatatype *_data;
	int _size;
	int _capacity;
}stack;
void stack_init(stack* st)
{
	if (st == NULL)
		return;
	st->_data = NULL;
	st->_size = st->_capacity = 0;
}
void checkcapacity(stack* st)
{
	if (st->_capacity == st->_size) {
		int newcapacity = st->_capacity == 0 ? 1 : 2 * st->_capacity;
		st->_data = (STdatatype*)realloc(st->_data, sizeof(STdatatype)*newcapacity);
		st->_capacity = newcapacity;
	}
}
void stack_push(stack* st, STdatatype val)      //进栈
{
	if (st == NULL)           //空栈
		return;
	checkcapacity(st);
	st->_data[st->_size++] = val;
}
void stack_pop(stack* st)       //删除栈顶元素----出栈
{
	if (st == NULL || st->_size == 0)
		return;
	--st->_size;
}
int stack_size(stack * st)
{
	if (st == NULL)
		return 0;
	return st->_size;
}
int stack_empty(stack *st)
{
	if (st == NULL || st->_size == 0)
		return 1;
	else
		return 0;
}
STdatatype stack_top(stack*st)
{
	return st->_data[st->_size - 1];
}
void stack_destory(stack* st)
{
	if (st) {
		if (st->_data) {
			free(st->_data);
			st->_data = NULL;
			st->_capacity = st->_size = 0;
		}
	}
}

2、定义两个栈结构来实现队列操作:

typedef struct {
	stack pushST;     //入队栈 
	stack popST;    //出队栈
} MyQueue;
MyQueue* myQueueCreate() {                      
	//动态创建
	MyQueue* pq = (MyQueue*)malloc(sizeof(MyQueue));
	stack_init(&pq->pushST);
	stack_init(&pq->popST);
	return pq;
}
void myQueuePush(MyQueue* obj, int x) { 
	//入队栈
	stack_push(&obj->pushST,x);
}
int myQueuePop(MyQueue* obj) {
	//出对栈------判断是否为空
	//空,需要把入队栈的元素导入
	//不空,之间出栈
	int front;
	if (!stack_empty(&obj->popST)) {
		front = stack_top(&obj->popST);
		stack_pop(&obj->popST);               //出栈
	}
	else {
		while (!stack_empty(&obj->pushST)) {
			stack_push(&obj->popST, stack_top(&obj->pushST));
			stack_pop(&obj->pushST);
		}
		front = stack_top(&obj->popST);
		stack_pop(&obj->popST);
	}
	return front;
}
int myQueuePeek(MyQueue* obj) {          //获取队头元素
	if (stack_empty(&obj->popST))
	{
		while (!stack_empty(&obj->pushST)) {
			stack_push(&obj->popST, stack_top(&obj->pushST));
			stack_pop(&obj->pushST);
		}
	}
	return stack_top(&obj->popST);
}
bool myQueueEmpty(MyQueue* obj) {                      //判空
	return stack_empty(&obj->popST) && stack_empty(&obj->pushST);
}
void myQueueFree(MyQueue* obj) {
	stack_destory(&obj->pushST);                     //销毁栈结构
	stack_destory(&obj->popST);
	free(obj);
}

用队列来实现栈

实现过程

在这里插入图片描述
获取栈顶元素:要在队列中获取正确的栈顶元素,需要先找到最后一个入队的元素,即队尾元素就是栈结构中的栈顶元素

代码:

1、调用队列操作:

typedef int qdatatype;
typedef struct qnode {
	qdatatype _data;
	struct qnode* _next;
}qnode;
typedef struct queue {
	qnode* _head;
	qnode* _tail;
	int _size;
}queue;
void queue_init(queue * q)
{
	if (q == NULL)
		return;
	q->_head = q->_tail = NULL;
	q->_size = 0;
}
qnode* creatnode(qdatatype val)              //创建新节点
{
	qnode* node = (qnode*)malloc(sizeof(qnode));
	node->_data = val;
	node->_next = NULL;
	return node;
}
void queue_push(queue* q, qdatatype val)     //插入------队尾
{
	qnode* node = creatnode(val);
	if (q->_head == NULL)
		q->_head = q->_tail = node;
	else {
		q->_tail->_next = node;
		q->_tail = node;
	}
	++q->_size;
}
void queue_pop(queue* q)                 //删除------队头
{
	if (q == NULL || q->_head == NULL)
		return;
	qnode* next = q->_head->_next;
	free(q->_head);
	q->_head = next;

	//如果队列中只有一个数据
	if (q->_head == NULL)
		q->_tail = NULL;
	--q->_size;
}
int queue_empty(queue* q)                //判空
{
	/*if (q == null || q->_head == null)
		return 1;
	return 0;*/
	return q->_head == NULL;
}
qdatatype queue_front(queue* q)      //取队头
{
	/*if (q == null || q->_head == null)
		return;*/
	return q->_head->_data;
}
qdatatype queue_back(queue* q)      //取队尾
{
	return q->_tail->_data;
}
int	queue_size(queue* q)
{
	if (q == NULL)
		return 0;
	return q->_size;
}
void queue_destory(queue* q)
{
	qnode* cur = q->_head;
	while (cur) {
		qnode* next = cur->_next;
		free(cur);
		cur = next;
	}
	q->_head = q->_tail = NULL;
}

2、定义队列来实现栈:

typedef struct {  
	queue q;             //定义一个队列
}mystack;
mystack* mystackcreat()
{
	//动态创建一个队列栈
	mystack* pst = (mystack*)malloc(sizeof(mystack));
	queue_init(&pst->q);   //进行初始化
	return pst;
}
void mystack_push(mystack* obj, int x)
{
	queue_push(&obj->q, x);          //进栈 -- 进队
}
int mystack_pop(mystack* obj)     //出栈:要找到入队的最后一个元素--相当于是栈顶元素
{
	int n = queue_size(&obj->q);
	while (n > 1) {
		int front = queue_front(&obj->q);
		queue_pop(&obj->q);
		queue_push(&obj->q,front);
		--n;
	}
	int top = queue_front(&obj->q);   //找到栈顶元素
	queue_pop(&obj->q);        //出栈
	return top;
}
int mystack_top(mystack* obj)       //栈顶元素---入队的队尾元素
{
	return queue_back(&obj->q);
}
bool mystack_empty(mystack* obj)
{
	return queue_empty(&obj->q);
}
bool mystack_size(mystack* obj)
{
	return queue_size(&obj->q);
}
void mystack_free(mystack* obj)
{
	queue_destory(&obj->q);
	free(obj);
}

本篇主要要理解并熟悉栈与队列的特点,同时熟练掌握基本的入栈出栈(入队出队)、判空,获取某一端元素等基本过程,读者可以通过画图来增加理解并练习!

(博客内容全为原创,有任何问题都可以评论私聊哦!欢迎发现不足^ .^)

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值