[数据结构笔记]栈与队列

栈的实现

Stack.h

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

typedef int STDatatype;

typedef struct Stack {
	STDatatype* arr;//存放数据的顺序表 
	int capacity;//容量(可存放的元素个数)
	int top;//栈顶元素的下一位的下标。(未保存元素时指向0号位)
}ST;

void StackInit(ST* ps);//初始化
void StackDestroy(ST* ps);//销毁栈
bool StackEmpty(ST* ps);//检测是否为空
int StackSize(ST* ps);//返回既存元素数量
STDatatype StackTop(ST* ps);//取栈顶元素

void StackPush(ST* ps, STDatatype x);//值为x的元素入栈
void StackPop(ST* ps);//栈顶元素出栈

Stack.c

#include"Stack.h"

//初始化
void StackInit(ST* ps) {
	assert(ps);//初始化不负责开结构体,所以ps不可能为空,需要断言检查
	ps->arr = (STDatatype*)malloc(sizeof(STDatatype) * 4);//先开4个元素大小的空间
	if (ps->arr == NULL) {//检查malloc的结果
		perror("malloc failed");
		exit(-1);
	}
	ps->top = 0;//栈顶元素的下一位的下标。未保存元素时指向0号位,则保存一个元素后会指向1号位。
		//↑若要top指向栈顶元素,则表示未保存元素的值应初始化为-1。
	ps->capacity = 4;//初始容量为4
}

//销毁栈
void StackDestroy(ST* ps) {
	assert(ps);//确保结构体不为空
	free(ps->arr);//释放栈体
	ps->arr = NULL;//释放后置空相应指针
	ps->top = ps->capacity = 0;//归零其余变量
}

//检测是否为空
bool StackEmpty(ST* ps) {
	assert(ps);//确保结构体不为空
	return ps->top == 0;//判断的结果本身就是布尔值
}

//返回既存元素数量
int StackSize(ST* ps) {
	assert(ps);//确保结构体不为空
	return ps->top;//在top初始为0的情况下,top的值即为元素数量。
}

//取栈顶元素
STDatatype StackTop(ST* ps) {
	assert(ps);//确保结构体不为空
	assert(!StackEmpty(ps));//确保栈不为空
	return ps->arr[ps->top - 1];//在top初始为0的情况下,top是栈顶后一位的下标,向前一位才是栈顶的下标
}

//值为x的元素入栈
void StackPush(ST* ps, STDatatype x) {
	assert(ps);//确保结构体不为空
	if (ps->top == ps->capacity) {//栈满则扩容
		STDatatype* tmp = (STDatatype*)realloc(ps->arr, sizeof(STDatatype) * ps->capacity * 2);//扩到两倍尺寸
		if (tmp == NULL) {//检查扩容结果 
			perror("realloc failed");
			exit(-1);
		}
		ps->arr = tmp;//交付扩容结果
		ps->capacity *= 2;//更新容量标识
	}
	ps->arr[ps->top] = x;//根据top位置引导在栈顶存入数据
	ps->top++;//top进位
	//↑若top初始为-1,则应当先将top进位再存入数据
}

//栈顶元素出栈
void StackPop(ST* ps) {
	assert(ps);//确保结构体不为空
	assert(!StackEmpty(ps));//确保栈不为空
	ps->top--;//top向前退一位
	//动态开辟的一段空间不能只free一部分,所以这里只能令要“删除”的元素无效化,而不能将其释放
}

队列

队列的实现

Queue.h

#pragma once

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

typedef int QDataType;

//链表队列的子结构:节点
typedef struct QueueNode {
	QDataType data;//节点中的元素
	struct QueueNode* next;//指向下一个节点的地址
}QNode;

//队列结构
typedef struct Queue {//要同时控制多个值,使用结构会比较方便(也是代替二级指针形式的一种方案)
	QNode* head;//队头节点
	QNode* tail;//队尾节点
	int size;//队内节点数
}Queue;

