数据结构---栈和队列

本文详细介绍了C语言中栈和队列的概念、基本操作,以及如何用栈和队列互换实现对方功能,还涉及括号匹配问题和循环队列的实现,展示了数据结构在编程中的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >



提示:以下是本篇文章正文内容,下面案例可供参考

一、栈和队列概念

(1)栈

(2)队列

二、栈的基本操作

(1)Stack.h

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




typedef int STDataType;        //这里我们用数组实现
typedef struct Stack
{
	STDataType* a;
	int top;
	int capacity;
}ST;

void STInit(ST* ps);//栈的初始化
void STDestroy(ST* ps);//栈的摧毁
void STPush(ST* ps, STDataType x);//入栈
void STPop(ST* ps); //出栈
STDataType STTop(ST* ps);//取栈顶元素

int STSize(ST* ps);//获取栈长度
bool STEmpty(ST* ps);//栈的判空

  (2)Stack.c

#include "Stack.h"

void STInit(ST* ps)
{
	assert(ps);//断言,防止传入空指针
	ps->a = NULL;
	ps->capacity = 0;
	ps->top = 0;
}

void STDestroy(ST* ps)
{
	assert(ps);

	free(ps->a);//释放掉在堆中开辟的空间
	ps->a = NULL;
	ps->top = ps->capacity = 0;
}

void STPush(ST* ps, STDataType x)
{
	assert(ps);
	//判断是否满,top==capacity,如果值为0,还没开辟数组空间那么开辟四个STDataType数据大小,
    //top==capacity ,值不为0,说明空间已经满,那么就开辟到原来空间大小的二倍
	if (ps->top == ps->capacity)
	{
		int newCapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;
		STDataType* tmp = (STDataType*)realloc(ps->a, sizeof(STDataType) * newCapacity);
		if (tmp == NULL)    //扩容函数realoc()如果参数指针为空,功能和malloc相同,同样是开辟一段空间
		{
			perror("realloc fail");
			exit(-1);
		}

		ps->a = tmp;
		ps->capacity = newCapacity;//扩容,那么更新空间大小
	}

	ps->a[ps->top] = x;//入一个元素
	ps->top++;//元素的个数
}

void STPop(ST* ps)//出栈,我们将下表TOP减减就可以了
{
	assert(ps);


	assert(ps->top > 0);//防止栈为空

	--ps->top;
}

STDataType STTop(ST* ps)//获取栈顶元素,top可以表示入栈元素的数量,但在数组下标而言每一次入栈top实际是指向下入栈位置的下一个位置
{
	assert(ps);

	// 
	assert(ps->top > 0);

	return ps->a[ps->top - 1];//队头元素数组下标为top-1
}

int STSize(ST* ps)//top是元素个数,因为每一次push一个元素,top就+1
{
	assert(ps);

	return ps->top;
}

bool STEmpty(ST* ps)//top是表示入栈元素的数量,top等于0,说明所有元素已经出栈了
{
	assert(ps);

	return ps->top == 0;//top等于0返回真,反之为假
}

(3)test.c

2.队列的基本操作

 (1)queue.h

#include<stdio.h>
#include<stdio.h>
#include<stdlib.h>
#include<stdbool.h>
#include<assert.h>
typedef int DataType;

typedef struct QueueNode {      //这里我们用链表实现队列,并用一个结构体记录链表头尾指针,节点的个数

	DataType date;
	QueueNode* next;



}QNode;
typedef struct Queue {

	QNode* head;
	QNode* tail;
	int size;


}Que;

(2)queue.c

