栈和队列的实现及基本操作(C语言实现)

目录

栈和队列的概念

什么是栈

什么是队列

栈的实现

 代码实现

Stack.h

Stack.c

Test.c

队列的实现

代码实现

Queue.h

Queue.c

 入队操作QueuePush

出队QueuePop

Test.c

栈和队列的相关例题(leetCode)

有效的括号

用队列实现栈

用栈实现队列

设计循环队列

什么是循环队列

 设计循环队列的问题


栈和队列的概念

什么是栈

栈:可以想象成桌面上的一堆文件或纸张,如果你要从顶部添加或者移除文件或纸张,那么最后一个放上去的文件会被第一个取出来,这就符合栈的特点(后进先出)
栈的特点:后进先出或先进后出
核心操作:入栈,出栈,获取栈顶的数据
压栈:栈的插入操作叫做进栈/压栈/入栈,入数据在栈顶。
出栈:栈的删除操作叫做出栈。出数据也在栈顶。
栈不像链表可以在两端进行操作,而栈只能在固定的一端进行操作
栈出栈的时机会影响出队的顺序:

假设数组左边是栈低,右边是栈顶

比如:入栈  1  2  3  4   5

最后:出栈的顺序就是  5   4  3   2   1


比如:入栈 1   2   3

          出栈一次,出栈顶的数据 3

          再入 4  5  6
最后:出栈顺序就是 3  6  5  4  3  2  1

如图所示:

什么是队列

队列:我们可以想象成在超市买东西需要排队,最前面的人先出队,最后的人后出队,就和队列的特点匹配上(先进先出或后进后出)
队列的特点:先进先出或后进后出
核心操作:入队,出队,获取队头的数据
入队列:进行插入操作的一端称为队尾
出队列:进行删除操作的一端称为队头

队列出队的时机不会影响出队的顺序


入队: 1 2 3 

出队一次,出的是对头的数据 1,入队 4 5 6
最后出队结果:1 2 3 4 5 6

如图所示:

栈的实现

实现栈可以用链表也可以用数组
实现栈建议用数组:数组更简单
栈的实现和顺序表的实现类似
栈顶top和顺序表的size一个意思,用来表示数据的有效个数,在栈中用top表示,指向最后一个元素的下一个位置

用数组表示: 

用链表表示:

 代码实现

Stack.h

#pragma once
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
typedef int STDataType;
typedef struct Stack
{
	STDataType* _a;
	int _top;  // size = top  // 栈顶下标
	int _capacity;
}Stack;

//初始化
void StackInit(Stack* pst);
// 入栈
void StackPush(Stack* pst, STDataType x);
//出栈
void StackPop(Stack* pst);
//求数据个数
int StackSize(Stack* pst);
//返回1是空,返回0是非空
int StackEmpty(Stack* pst);
//获取栈顶数据
STDataType StackTop(Stack* pst);
//销毁栈
void StackDestory(Stack* pst);

Stack.c

#define _CRT_SECURE_NO_WARNINGS 1
#include "Stack.h"

//初始化
void StackInit(Stack* pst)
{
	assert(pst);
	/*pst->_a = NULL;
	pst->_top = 0;
	pst->_capacity = 0;*/
	pst->_a = (STDataType*)malloc(sizeof(STDataType) * 4);
	if (pst->_a == NULL)
		return;
	pst->_top = 0;
	pst->_capacity = 4;
}

// 入栈
void StackPush(Stack* pst, STDataType x)
{
	assert(pst);
	if (pst->_top == pst->_capacity)
	{
		pst->_capacity *= 2;
		STDataType* tmp = realloc(pst->_a, sizeof(STDataType) * pst->_capacity);
		if (tmp != NULL)
		{
			pst->_a = tmp;
		}
	}
	pst->_a[pst->_top] = x;
	pst->_top++;
}
//出栈
void StackPop(Stack* pst)
{
	assert(pst);
	assert(pst->_top > 0);
	--(pst->_top);
}
//求数据个数
int StackSize(Stack* pst)
{
	assert(pst);
	return  pst->_top;
}
//返回1是空,返回0是非空
int StackEmpty(Stack* pst)
{
	assert(pst);
	return pst->_top == 0 ? 1 : 0;
	//return !(pst->_top);
}
//获取栈顶数据
STDataType StackTop(Stack* pst)
{
	assert(pst);
	assert(pst->_top > 0);
	return pst->_a[pst->_top - 1];
}