void QueueInit(Queue* pq);//初始化队列
void QueueDestroy(Queue* pq);//销毁队列
bool QueueEmpty(Queue* pq);//检查队列是否为空

void QueuePush(Queue* pq, QDataType x);//插入节点。队列的出入是单向的,所以不会同时存在头插和尾插
void QueuePop(Queue* pq);//删除节点。与插入同理,头删和尾删只能二选一
//这里使用尾插头删的方案

//即便是简单的功能也应做成函数供调用,方便维护,降低耦合,
//,而不应令用户在使用功能时必须关注具体的实现方式,即便用户是自己
QDataType QueueFront(Queue* pq);//获取队头节点存放的值
QDataType QueueBack(Queue* pq);//获取队尾节点存放的值

int QueueSize(Queue* pq);//获取队列节点数

Queue.c

#include"Queue.h"

//初始化队列
void QueueInit(Queue* pq) {
	assert(pq);//确保pq不为空
	pq->head = NULL;//队头初始为空
	pq->tail = NULL;//队尾初始为空
	pq->size = 0;//队中初始时无节点
}

//销毁队列
void QueueDestroy(Queue* pq) {
	assert(pq);//确保pq不为空
	QNode* cur = pq->head;//从队头开始遍历,逐个节点释放
	while (cur) {
		QNode* del = cur;//标记cur当前所指的节点为要释放的节点
		cur = cur->next;//cur向后挪一位
		free(del);//释放del标记的节点
		//这里不需要del=NULL,因为局部变量出当前函数就自动销毁,不会在作为野指针的情况下被意外访问
	}
	pq->head = pq->tail = NULL;//这个需要置空,因为若作为野指针存在有可能被意外访问
	pq->size = 0;//size归零
}

//检查队列是否为空
bool QueueEmpty(Queue* pq) {
	assert(pq);//确保pq不为空
	return pq->head == NULL && pq->tail == NULL;//队头与队尾同时为空则队列为空
}

//插入节点
void QueuePush(Queue* pq, QDataType x) {
	assert(pq);//确保pq不为空
	QNode* newnode = (QNode*)malloc(sizeof(QNode));//申请空间。不需要专门写BuyNode函数,因为这里只有这个Push函数需要开节点
	if (newnode == NULL) {//检查空间申请结果
		perror("malloc failed");
		exit(-1);
	}
	newnode->data = x;//为元素赋值x
	newnode->next = NULL;//由于是尾插,下一位置空

	if (pq->tail == NULL) {//队内尚无节点,则令头与尾都为新节点
		pq->head = pq->tail = newnode;
	}
	else {//队内已有节点,则先将尾节点的下一位绑定到新节点,再将新节点设为尾
		pq->tail->next = newnode;
		pq->tail = newnode;
	}
	pq->size++;//节点数量加一
}

//删除节点
void QueuePop(Queue* pq) {
	assert(pq);//确保pq不为空
	assert(!QueueEmpty(pq));//确保队列不为空

	if (pq->head->next == NULL) {//若队内只有一个节点,即head==tail时
		free(pq->head);//直接释放队头
		pq->head = pq->tail = NULL;//此时head与tail都需要置空,因为队内只有一个节点时头与尾指向同一个地址 
	}
	else {//若队内不止一个节点
		QNode* del = pq->head;//标记要删除的节点。
		pq->head = pq->head->next;//令头节点的后一位成为头节点
			//尾节点的next为空,所以连续pop时head最后会自动置空
		free(del);//释放del标记的节点
		//该部分只能处理head 
		//因此,若只有该部分而不对队内只有一个节点的情况单独处理,
		//,则当只剩一个节点时,会在head置空并释放del后使tail成为野指针
	}
	//相对于这里的把只剩一个节点的情况单独拎出来处理的方式,
	//另一种处理只剩一个节点时tail的置空问题的方式是,
	//在最后检查head是否为空,若head为空则tail也置空
	pq->size--;//节点数量减一
}

