c语言——栈和队列

1.栈

1.1栈的概念及结构

1.1.1概念

:一种特殊的线性表,其只允许在固定的一端进行插入和删除元素操作。进行数据插和删除操作的一端称为栈顶,另一端称为栈底。栈中的数据元素遵守后进先出LIFO(LastInFirstOut)的则。
压栈 :栈的插入操作叫做进栈/压栈/入栈,入数据在栈顶。
出栈:栈的删除操作叫做出栈。出数据也在栈顶。

1.1.2结构

数组栈


链式栈

如果用尾做栈顶,尾插尾删,要设计成双向链表,否则删除数据效率低

如果用头做栈顶,头插头删,就可以设计成单链表

两种都可以,非要选一种,数组栈结构稍微好一点。

1.2接口函数

cpp 复制代码
//要改变结构体的内容
//传结构体的指针
void StackInit(ST* ps);
void StackDestory(ST* ps);
void StackPush(ST* ps,STDataType x);
void StackPop(ST* ps);
STDataType StackTop(ST* ps);
//取栈顶的数据
int StackSize(ST* ps);
//栈中元素个数
bool StackEmpty(ST* ps);
//判断栈是否为空

1.3详细代码

cpp 复制代码
#pragma once
#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 StackInit(ST* ps)
{
	assert(ps);
	ps->a = NULL;
	ps->top = 0;
	//初始化时,top给的是0,
	// 意味着top指向栈顶数据的下一个
	// ps->top=-1;
	//初始化时,top给的是 - 1,
	// 意味着top指向栈顶数据
	ps->capacity = 0;
}

void StackDestory(ST* ps)
{
	assert(ps);
	free(ps->a);
	ps->a = NULL;
	ps->top = 0;
	ps->capacity = 0;
}

void StackPush(ST* ps, STDataType x)
{
	assert(ps);
	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)
		{
			printf("realloc fail\n");
			exit(-1);
		}
		ps->a = tmp;
		ps->capacity = newCapacity;
	}
	ps->a[ps->top] = x;
	ps->top++;
}

void StackPop(ST* ps)
{
	assert(ps);
	assert(ps->top > 0);
	//栈不为空才能删
	ps->top--;
}

STDataType StackTop(ST* ps)
{
	assert(ps);
	assert(ps->top > 0);
	//栈不为空才能取栈顶元素
	return ps->a[ps->top - 1];
}

int StackSize(ST* ps)
{
	assert(ps);
	return ps->top;
}

bool StackEmpty(ST* ps)
{
	assert(ps);
	/*
	if (ps->top == 0)
		return true;
	else
		return false;
	*/
	return ps->top == 0;
}

void TestStack()
{
	ST st;
	StackInit(&st);
	StackPush(&st, 1);
	StackPush(&st, 2);
	StackPush(&st, 3);
	StackPush(&st, 4);
	
	//遍历栈中的数据
	while (!StackEmpty(&st))
	{
		printf("%d ", StackTop(&st));
		StackPop(&st);
	}

	StackDestory(&st);
}

int main()
{
	TestStack();
	return 0;
}

1.4例题:有效括号

给定一个只包括 '('')''{''}''['']' 的字符串 s ,判断字符串是否有效。

有效字符串需满足:

  1. 左括号必须用相同类型的右括号闭合。
  2. 左括号必须以正确的顺序闭合。
  3. 每个右括号都有一个对应的相同类型的左括号。
cpp 复制代码
bool isValid(char* s) 
{
    struct Stack st;
    Init(&st);
    while(*s)
    {
        //左括号入栈
        if(*s=='(' || *s=='[' || *s=='{')
        {
            Push(&st,*s);
            s++;
        }
        //剩下的都是右括号
        else
        {
            //如果没有左括号和剩下的右括号匹配
            //就不是有效括号
            if(isEmpty(&st))
                return false;
            //取出栈顶的左括号
            char top=GetTop(&st);
            Pop(&st);
            //如果左括号和右括号不匹配
            //就不是有效括号
            if((top=='(' && *s!=')')||
                (top=='[' && *s!=']')||
                (top=='{' && *s!='}'))
            {
                Destory(&st);
                return false;
            }
            //匹配了就往后继续判断
            else
                s++;
        }
    }
    //如果列表的左括号和右括号都能匹配上
    //则执行完while循环后,栈为空
    if(isEmpty(&st))
        return true;
    Destory(&st);
    return false;
}