//销毁栈
void StackDestory(Stack* pst)
{
	assert(pst);
	free(pst->_a);
	pst->_a = NULL;
	pst->_top = 0;
	pst->_capacity = 0;
}

Test.c

#define _CRT_SECURE_NO_WARNINGS 1
#include "Stack.h"
//栈的作用:
// 1,如果有后进先出需求的地方(比如迷宫问题、递归改成非递归)
void TestStack()
{
	Stack st;
	StackInit(&st);
	StackPush(&st, 1);
	StackPush(&st, 2);

	printf("%d ", StackTop(&st));
	StackPop(&st);

	StackPush(&st, 3);
	StackPush(&st, 4);

	while (!StackEmpty(&st))
	{
		printf("%d ", StackTop(&st));
		StackPop(&st);
	}
	printf("\n");
	StackDestory(&st);
}

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

我们在入栈1   2  然后打印栈顶的数据 2,打印完之后出栈(出栈顶的数据2),再次入栈  3  4 while循环判断栈是否为空,StackEmpty 返回 0 表示栈里面有数据,所以取反,为真,打印栈顶的数据,再出栈(出栈顶的数据),不断循环直到栈中出栈到没有数据。最后销毁栈。

打印结果: 2  4  3  1

队列的实现

实现队列可以用链表也可以用数组
实现队列建议用链表:链表操作更简单
队列的实现和单链表的实现类似

 用数组表示:

用链表表示:

代码实现

Queue.h

#pragma once
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
typedef int QDataType;

//队列中的结点
typedef struct QueueNode
{
	struct QueueNode* _next;
	QDataType _data;
}QueueNode;

//队列
typedef struct Queue
{
	QueueNode* _head;
	QueueNode* _tail;
}Queue;

//初始化
void QueueInit(Queue* pq);
//入队
void QueuePush(Queue* pq, QDataType x);
//出队,出队头的数据
void QueuePop(Queue* pq);
//返回队头的数据
QDataType QueueFront(Queue* pq);
//返回队尾的数据
QDataType QueueBack(Queue* pq);
//判断队列是否为空,如果为空返回 1,存在数据返回 0
int QueueEmpty(Queue* pq);
//求队列的有效数据个数
int QueueSize(Queue* pq);
//销毁队列
void QueueDestory(Queue* pq);

有一个Queue结构体,里面存储两个指针,用来指向队头和队尾,初始化结构体Queue中的指针_head==_tail ==NULL。

我们学习过单链表,外部有一个结构体指针,用来指向头结点,在队列中,外面有两个指针,一个用了存储头,一个用来存储尾,两者类似,只不过队列中外面的两个结构体指针在Queue结构体中

 如图:

Queue.c

#define _CRT_SECURE_NO_WARNINGS 1
#include "Queue.h"

//队列,先进先出的场景,比如要保持序列公平,排队抽号机
// 广度优先遍历
void QueueInit(Queue* pq)
{
	assert(pq);
	pq->_head = pq->_tail = NULL;
}

void QueuePush(Queue* pq, QDataType x)
{
	assert(pq);
	QueueNode* newnode = (QueueNode*)malloc(sizeof(QueueNode));
	if (newnode == NULL)
	{
		printf("内存不足\n");
		exit(-1);
	}
	newnode->_data = x;
	newnode->_next = NULL;
	if (pq->_head == NULL)
	{
		pq->_head = pq->_tail = newnode;
	}
	else
	{
		pq->_tail->_next = newnode;
		pq->_tail = newnode;
	}
}
void QueuePop(Queue* pq)
{
	assert(pq);
	assert(pq->_head);
	QueueNode* next = pq->_head->_next;
	free(pq->_head);
	pq->_head = next;
	if (pq->_head == NULL)
	{
		pq->_tail = NULL;
	}
}
QDataType QueueFront(Queue* pq)
{
	assert(pq);
	assert(pq->_head);
	return pq->_head->_data;
}
QDataType QueueBack(Queue* pq)
{
	assert(pq);
	assert(pq->_tail);
	return pq->_tail->_data;
}
//返回1是空,返回0是非空
int QueueEmpty(Queue* pq)
{
	assert(pq);
	return pq->_head == NULL ? 1 : 0;
}
int QueueSize(Queue* pq)
{
	assert(pq);
	QueueNode* cur = pq->_head;
	int size = 0;
	while (cur)
	{
		++size;
		cur = cur->_next;
	}
	return size;
}