//获取队头节点存放的值
QDataType QueueFront(Queue* pq) {
	assert(pq);//确保pq不为空
	assert(!QueueEmpty(pq));//确保队列不为空
	return pq->head->data;//返回头节点元素的数据
}

//获取队尾节点存放的值
QDataType QueueBack(Queue* pq) {
	assert(pq);//确保pq不为空
	assert(!QueueEmpty(pq));//确保队列不为空
	return pq->tail->data;//返回尾节点元素的数据
}

//获取队列节点数
int QueueSize(Queue* pq) {
	assert(pq);//确保pq不为空
	return pq->size;//直接返回结构体中size变量的值
}

例题

使用栈判断括号有效性

在这里插入图片描述

//思路:
//遇到左括号就把左括号入栈
//遇到右括号就先检查栈是否为空,若不为空则把栈顶元素出栈并与右括号匹配,以检测右括号有效性
//最后检查栈是否为空,即所有左括号是否都被匹配

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

 //----------实现栈----------
typedef char STDatatype;
typedef struct Stack {
	STDatatype* arr;
	int capacity;
	int top;
}ST;
void StackInit(ST* ps) {
	assert(ps);
	ps->arr = (STDatatype*)malloc(sizeof(STDatatype) * 4);
	if (ps->arr == NULL) {
		perror("malloc failed");
		exit(-1);
	}
	ps->top = 0;
	ps->capacity = 4;
}
void StackDestroy(ST* ps) {
	assert(ps);
	free(ps->arr);
	ps->arr = NULL;
	ps->top = ps->capacity = 0;
}
void StackPush(ST* ps, STDatatype x) {
	assert(ps);
	if (ps->top == ps->capacity) {
		STDatatype* tmp = (STDatatype*)realloc(ps->arr, sizeof(STDatatype) * ps->capacity * 2);
		if (tmp == NULL) {
			perror("realloc failed");
			exit(-1);
		}
		ps->arr = tmp;
		ps->capacity *= 2;
	}
	ps->arr[ps->top] = x;
	ps->top++;
}
bool StackEmpty(ST* ps) {
	assert(ps);
	return ps->top == 0;
}
void StackPop(ST* ps) {
	assert(ps);
	assert(!StackEmpty(ps));
	ps->top--;
}
STDatatype StackTop(ST* ps) {
	assert(ps);
	assert(!StackEmpty(ps));
	return ps->arr[ps->top - 1];
}
int StackSize(ST* ps) {
	assert(ps);
	return ps->top;
}
//----------实现栈----------

//检测括号匹配
bool isValid(char* s) {
	ST st;
	StackInit(&st);
	while (*s) {//\0值为0
		if (*s == '[' || *s == '(' || *s == '{') {
			//遇到左括号,则按顺序入栈
			StackPush(&st, *s);
			s++;//入栈后指针指向下一位
		}
		else {
			//遇到右括号,先检查栈是否为空,不为空则将其与栈顶的左括号匹配
			if (StackEmpty(&st)) {
				StackDestroy(&st);//每个返回前都要确保释放本函数临时开辟的空间,防止内存泄漏
				return false;//遇到右括号而栈内无左括号,该右括号无效,返回false
			}
			char top = StackTop(&st);
			StackPop(&st);
			if (*s == ')' && top != '(' ||
				*s == ']' && top != '[' ||
				*s == '}' && top != '{') {
				StackDestroy(&st);//每个返回前都要确保释放本函数临时开辟的空间,防止内存泄漏
				return false;//匹配失败,无效的右括号,返回false
			}
			else {
				s++;//匹配成功或者并非括号,向后一位
			}
		}
	}
	bool ret = StackEmpty(&st);//栈若不为空则还有未匹配的左括号,这些左括号是无效的
	StackDestroy(&st);
	return ret;
}