#include"queue.h"
void QueueInit(Que* ps)//队列的初始化
{
	ps->head = ps->tail = NULL;
	ps->size = 0;


}
void QueuePush(Que* ps, DataType x)//入队,(其实这里就是链表节点的尾插)
{
	assert(ps);
	QNode* newnode = (QNode*)malloc(sizeof(struct QueueNode));
	if (newnode == NULL)
	{
		perror("malloc:");
		exit(-1);
	}
	newnode->date = x;
	newnode->next = NULL;

	if (ps->tail == NULL)//如果tail为NULL,说明是空链表也就是没有任何节点,这时头尾指针都指向这一个
	{
		ps->head = newnode;
		ps->tail - newnode;
	}
	else//链表不为空,尾指针指向这个节点
	{
		ps->tail->next = newnode;//
		ps->tail = newnode;

	}
	++ps->size;//入队就++


}
bool QueueEmpty(Que* ps);//这里先声明一下判空的函数(判空函数定义在这个函数后面)但是Pop函数需要用到,所以需要声明
void  QueuePop(Que* ps)//出队
{
	assert(ps);
	assert(!QueueEmpty(ps));//防止队列为空
	if (ps->head->next == NULL)//只有一个节点
	{
		free(ps->head);
		ps->head = NULL;
		ps->tail = NULL;
	}
	else 
	{

		QNode* next = ps->head->next;//保存下一个节点,因为如果直接删掉就找不到下一个节点了,这也单链表的特点(下一个需要上一个)
		free(ps->head);
		ps->head = next;//头节点指向下一个,前面已经被删了(出队列)
	}
	--ps->size;


}
void QueueDestroy(Que* ps)//队列的删除
{
	assert(ps);
	QNode* cur = ps->head; //用cur保存队列的头节点
	while (cur)//循环条件我们用每个节点作为条件,如果用cur->next最后一个节点遍历不到,需要后面处理
	{
		QNode* newnext =cur->next;
		free(cur);
		cur = newnext;
	}
}

int QueueFront(Que* ps)//取队头元素,就是获取链表的头节点的date,链表必须要不为空
{
	assert(ps);
	assert(!QueueEmpty(ps));
	return ps->head->date;


}
int QueueBack(Que* ps)//获取队队尾的元素
{
	assert(ps);
	assert(!QueueEmpty(ps));
	return ps->tail->date;
}
int QueueSize(Que* ps)//获取队列的大小
{
	return ps->size;
}
bool QueueEmpty(Que* ps)
{
	return ps->head == NULL;
}

(3)test.c


三,栈和队列的相互实现

3.1栈实现队列

//栈实现队列

typedef struct myQueue { //用两个栈实现

	ST pushst;

	ST popst;



}MyQueue;

MyQueue* CreatQueue()//初始化



{

	MyQueue* obj = (MyQueue*)malloc(sizeof(MyQueue));

	STInit(&obj->popst);

	STInit(&obj->pushst);

	return obj;

}



void MyQueuePush(MyQueue* ps, int x)//对pushst入栈

{

	STPush(&ps->pushst, x);

}

int MyQueuePeek(MyQueue* ps)//将pushst中的元素入栈到popst中,返回popst队头元素

{

	while (!STEmpty(&ps->pushst))//将pushst中的元素全部入栈到popst中,入一个出栈一个

	{

		STPush(&ps->popst, STTop(&ps->pushst));

		STPop(&ps->pushst);
    }

	return STTop(&ps->popst);

}

int myQueuePop(MyQueue* ps)//获取出队列

{

	int front = MyQueuePeek(&ps->popst);//获取队头元素

	STPop(&ps->popst);

	return front;

}

bool myQueueEmpty(MyQueue* ps)//判空

{

return STEmpty(&ps->popst) && STEmpty(&ps->pushst);

}

void myQueueFree(MyQueue* ps)//销毁

{

	STEmpty(&ps->popst);

	STEmpty(&ps->pushst);

	free(ps);



}

3.2队列实现栈

思路:我们利用两个队列互相进出,实现栈的功能

//队列实现栈,使用两个队列
typedef struct stack {

	Que q1;
	Que q2;


}stack;

void Init()//栈的初始化
{
	stack* temp = (stack*)malloc(sizeof(stack));
	QueueInit(&temp->q1);
	QueueInit(&temp->q2);
	return temp;


}
void Push(stack* ps,int x)//入不为空的队列
{
	if (!QueueEmpty(&ps->q1))
	{
		QueuePush(&ps->q1, x);
	}
	else
	{
		QueuePush(&ps->q2, x);
	}


}