2.队列

2.1队列的概念及结构

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

2.2队列的实现

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

2.2.1接口函数

cpp 复制代码
void QueueInit(Queue* pq);
void QueueDestroy(Queue* pq);
void QueuePush(Queue* pq,QDataType x);
void QueuePop(Queue* pq);
QDataType QueueFront(Queue* pq);
QDataType QueueBack(Queue* pq);
int QueueSize(Queue* pq);
bool QueueEmpty(Queue* pq);
2.2.1.1定义队列
cpp 复制代码
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>

typedef int QDataType;

//这是单链表,要有节点
typedef struct QueueNode
{
	struct QueueNode* next;
	QDataType data;
}QueueNode;

//为了方便尾插,除了有一个头指针,
//还要有一个尾指针
//所以定义一个结构体出来
typedef struct Queue
{
	QueueNode* head;
	QueueNode* tail;
}Queue;
2.2.1.2初始化队列
cpp 复制代码
//传结构体的指针,无需二级指针
void QueueInit(Queue* pq)
{
	assert(pq);
	pq->head = NULL;
	pq->tail = NULL;
}
2.2.1.3销毁队列
cpp 复制代码
void QueueDestory(Queue* pq)
{
	assert(pq);
	QueueNode* cur = pq->head;
	while (cur != NULL)
	{
		QueueNode* next = cur->next;
		free(cur);
		cur = next;
	}
	pq->head = NULL;
	pq->head = NULL;
}
2.2.1.4队尾入
cpp 复制代码
void QueuePush(Queue* pq, QDataType x)
{
	assert(pq);
	QNode* newnode = (QNode*)malloc(sizeof(QNode));
	if (newnode == NULL)
	{
		printf("malloc fail.\n");
		exit(-1);
	}
	newnode->data = x;
	newnode->next = NULL;
	//如果没有节点
	if (pq == NULL)
	{
		pq->head = newnode;
		pq->tail = newnode;
	}
	//有节点就直接插入、链接
	else
	{
		//head		tail	newnode
		pq->tail->next = newnode;
		pq->tail = newnode;
	}
}
2.2.1.5队头出
cpp 复制代码
void QueuePop(Queue* pq)
{
	assert(pq);
	//队列不能为空
	assert(pq->head);

	//如果只有一个节点
	if (pq->head->next == NULL)
	{
		free(pq->head);
		pq->head = NULL;
		pq->tail = NULL;
	}

	//先保存下一个
	QNode* next = pq->head->next;
	free(pq->head);
	pq->head = next;
}
2.2.1.6判断队列是否为空
cpp 复制代码
bool QueueEmpty(Queue* pq)
{
	assert(pq);
	return pq->head == NULL;
}
2.2.1.7获取队头的数据
cpp 复制代码
QDataType QueueFront(Queue* pq)
{
	assert(pq);
	assert(pq->head);
	return pq->head->data;
}
2.2.1.8获取队尾的数据
cpp 复制代码
QDataType QueueBack(Queue* pq)
{
	assert(pq);
	assert(pq->head);
	return pq->tail->data;
}
2.2.1.9获取队列大小
cpp 复制代码
int QueueSize(Queue* pq)
{
	assert(pq);
	int size = 0;
	QNode* cur = pq->head;
	while (cur)
	{
		size++;
		cur = cur->next;
	}
	return size;
}
2.2.1.10销毁队列
cpp 复制代码
void QueueDestory(Queue* pq)
{
	assert(pq);
	QNode* cur = pq->head;
	while (cur)
	{
		QNode* next = cur->next;
		free(cur);
		cur = next;
	}
	pq->head = NULL;
	pq->tail = NULL;
}
2.2.1.11测试
cpp 复制代码
void Test()
{
	Queue q;
	QueueInit(&q);
	QueuePush(&q, 1);
	QueuePush(&q, 2);
	QueuePush(&q, 3);
	QueuePush(&q, 4);
	QueuePop(&q);
	QueuePush(&q, 5);

	printf("size=%d\n", QueueSize(&q));
	printf("head=%d\n", QueueFront(&q));
	printf("tail=%d\n", QueueBack(&q));
	while (!QueueEmpty(&q))
	{
		printf("%d ", QueueFront(&q));
		QueuePop(&q);
	}

	QueueDestroy(&q);
}