用队列实现栈

在这里插入图片描述

//本题并不关心效率,考察栈与队列的性质,将队列转换为栈
//队列先进先出,栈后进先出
//思路:
//开两个队列,每次只向其中一个push,
//,栈要pop时就将存了元素的队列挨个pop并将pop出的元素挨个push到之前未存元素的队列,
//,当pop到只剩下一个元素时,将这个剩下的元素作为可供栈pop的元素,如此实现后进先出

//--------------实现队列--------------
typedef int QDataType;
typedef struct QueueNode {
	QDataType data;
	struct QueueNode* next;
}QNode;
typedef struct Queue {
	QNode* head;
	QNode* tail;
	int size;
}Queue;
void QueueInit(Queue* pq) {
	assert(pq);
	pq->head = NULL;
	pq->tail = NULL;
	pq->size = 0;
}
void QueueDestroy(Queue* pq) {
	assert(pq);
	QNode* cur = pq->head;
	while (cur){
		QNode* del = cur;
		cur = cur->next;
		free(del);
	}
	pq->head = pq->tail = NULL;
	pq->size = 0;
}
void QueuePush(Queue* pq, QDataType x) {
	assert(pq);
	QNode* newnode = (QNode*)malloc(sizeof(QNode));
	if (newnode == NULL) {
		perror("malloc failed");
		exit(-1);
	}
	newnode->data = x;
	newnode->next = NULL;
	if (pq->tail == NULL) {
		pq->head = pq->tail = newnode;
	}
	else{
		pq->tail->next = newnode;
		pq->tail = newnode;
	}
	pq->size++;
}
void QueuePop(Queue* pq) {
	assert(pq);
	assert(!QueueEmpty(pq));
	if (pq->head->next == NULL) {
		free(pq->head);
		pq->head = pq->tail = NULL;
	}
	else{
		QNode* del = pq->head;
		pq->head = pq->head->next;
		free(del);
	}
	pq->size--;
}
QDataType QueueFront(Queue* pq) {
	assert(pq);
	assert(!QueueEmpty(pq));
	return pq->head->data;
}
QDataType QueueBack(Queue* pq) {
	assert(pq);
	assert(!QueueEmpty(pq));
	return pq->tail->data;
}
int QueueEmpty(Queue* pq) {//不明白为何这里用bool会编译错误说隐式声明啥的,非得改成int
	assert(pq);
	return pq->head == NULL && pq->tail == NULL;
}
int QueueSize(Queue* pq) {
	assert(pq);
	return pq->size;
}
//--------------实现队列--------------

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) {
	//push到非空队列
	if (!QueueEmpty(&obj->q1)) {
		QueuePush(&obj->q1,x);
	}
	else{
		QueuePush(&obj->q2, x);
	}
}

int myStackPop(MyStack* obj) {
	//先假定q1为空q2不为空
	Queue* emptyQ = &obj->q1;
	Queue* nonemptyQ = &obj->q2;
	if (!QueueEmpty(&obj->q1)){
		//若判断结构与假设不符,则交换
		emptyQ = &obj->q2;
		nonemptyQ = &obj->q1;
	}
	while (QueueSize(nonemptyQ)>1){
		//当非空的队列剩余节点数大于1,则从非空队列队头开始依次将元素push进空队列,并依次pop该非空队列节点
		QueuePush(emptyQ, QueueFront(nonemptyQ));//非空队列的队头插入到空队列
		QueuePop(nonemptyQ);
	}
	//非空队列只剩一个节点时
	int top = QueueFront(nonemptyQ);//这里题目指定了数据类型为int,直接用int
	QueuePop(nonemptyQ);//pop掉最后这个节点,原非空队列成为空队列
	return top;//至此,最后进队列的元素被作为栈顶先pop
}