void QueueDestory(Queue* pq)
{
	assert(pq);
	QueueNode* cur = pq->_head;
	while (cur)
	{
		QueueNode* next = cur->_next;
		free(cur);
		cur = next;
	}
	pq->_head = pq->_tail = NULL;
}
 入队操作QueuePush

如图:

出队QueuePop

如图:

Test.c

#define _CRT_SECURE_NO_WARNINGS 1
#include "Queue.h"

void QueueTest()
{
	Queue q;
	QueueInit(&q);
	QueuePush(&q, 1);
	QueuePush(&q, 2);

	printf("%d ", QueueFront(&q));
	QueuePop(&q);

	QueuePush(&q, 3);
	QueuePush(&q, 4);
	while (!QueueEmpty(&q))
	{
		printf("%d ", QueueFront(&q));
		QueuePop(&q);
	}
	QueueDestory(&q);
}

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

 打印结果:  1   2   3   4

栈和队列的相关例题(leetCode)

有效的括号

. - 力扣(LeetCode)

利用栈的性质来判断括号是不是有效,C语言中我们先写一个栈,才能初始化栈和调用栈的函数

bool isValid(char* s) {
 	Stack st;
	StackInit(&st);
	bool ret;
	while (*s != '\0') 
	{
		if (*s == '[' || *s == '{' || *s == '(') 
		{
			StackPush(&st, *s);
			++s;
		}
		else
		{
			if (StackEmpty(&st))
			{
				ret = false;
				break;
			}
			char top = StackTop(&st);
			if (*s == ']' && top != '[')
			{
				ret = false;
				break;
			}
			if (*s == '}' && top != '{')
			{
				ret = false;
				break;
			}
			if (*s == ')' && top != '(')
			{
				ret = false;
				break;
			}
			StackPop(&st);
			++s;
		}
	}
   // 如果都匹配上了,栈为空
	if (*s == '\0') 
		ret = StackEmpty(&st);
	StackDestory(&st);
	return ret;
}

用队列实现栈

. - 力扣(LeetCode)

在实现用队列实现栈的过程中,我们需要先实现一个队列

 思路:

typedef struct {
	Queue _q1;
	Queue _q2;
} MyStack;


MyStack* myStackCreate() 
{
	MyStack* st = (MyStack*)malloc(sizeof(MyStack));
	if (st == NULL)
		return NULL;
	QueueInit(&st->_q1);
	QueueInit(&st->_q2);
	return st;
}
void myStackPush(MyStack* obj, int x) 
{
	//不为空
	if (!QueueEmpty(&obj->_q1)) 
	{
		QueuePush(&obj->_q1, x);
	}
	else
	{
		QueuePush(&obj->_q2, x);
	}
}
int myStackPop(MyStack* obj) 
{
	Queue* empty = &obj->_q1;
	Queue* noempty = &obj->_q2;
	if (!QueueEmpty(&obj->_q1)) 
	{
		empty = &obj->_q2;
		noempty =&obj->_q1;
	}
	while (QueueSize(noempty) > 1) 
	{
		QueuePush(empty, QueueFront(noempty));
		QueuePop(noempty);
	}
	int top = QueueFront(noempty);
	QueuePop(noempty);
	return top;
}
int myStackTop(MyStack* obj) 
{
	if (!QueueEmpty(&obj->_q1)) 
	{
		return QueueBack(&obj->_q1);
	}
	else
	{
		return QueueBack(&obj->_q2);
	}
}
bool myStackEmpty(MyStack* obj) 
{
	return QueueEmpty(&obj->_q1) && QueueEmpty(&obj->_q2);
}
void myStackFree(MyStack* obj) 
{
	QueueDestory(&obj->_q1);
	QueueDestory(&obj->_q2);
	free(obj);
}

用栈实现队列

. - 力扣(LeetCode)

用栈实现队列和用队列实现栈原理类似,用两个栈实现队列的特点

 思路:

//用栈实现队列
MyQueue* myQueueCreate() 
{
	MyQueue* q = (MyQueue*)malloc(sizeof(MyQueue));
	if (q == NULL) 
		return NULL;
	StackInit(&q->_PushST);
	StackInit(&q->_PopST);
	return q;
}
void myQueuePush(MyQueue* obj, int x) 
{
	StackPush(&obj->_PushST, x);
}
int myQueuePop(MyQueue* obj) 
{
	int front =  myQueuePeek(obj);
	StackPop(&obj->_PopST);
	return front;
}
int myQueuePeek(MyQueue* obj) 
{
	if (!StackEmpty(&obj->_PopST)) 
	{
		return StackTop(&obj->_PopST);
	}
	else
	{
		while (!StackEmpty(&obj->_PushST))
		{
			StackPush(&obj->_PopST, StackTop(&obj->_PushST));
			StackPop(&obj->_PushST);
		}
		return StackTop(&obj->_PopST);
	}
}
bool myQueueEmpty(MyQueue* obj) 
{
	return StackEmpty(&obj->_PushST) && StackEmpty(&obj->_PopST);
}
void myQueueFree(MyQueue* obj) 
{
	StackEmpty(&obj->_PushST);
	StackEmpty(&obj->_PopST);
	free(obj);
}

设计循环队列

什么是循环队列

1,如图所示,逻辑结构是我们想象出来的,但是循环队列可以使链表也可以是数组。

2,在这个题目中循环队列的大小是固定的,不需要动态增长,所以我们使用数组来设计循环队列。

3,在数组中物理结构中,数组的 front 或者 rear 下标走到数组末尾,最后再走到 0 下标,逻辑上就是首尾相接。

4,队列满了就不允许入队。

逻辑结构:

 物理结构:

 设计循环队列的问题

这样的情况下我们无法区分队列满了还是队列为空的情况

针对无法区分队满还是对空的情况如何解决 ?

思路:我们需要留出一个空位置来,也就是数组的大小多开一个位置,以下图示都是在证明多开一个空间的好处。

只有入队操作:

 存在入队和出队操作:

不断出队:

. - 力扣(LeetCode)

// 设计循环队列
typedef struct {
    int* _a;
    int _front;
    int _rear;
    int _k;
} MyCircularQueue;

MyCircularQueue* myCircularQueueCreate(int k);

// 插入数据,成功返回真,否则返回假
bool myCircularQueueEnQueue(MyCircularQueue* obj, int value);

bool myCircularQueueDeQueue(MyCircularQueue* obj);

int myCircularQueueFront(MyCircularQueue* obj);
int myCircularQueueRear(MyCircularQueue* obj);

bool myCircularQueueIsEmpty(MyCircularQueue* obj);

bool myCircularQueueIsFull(MyCircularQueue* obj);

void myCircularQueueFree(MyCircularQueue* obj);

// 设计循环队列
MyCircularQueue* myCircularQueueCreate(int k) {
    MyCircularQueue* q = (MyCircularQueue*)malloc(sizeof(MyCircularQueue));
    if (q == NULL) {
        return NULL;
    }
    q->_a = (int*)malloc(sizeof(int) * (k + 1));
    q->_front = 0;
    q->_rear = 0;
    q->_k = k;
    return q;
}

// 插入数据,成功返回真,否则返回假
bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {
    if (myCircularQueueIsFull(obj)) {
        return false;
    } else {
        obj->_a[obj->_rear] = value;
        ++obj->_rear;
        obj->_rear = obj->_rear % (obj->_k + 1);
        return true;
    }
}

// 删除
bool myCircularQueueDeQueue(MyCircularQueue* obj) {
    if (myCircularQueueIsEmpty(obj)) {
        return false;
    } else {
        ++obj->_front;
        obj->_front = obj->_front % (obj->_k + 1);
        return true;
    }
}

int myCircularQueueFront(MyCircularQueue* obj) {
    if (myCircularQueueIsEmpty(obj))
        return -1;
    else
        return obj->_a[obj->_front];
}
int myCircularQueueRear(MyCircularQueue* obj) {
    if (myCircularQueueIsEmpty(obj)) {
        return -1;
    } else {
        int tail = obj->_rear - 1;
        if (tail == -1)
            tail = obj->_k;
        return obj->_a[tail];
    }
}

bool myCircularQueueIsEmpty(MyCircularQueue* obj) {
    return obj->_front == obj->_rear;
}

bool myCircularQueueIsFull(MyCircularQueue* obj) {
    return obj->_front == (obj->_rear + 1) % (obj->_k + 1);
}

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

  • 27
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值