提示:以下是本篇文章正文内容,下面案例可供参考
一、栈和队列概念
(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);
}
本文详细介绍了C语言中栈和队列的概念、基本操作,以及如何用栈和队列互换实现对方功能,还涉及括号匹配问题和循环队列的实现,展示了数据结构在编程中的应用。

被折叠的 条评论
为什么被折叠?