int myStackTop(MyStack* obj) {
	//使用QueueBack接口,取非空队列的队尾
	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) {
	//本题的栈是套了队列的,而队列是套了单链表的,若直接释放obj并不会使已使用的单链表被释放,进而内存泄漏
	//所以应从基础的结构开始释放
	QueueDestroy(&obj->q1);
	QueueDestroy(&obj->q2);
	free(obj);//最后释放obj
}

/**
 * Your MyStack struct will be instantiated and called as such:
 * MyStack* obj = myStackCreate();
 * myStackPush(obj, x);
 
 * int param_2 = myStackPop(obj);
 
 * int param_3 = myStackTop(obj);
 
 * bool param_4 = myStackEmpty(obj);
 
 * myStackFree(obj);
*/

用栈实现队列

在这里插入图片描述

//本题与用队列实现栈类似,使数据在两个栈间转移以获得不同于单个栈的进出顺序
//一个栈负责被push,另一个负责向外pop

//----------实现栈----------
typedef int STDatatype;
typedef struct Stack {
	STDatatype* arr;
	int capacity;
	int top;
}ST;
void StackInit(ST* ps) {
	assert(ps);
	ps->arr = (STDatatype*)malloc(sizeof(STDatatype) * 4);
	if (ps->arr == NULL) {
		perror("malloc failed");
		exit(-1);
	}
	ps->top = 0;
	ps->capacity = 4;
}
void StackDestroy(ST* ps) {
	assert(ps);
	free(ps->arr);
	ps->arr = NULL;
	ps->top = ps->capacity = 0;
}
void StackPush(ST* ps, STDatatype x) {
	assert(ps);
	if (ps->top == ps->capacity) {
		STDatatype* tmp = (STDatatype*)realloc(ps->arr, sizeof(STDatatype) * ps->capacity * 2);
		if (tmp == NULL) {
			perror("realloc failed");
			exit(-1);
		}
		ps->arr = tmp;
		ps->capacity *= 2;
	}
	ps->arr[ps->top] = x;
	ps->top++;
}
bool StackEmpty(ST* ps) {
	assert(ps);
	return ps->top == 0;
}
void StackPop(ST* ps) {
	assert(ps);
	assert(!StackEmpty(ps));
	ps->top--;
}
STDatatype StackTop(ST* ps) {
	assert(ps);
	assert(!StackEmpty(ps));
	return ps->arr[ps->top - 1];
}
int StackSize(ST* ps) {
	assert(ps);
	return ps->top;
}
//----------实现栈----------

typedef struct {
	ST pushst;
	ST popst;
} MyQueue;


MyQueue* myQueueCreate() {
	MyQueue* pq = (MyQueue*)malloc(sizeof(MyQueue));
	StackInit(&pq->pushst);
	StackInit(&pq->popst);
	return pq;
}

void myQueuePush(MyQueue* obj, int x) {
	assert(obj);
	StackPush(&obj->pushst, x);
}

bool myQueueEmpty(MyQueue* obj) {
	assert(obj);
	return StackEmpty(&obj->pushst) && StackEmpty(&obj->popst);
}

int myQueuePeek(MyQueue* obj) {//取队头
	assert(obj);
	assert(!myQueueEmpty(obj));

	if (StackEmpty(&obj->popst)){
		//若pop栈为空,则从push栈向pop栈依次转移数据直到push栈为空
		while (!StackEmpty(&obj->pushst)){
			StackPush(&obj->popst, StackTop(&obj->pushst));
			StackPop(&obj->pushst);
		}
	}
	//从pop栈取栈顶作为队列的pop结果
	return StackTop(&obj->popst);
}

int myQueuePop(MyQueue* obj) {
	assert(obj);
	assert(!myQueueEmpty(obj));

	int peek = myQueuePeek(obj);
	StackPop(&obj->popst);
	return peek;
}

