目录
栈
(1)先进后出
(2)只有一个口提供出入数据
(3)出入数据的口叫「栈顶」,另一端叫「栈底」
(4)压栈:插入数据
(5)出栈:删除数据
头文件
#pragma once
#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
#include<stdbool.h>
typedef int STDataType;
typedef struct Stack
{
STDataType* data;//存放的数据
int top;//指向栈顶元素的后一个位置
int capacity;//当前栈容量
}ST;
void StackInit(ST* ps);//初始化栈
void StackDestroy(ST* ps);//释放栈
void StackPush(ST* ps, STDataType x);//压栈
void StackPop(ST* ps);//出栈
STDataType StackTop(ST* ps);//获取栈顶第一个数据
bool StackEmpty(ST* ps);//判断栈是否为空
int StackSize(ST* ps);//获取栈中的元素个数
源文件
#include"Stack.h"
void StackInit(ST* ps)//初始化栈
{
assert(ps);
ps->capacity = ps->top = 0;
ps->data = NULL;
}
void StackDestroy(ST* ps)//释放栈
{
assert(ps);
ps->capacity = ps->top = 0;
free(ps->data);
ps->data = NULL;
}
void ChackCap(ST* ps)//检查容量
{
assert(ps);
if (ps->capacity == ps->top)
{
int newcap = ps->capacity == 0 ? 4 : ps->capacity * 2;
STDataType* newdata = (STDataType*)realloc(ps->data, newcap * sizeof(STDataType));
assert(newdata);
ps->data = newdata;
ps->capacity = newcap;
}
}
void StackPush(ST* ps, STDataType x)//压栈
{
assert(ps);
ChackCap(ps);
ps->data[ps->top] = x;
ps->top++;
}
void StackPop(ST* ps)//出栈
{
assert(ps);
assert(!StackEmpty(ps));
ps->top--;
}
STDataType StackTop(ST* ps)//获取栈顶第一个数据
{
assert(ps);
assert(!StackEmpty(ps));
return ps->data[ps->top - 1];
}
bool StackEmpty(ST* ps)//判断栈是否为空
{
assert(ps);
return ps->top == 0;
}
int StackSize(ST* ps)//获取栈中的元素个数
{
assert(ps);
return ps->top;
}
队列
(1)先进先出
(2)只允许在一端插入数据,在另一端删除数据,但两端都能获取数据
(3)队尾:插入数据的一端
(4)对头:删除数据的一端
头文件
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<stdbool.h>
#include<assert.h>
typedef int QDataType;
typedef struct QueueNode//节点
{
QDataType val;
struct QueueNode* next;
}QNode;
typedef struct Queue//用于控制所有节点整体的起始位置与末尾位置
{
QNode* head;
QNode* tail;
}Queue;
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);//获取队尾数据
bool QueueEmpty(Queue* pq);//判断队列是否为空
int QueueSize(Queue* pq);//获取队列中的元素数量
源文件
#define _CRT_SECURE_NO_WARNINGS
#include"Queue.h"
void QueueInit(Queue* pq)//初始化队列
{
assert(pq);
pq->head = pq->tail = NULL;
}
void QueueDestroy(Queue* pq)//释放队列
{
assert(pq);
QNode* cur = pq->head;
while (cur)
{
QNode* del = cur;
cur = cur->next;
free(del);
del = NULL;
}
pq->head = pq->tail = NULL;
}
QNode* BuyQNode(QDataType x)//创建新节点
{
QNode* newnode = (QNode*)malloc(sizeof(QNode));
assert(newnode);
newnode->val = x;
newnode->next = NULL;
return newnode;
}
void QueuePush(Queue* pq, QDataType x)//插入数据
{
assert(pq);
QNode* newnode = BuyQNode(x);
if (pq->head == NULL)
{
pq->head = pq->tail = newnode;
}
else
{
pq->tail->next = newnode;
pq->tail = pq->tail->next;
}
}
void QueuePop(Queue* pq)//删除数据
{
assert(pq);
assert(!QueueEmpty(pq));
QNode* del = pq->head;
pq->head = pq->head->next;
free(del);
del = NULL;
}
QDataType QueueFront(Queue* pq)//获取队头数据
{
assert(pq);
assert(!QueueEmpty(pq));
return pq->head->val;
}
QDataType QueueBack(Queue* pq)//获取队尾数据
{
assert(pq);
assert(!QueueEmpty(pq));
return pq->tail->val;
}
bool QueueEmpty(Queue* pq)//判断队列是否为空
{
assert(pq);
return pq->head == NULL;
}
int QueueSize(Queue* pq)//获取队列中的元素数量
{
assert(pq);
int count = 0;
QNode* cur = pq->head;
while (cur)
{
count++;
cur = cur->next;
}
return count;
}
相关练习
(1)20. 有效的括号 - 力扣(LeetCode) https://leetcode.cn/problems/valid-parentheses/
思路:遇到左括号就压栈,遇到右括号就将左括号出栈与其比较是否匹配。
bool isValid(char * s)
{
ST st;
StackInit(&st);
while (*s)
{
//当遇到左括号时
if ((*s == '(') || (*s == '[') || (*s == '{'))
{
StackPush(&st, *s);
}
//当遇到右括号时
else
{
//若栈为空,说明数量不匹配,返回假
if (StackEmpty(&st))
{
return false;
}
char tmp = StackTop(&st);
StackPop(&st);
if ((*s == ')' && tmp != '(')
|| (*s == ']' && tmp != '[')
|| (*s == '}' && tmp != '{'))
{
StackDestroy(&st);
return false;
}
}
s++;
}
//若栈内还有数据,则说明数量不匹配,反回假
bool returnvalue = StackEmpty(&st) == true ? true : false;
StackDestroy(&st);
return returnvalue;
}
(2)225. 用队列实现栈 - 力扣(LeetCode) https://leetcode.cn/problems/implement-stack-using-queues/
思路:使用两个队列,始终保持其中一个为空,当使用 StackPop 时,将非空队列除最后一个元素外的全部元素移动到空队列中,再将该元素 Pop
typedef struct {//创建两个队列
Queue q1;
Queue q2;
} MyStack;
MyStack* myStackCreate() {//创建栈并初始化
MyStack* obj = (MyStack*)malloc(sizeof(MyStack));
QueueInit(&obj->q1);
QueueInit(&obj->q2);
return obj;
}
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);
}
//pop 剩余的最后一个元素并返回
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) {
QueueDestroy(&obj->q1);
QueueDestroy(&obj->q2);
free(obj);
obj = NULL;
}
(3)232. 用栈实现队列 - 力扣(LeetCode) https://leetcode.cn/problems/implement-queue-using-stacks/
思路:使用两个栈,分别专门用来 Push 和 Pop,当需要 Pop 队列时,将 Push 栈中的数据 Push 到 Pop 栈中,这时栈中的数据位置就和原来相反,可以直接 Pop
typedef struct {//创建两个栈
ST pushST;
ST popST;
} MyQueue;
MyQueue* myQueueCreate() {//创建队列并初始化
MyQueue* obj = (MyQueue*)malloc(sizeof(MyQueue));
StackInit(&obj->popST);
StackInit(&obj->pushST);
return obj;
}
void myQueuePush(MyQueue* obj, int x) {//直接插入
StackPush(&obj->pushST, x);
}
int myQueuePop(MyQueue* obj) {
//若 Pop 栈为空,从 Push 栈中 Push 数据
if (StackEmpty(&obj->popST))
{
while (!StackEmpty(&obj->pushST))
{
StackPush(&obj->popST, StackTop(&obj->pushST));
StackPop(&obj->pushST);
}
}
//Pop 并返回数据
int pop = StackTop(&obj->popST);
StackPop(&obj->popST);
return pop;
}
int myQueuePeek(MyQueue* obj) {
//若 Pop 栈为空,从 Push 栈中 Push 数据
if (StackEmpty(&obj->popST))
{
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) {
StackDestroy(&obj->pushST);
StackDestroy(&obj->popST);
free(obj);
obj = NULL;
}
(4)622. 设计循环队列 - 力扣(LeetCode) https://leetcode.cn/problems/design-circular-queue/
思路:使用数组存放数据,当向后越界时指回下标 0,当向前越界时指回最后一个元素的下标;分别使用 front 和 back 区分头尾,由于当队列为空或为满时,front 和 back 都指向同一个位置,所以开辟空间时多开辟一个元素的空间,用来区分队列为空或为满(front == back 为空)(back + 1 == front 为满)。
typedef struct {
int* data;//存放数据
int front;//头
int back;//尾
int n;//可存放元素总个数
} MyCircularQueue;
MyCircularQueue* myCircularQueueCreate(int k) {
MyCircularQueue* obj = (MyCircularQueue*)malloc(sizeof(MyCircularQueue));
obj->data = (int*)malloc(sizeof(int) * (k + 1));//多开辟一个元素的空间用来区分队列空、满
obj->front = obj->back = 0;
obj->n = k + 1;
return obj;
}
//由于这两个函数在实现前需要用到,所以提前写声明
bool myCircularQueueIsEmpty(MyCircularQueue* obj);
bool myCircularQueueIsFull(MyCircularQueue* obj);
bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {
if (myCircularQueueIsFull(obj))
return false;
obj->data[obj->back] = value;
obj->back++;
obj->back %= obj->n;
return true;
}
bool myCircularQueueDeQueue(MyCircularQueue* obj) {
if (myCircularQueueIsEmpty(obj))
return false;
obj->front++;
//front 可能在最后一个元素的位置,++ 后超出空间,取模后正好回到 0 下标
obj->front %= obj->n;
return true;
}
int myCircularQueueFront(MyCircularQueue* obj) {
if (myCircularQueueIsEmpty(obj))
return - 1;
return obj->data[obj->front];
}
int myCircularQueueRear(MyCircularQueue* obj) {
if (myCircularQueueIsEmpty(obj))
return - 1;
//bake 可能在下标 0 位置,- 1 后越界,+ n 后正好指向最后一个下标
return obj->data[(obj->back - 1 + obj->n) % obj->n];
}
bool myCircularQueueIsEmpty(MyCircularQueue* obj) {
return obj->front == obj->back;
}
bool myCircularQueueIsFull(MyCircularQueue* obj) {
//+ 1 后可能越界,需取模回到下标 0
return (obj->back + 1) % obj->n == obj->front;
}
void myCircularQueueFree(MyCircularQueue* obj) {
free(obj->data);
obj->data = NULL;
free(obj);
obj = NULL;
}