3.测试

3.1用栈实现队列

cpp 复制代码
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>
typedef int Type;
struct Stack
{
	int top;
	int capacity;
	Type* a;
};

void Init(struct Stack* st)
{
	assert(st);
	st->a = NULL;
	st->top = 0;
	st->capacity = 0;
}

void Push(struct Stack* st,Type x)
{
	int newCapacity = 4;
	if (st->top == st->capacity)
	{
		if (st->top != 0)
			newCapacity = st->capacity * 2;
		Type* tmp = (Type*)realloc(st->a,sizeof(Type)*newCapacity);
		if (tmp == NULL)
		{
			printf("realloc fail.\n");
			exit(-1);
		}
		st->a = tmp;
		st->capacity = newCapacity;
	}
	st->a[st->top] = x;
	st->top++;
}

void Pop(struct Stack* st)
{
	assert(st);
	assert(st->top > 0);
	st->top--;
}

Type Top(struct Stack* st)
{
	assert(st);
	assert(st->top > 0);
	return st->a[st->top - 1];
}

Type Peek(struct Stack* st)
{
	assert(st);
	assert(st->top > 0);
	return st->a[0];
}

bool isEmpty(struct Stack* st)
{
	assert(st);
	return st->top == 0;
}

int Size(struct  Stack* st)
{
	assert(st);
	return st->top;
}

void Destory(struct Stack* st)
{
	assert(st);
	free(st->a);
	st->capacity = 0;
	st->top = 0;
}

void Print(struct Stack* st)
{
	int i = 0;
	for (i = 0;i < st->top;i++)
		printf("%d ", st->a[i]);
	printf("\n");
}


typedef struct 
{
    struct Stack st1;
    struct Stack st2;
} MyQueue;


MyQueue* myQueueCreate() 
{
    MyQueue* q=(MyQueue*)malloc(sizeof(MyQueue));
    Init(&q->st1);
    Init(&q->st2);
    return q;
}

void myQueuePush(MyQueue* obj, int x) 
{
    Push(&obj->st1,x);
}

int myQueuePop(MyQueue* obj) 
{
    int top=0;
    int ret=Peek(&obj->st1);
    while(Size(&obj->st1)>1)
    {
        top=Top(&obj->st1);
        Push(&obj->st2,top);
        Pop(&obj->st1);
    }
    Pop(&obj->st1);
    while(Size(&obj->st2))
    {
        top=Top(&obj->st2);
        Push(&obj->st1,top);
        Pop(&obj->st2);
    }
    return ret;
}

int myQueuePeek(MyQueue* obj) 
{
    return Peek(&obj->st1);    
}

bool myQueueEmpty(MyQueue* obj) 
{
    return isEmpty(&obj->st1);
}

void myQueueFree(MyQueue* obj) 
{
    Destory(&obj->st1);
    Destory(&obj->st2);
}

3.2用队列实现栈

cpp 复制代码
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>
typedef int Type;
struct QueueNode
{
	Type val;
	struct QueueNode* next;
};
struct Queue
{
	struct QueueNode* head;
	struct QueueNode* tail;
};

void Init(struct Queue* pq)
{
	assert(pq);
	pq->head = NULL;
	pq->tail = NULL;
}

void Push(struct Queue* pq, Type x)
{
	struct QueueNode* newnode = (struct QueueNode*)malloc(sizeof(struct QueueNode));
	if (newnode == NULL)
	{
		printf("malloc fail.\n");
		exit(-1);
	}
	newnode->val = x;
	newnode->next = NULL;
	if (pq->head == NULL)
	{
		pq->head = newnode;
		pq->tail = newnode;
		return;
	}
	pq->tail->next = newnode;
	pq->tail = newnode;
}

void Pop(struct Queue* pq)
{
	assert(pq);
	assert(pq->head);
	if (pq->head->next == NULL)
	{
		free(pq->head);
		pq->head = NULL;
		return;
	}
	struct QueueNode* nxt = pq->head->next;
	free(pq->head);
	pq->head = nxt;
}

Type Front(struct Queue* pq)
{
	assert(pq);
	assert(pq->head);
	return pq->head->val;
}