void myQueueFree(MyQueue* obj) {//同队列实现栈的情况,这里有两层结构,需要先从内层开始释放,否则便会内存泄漏
	assert(obj);

	StackDestroy(&obj->pushst);
	StackDestroy(&obj->popst);

	free(obj);
}

/**
 * Your MyQueue struct will be instantiated and called as such:
 * MyQueue* obj = myQueueCreate();
 * myQueuePush(obj, x);

 * int param_2 = myQueuePop(obj);

 * int param_3 = myQueuePeek(obj);

 * bool param_4 = myQueueEmpty(obj);

 * myQueueFree(obj);
*/

环形队列

在这里插入图片描述

//思路:
//顺序表(数组)更便于实现环形队列.
//front==rear即为空队列.
//rear+1==front并不能有效判断满队列,比如rear在数组尾部时.
//为解决队尾指针rear在数组尾部时判断满队列的问题,
//,设满队列长度为k,数组长度k+1,(rear+1)%(k+1)==front 为有效的判满方式
//当rear在front前,(rear+1)%(k+1)==rear+1,若为空则满足rear+1==front
//当rear在front后,若队为空则rear在数组尾部且front在数组头部,等式也成立
//若不用取模的方式,也可以通过if来专门分情况处理

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

typedef struct {
	int* a;//数组,做成动态的
	int front;//队头指针
	int rear;//队尾指针,所指位置为空位,前一位为队尾
	int k;//满队列长度,为数组长度-1
} MyCircularQueue;

MyCircularQueue* myCircularQueueCreate(int k) {
	MyCircularQueue* obj = (MyCircularQueue*)malloc(sizeof(MyCircularQueue));
	//创建动态数组,大小为队列长度+1,使用一个空位以允许使用rear==front和rear+1==front解决判空判满的问题
	obj->a = (int*)malloc(sizeof(int) * (k + 1));
	obj->front = obj->rear = 0;
	obj->k = k;
	return obj;
}

bool myCircularQueueIsEmpty(MyCircularQueue* obj) {
	assert(obj);
	return obj->rear == obj->front;
}

bool myCircularQueueIsFull(MyCircularQueue* obj) {
	assert(obj);
	return ((obj->rear + 1) % (obj->k + 1)) == obj->front;
}

bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {
	assert(obj);
	if (myCircularQueueIsFull(obj)) {
		return false;
	}
	obj->a[obj->rear] = value;
	obj->rear++;
	obj->rear %= (obj->k + 1);//解决从数组尾部跳转到头部的情况(回绕问题)
	return true;
}

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

int myCircularQueueFront(MyCircularQueue* obj) {
	assert(obj);
	if (myCircularQueueIsEmpty(obj)) {
		return -1;
	}
	return obj->a[obj->front];
}

int myCircularQueueRear(MyCircularQueue* obj) {
	assert(obj);
	if (myCircularQueueIsEmpty(obj)) {
		return -1;
	}
	return obj->a[(obj->rear + obj->k) % (obj->k + 1)];
	//(rear-1 + k+1)% (k+1)应对rear在数组首位时的情况
	//也可以int rear = obj->rear == 0 ? obj->k : obj->rear - 1;然后return obj->a[rear]
}

void myCircularQueueFree(MyCircularQueue* obj) {
	//多层结构先释放内层,这里先释放的是数组
	free(obj->a);
	free(obj);
}

/**
 * Your MyCircularQueue struct will be instantiated and called as such:
 * MyCircularQueue* obj = myCircularQueueCreate(k);
 * bool param_1 = myCircularQueueEnQueue(obj, value);

 * bool param_2 = myCircularQueueDeQueue(obj);

 * int param_3 = myCircularQueueFront(obj);

 * int param_4 = myCircularQueueRear(obj);

 * bool param_5 = myCircularQueueIsEmpty(obj);

 * bool param_6 = myCircularQueueIsFull(obj);

 * myCircularQueueFree(obj);
*/
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值