int MystackPop(stack* ps,int x)//出栈,我们把不为空的那个队列size-1个元素导入另一个队列,
{                               //剩下那个队列里的那个元素就是我们要出栈的那个元素
	Que* empty = &ps->q1;
	Que* noempty = &ps->q2;
	if (!QueueEmpty(&ps->q1))
	{
		empty = &ps->q2;
		noempty = &ps->q1;
	}
	while (QueueSize(noempty) > 1)//将size-1个元素导入空的队列
	{
		QueuePush(empty, QueueFront(noempty));
		QueuePop(noempty);
        }
	int top = QueueFront(noempty);//获取要出栈的元素
	QueuePop(noempty);//获取完,将元素出队,变成一个空队列

}
int MystzckTop(stack* ps)//取栈顶元素,栈顶元素就是不为空队列的队尾元素
{
	if (!QueueEmpty(&ps->q1))
	{
		return QueueBack(&ps->q1);
	}
	else
	{
		return QueueEmpty(&ps->q2);
	}
}
bool MystackEmptroy(stack* ps)//判空
{
	return QueueEmpty(&ps->q1) && QueueEmpty(&ps->q2);

}

void Free(stack* ps)//结构体内开有两个队列,释放必须先free掉里面的q1,q2然后再free掉ps,不然内存泄露
{
	QueueDestroy(&ps->q1);
	QueueDestroy(&ps->q2);
	free(ps);


}

3.3,栈实现括号匹配问题

bool isValid(char* s)
{
	ST st;
	STInit(&st);
	char topVal;
	while (*s)
	{
		if (*s == '(' || *s == '[' || *s == '{')
		{
			STPush(&st, *s);
		}
		else
		{
			if (STEmpty(&st))//如果*s不是以上的符号就没有入栈,说明数量不匹配(')','[',']','{','}'),直接结束
			{
				STDestroy(&st);
				return false;
			}
			topVal = STTop(&st);//如果遇到符号不和下列几种情况匹配,也是直接结束
			STop(&st);          //('{',]','[',']','(',')')
			if ((*s == ']' && topVal != '[')
				|| (*s == '}' && topVal != '{')
				|| (*s == ')' && topVal != '('))
			{
				STDestroy(&st);
				return false;
			}
		}
		++s;
	}
	bool ret = STEmpty(&st);//如果出完栈还有符号,说明数量不匹配('[',']','{','}','(')
	STDestroy(&st);
	return ret;
}

3.4,循环队列

 我们多开一个空间是为了方便判断满和空

               

typedef struct {
	int* a;
	int front;
	int rear;
	int k;

}MyCircularQueue;
MyCircularQueue* myCircularQueueCreat(int k)
{

	MyCircularQueue* obj = (MyCircularQueue*)malloc(sizeof(MyCircularQueue));
	int* a = (int*)malloc(sizeof(int) * k + 1);
	obj->k = k;
	obj->front = 0;
	obj->rear = 0;
	return obj;

}
bool myCircularQueueIsEmpty(MyCircularQueue* obj)
{
	return obj->front == obj->rear;
}
bool myCircularQueueIsFull(MyCircularQueue* obj)
{
	return (obj->rear + 1) % (obj->k + 1) == obj->front;//rear指向最后一个位置队列就满了

}
bool myCircularQueueEnqueue(MyCircularQueue* obj, int val)
{
	if (myCircularQueueIsEmpty(obj))
	{
		return false;
	}
	obj->a[obj->rear] = val;
	obj->rear++;
obj->rear = (obj->rear) % (obj->k + 1);

	return true;
}
bool  myCircularQueueDequeue(MyCircularQueue* obj)
{
	if (myCircularQueueIsEmpty(obj))
	{
		return false;
	}
	++obj->front;
	obj->front %= (obj->k + 1);
	return true;

}
int myCircularQueueFront(MyCircularQueue* obj)
{

	if (myCircularQueueIsEmpty(obj))
	{
		return -1;
	}
	return obj->a[obj->front];

}
int  myCircularQueueRear(MyCircularQueue* obj)
{
	if (myCircularQueueIsEmpty(obj))
	{
		return -1;
	}
	return obj->a[(obj->rear + obj->k) % (obj->k + 1)];
}
void  myCircularFree(MyCircularQueue* obj)//因为我们是在栈空间开辟的obj,然后又开辟了a的空间
并且用obj->a指向这个a的空间,所以要先释放里面的a最后释放obj
{
	free(obj->a);
	free(obj);


}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值