Type Back(struct Queue* pq)
{
	assert(pq);
	assert(pq->head);
	return pq->tail->val;
}

int Size(struct Queue* pq)
{
	assert(pq);
	struct QueueNode* cur = pq->head;
	int size = 0;
	while (cur)
	{
		size++;
		cur = cur->next;
	}
	return size;
}

bool isEmpty(struct Queue* pq)
{
	assert(pq);
	return pq->head == NULL;
}

void Destory(struct Queue* pq)
{
	assert(pq);
	struct QueueNode* cur = pq->head;
	while (cur)
	{
		struct QueueNode* nxt = cur->next;
		free(cur);
		cur = nxt;
	}
	pq->head = NULL;
	pq->tail = NULL;
}


typedef struct
{
	struct Queue q1;
	struct Queue q2;
} MyStack;


MyStack* myStackCreate()
{
	MyStack* st = (MyStack*)malloc(sizeof(MyStack));
	Init(&st->q1);
	Init(&st->q2);
	return st;
}

void myStackPush(MyStack* obj, int x)
{
	if (!isEmpty(&obj->q1))
		Push(&obj->q1,x);
	else
		Push(&obj->q2,x);
}

int myStackTop(MyStack* obj)
{
	if (!isEmpty(&obj->q1))
		return Back(&obj->q1);
	return Back(&obj->q2);
}

bool myStackEmpty(MyStack* obj)
{
	if (isEmpty(&obj->q1) && isEmpty(&obj->q2))
		return true;
	return false;
}

int myStackPop(MyStack* obj)
{
	if (myStackEmpty(obj))
		return -1;
	int top = 0;
    int ret=myStackTop(obj);
	if (!isEmpty(&obj->q1))
	{
		while (Size(&obj->q1)>1)
		{
            top =Front(&obj->q1);
			Pop(&obj->q1);
			Push(&obj->q2, top);
		}
		Pop(&obj->q1);
	}
	else if (!isEmpty(&obj->q2))
	{
		while (Size(&obj->q2)>1)
		{
            top = Front(&obj->q2);
			Pop(&obj->q2);
			Push(&obj->q1, top);
		}
		Pop(&obj->q2);
	}
	return ret;
}

void myStackFree(MyStack* obj)
{
	Destory(&obj->q1);
	Destory(&obj->q2);
    obj=NULL;
}

3.3设计循环队列

无论使用数组实现还是链表实现,都要多开一个空间。如果设计存k个数据的循环队列,就要开k+1个空间。否则无法实现判空和判满。

①数组实现

cpp 复制代码
typedef struct 
{
    int* a;
    int size;
    int head;
    int tail;
} MyCircularQueue;


MyCircularQueue* myCircularQueueCreate(int k) 
{
    MyCircularQueue* cq=(MyCircularQueue*)malloc(sizeof(MyCircularQueue));
    cq->a=(int*)malloc(sizeof(int)*(k+1));
    cq->head=0;
    cq->tail=0;
    cq->size=k;
    return cq;
}

bool myCircularQueueIsEmpty(MyCircularQueue* obj) 
{
    return (obj->head==obj->tail);
}

bool myCircularQueueIsFull(MyCircularQueue* obj) 
{
    if(obj->head==0 && obj->tail+obj->head==obj->size)
        return true;
    if(obj->tail<obj->head && obj->tail+1==obj->head)
        return true;
    return false;
}

bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) 
{
    if(myCircularQueueIsFull(obj))
        return false;
    obj->tail++;
    obj->tail=obj->tail%(obj->size+1);
    obj->a[obj->tail]=value;
    return true;
}

bool myCircularQueueDeQueue(MyCircularQueue* obj) 
{
    if(myCircularQueueIsEmpty(obj))
        return false;
    obj->head++;
    obj->head=obj->head%(obj->size+1);
    return true;
}

int myCircularQueueFront(MyCircularQueue* obj) 
{
    if(myCircularQueueIsEmpty(obj))
        return -1;
    int index=(obj->head+1);
    if(index>obj->size)
        index=0;
    return obj->a[index];
}

int myCircularQueueRear(MyCircularQueue* obj) 
{
    if(myCircularQueueIsEmpty(obj))
        return -1;
    int index=obj->tail;
    return obj->a[index];
}

void myCircularQueueFree(MyCircularQueue* obj) 
{
    free(obj->a);
    free(obj);
}

②链表实现

cpp 复制代码
struct QueueNode
{
    int val;
    struct QueueNode* next;
};

typedef struct
{
    struct QueueNode* head;
    struct QueueNode* tail;
} MyCircularQueue;

struct QueueNode* BuyNode(int x)
{
    struct QueueNode* newnode = (struct QueueNode*)malloc(sizeof(struct QueueNode));
    if (newnode == NULL)
    {
        printf("malloc fail\n");
        exit(-1);
    }
    newnode->val = x;
    newnode->next = NULL;
    return newnode;
}

//构造器,设置队列长度为 k 
MyCircularQueue* myCircularQueueCreate(int k)
{
    MyCircularQueue* cq = (MyCircularQueue*)malloc(sizeof(MyCircularQueue));
    int i = 0;
    cq->head = BuyNode(0);
    cq->tail = cq->head;
    struct QueueNode* cur = cq->head;
    for (i = 0;i < k;i++)
    {
        struct QueueNode* newnode = BuyNode(0);
        cur->next = newnode;
        cur = cur->next;
    }
    cur->next = cq->head;
    return cq;
}

//检查循环队列是否为空。
bool myCircularQueueIsEmpty(MyCircularQueue* obj)
{
    return obj->head==obj->tail;
}

// 检查循环队列是否已满。
bool myCircularQueueIsFull(MyCircularQueue* obj)
{
    return ((obj->tail)->next == obj->head);
}

//向循环队列插入一个元素。如果成功插入则返回真。
bool myCircularQueueEnQueue(MyCircularQueue* obj, int value)
{
    if (myCircularQueueIsFull(obj))
        return false;
    //尾插
    obj->tail = obj->tail->next;
    (obj->tail)->val = value;
    return true;
}

//从循环队列中删除一个元素。如果成功删除则返回真。
bool myCircularQueueDeQueue(MyCircularQueue* obj)
{
    if (myCircularQueueIsEmpty(obj))
        return false;
    //头删
    obj->head = obj->head->next;
    return true;
}

//从队首获取元素。如果队列为空,返回 -1 
int myCircularQueueFront(MyCircularQueue* obj)
{
    if (myCircularQueueIsEmpty(obj))
        return -1;
    return (obj->head->next)->val;
}

//获取队尾元素。如果队列为空,返回 -1 。
int myCircularQueueRear(MyCircularQueue* obj)
{
    if (myCircularQueueIsEmpty(obj))
        return -1;
    return (obj->tail)->val;
}

//释放队列内存
void myCircularQueueFree(MyCircularQueue* obj)
{
    struct QueueNode* cur = obj->head;
    cur = cur->next;
    while (cur != obj->head)
    {
        struct QueueNode* nxt = cur->next;
        free(cur);
        cur = nxt;
    }
}
相关推荐
Scott9999HH3 小时前
【IIoT流量实战】蒸汽管道阀门全关却仍有流量?用 Python 实现涡街信号 FFT 频谱分析与温压全补偿积算网关,深度拆解靠谱的涡街流量计厂家硬核技术标准
开发语言·python
腻害兔3 小时前
【若依项目-产品经理视角】深度拆解 RuoYi-Vue-Pro 商城模块:从商品管理到交易引擎,50 张表撑起一整套电商系统
java·大数据·vue.js·产品经理·ai编程
码智社4 小时前
AES加密原理详解及Java实现加解密实战
java·开发语言
AI云海4 小时前
python 列表、元组、集合和字典
开发语言·python
萧瑟余晖5 小时前
JDK 26 新特性详解
java·开发语言
马优晨6 小时前
Freemarker 完整讲解(后端 Java 模板引擎)
java·开发语言·freemarker·freemarker 完整讲解·freemarker模板引擎
人邮异步社区7 小时前
怎么把C语言学到精通?
c语言·开发语言
心平气和量大福大8 小时前
C#-WPF-控件-TextBox 数据绑定
开发语言·c#·wpf
ttwuai8 小时前
Cursor 生成 CRUD 后,Go 后台接口别只测 200:JWT、RBAC 和 tenant_id 怎么验
开发语言·后端·golang
维天说8 小时前
CLI-Switch 2026年3月版历史设计:Hook、TTY 隔离与 JSON 状态
java·服务器·json