目录
栈
栈的定义和结构
一种特殊的线性表,进行数据插入和删除操作的一端称为栈顶,另一端称为栈底。
后进先出LIFO(Last In First Out)
压栈:栈的插入操作叫做进栈/压栈/入栈,入数据在栈顶。
出栈:栈的删除操作叫做出栈。出数据也在栈顶。
栈的实现
栈的实现一般可以使用数组或者链表实现,相对而言数组的结构实现更优一些。因为数组在尾上插入数据的代价比较小。
int top代表当前存放了多少个元素
int capacity 代表当前申请了多少内存
typedef int STDataType;
typedef struct Stack
{
STDataType* a;
int top;
int capacity;
}ST;
栈的初始化和释放
初始化时,top给值0,意味着 top指向着栈顶数据的下一个
初始化时,top给值-1,意味着top指向着栈顶数据
assert(ps),用于断言。
void StackInit(ST* ps)
{
assert(ps);
ps->a = NULL;
ps->capacity = 0;
ps->top = 0;
}
void StackDestroy(ST* ps)
{
assert(ps);
free(ps->a);
ps->a = NULL;
ps->capacity = ps->top = 0;
}
栈的头插与头删
前面初始化的时候,top值给的是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 = 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(!StackEmpty(ps));//此处是判断是否为空,函数在下面实现
ps->top--;
}
获取栈顶数据的值
因为ps->top指向的是栈顶数据的下一个空间,所以需要 -1
STDataType StackTop(ST* ps)
{
assert(ps);
assert(!StackEmpty(ps));
return ps->a[ps->top - 1];
}
获取栈的元素个数
直接访问top值即可
int StackSize(ST* ps)
{
assert(ps);
return ps->top;
}
检查是否栈是否为空
bool StackEmpty(ST* ps)
{
assert(ps);
return ps->top == 0;
}
队列
队列的定义和结构
队列只准许一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表
先进先出FIFO(First In First Out)
入队列:进行插入操作的一端称为队尾
出队列:进行删除操作的一端称为队头
队列的实现
队列也可以数组和链表的结构实现,使用链表的结构实现更优一些,因为如果使用数组的结构,出队列在数组头上出数据,效率会比较低。
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 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);
// 检测队列是否为空,如果为空返回非零结果,如果非空返回0
bool QueueEmpty(Queue* pq);
初始化和销毁队列
void QueueInit(Queue* pq)
{
assert(pq);
pq->head = NULL;
pq->tail = NULL;
}
void QueueDestroy(Queue* pq)
{
assert(pq);
QueueNode* cur = pq->head;
while (cur != NULL)
{
QueueNode* next = cur->next;
free(cur);
cur = next;
}
pq->head = pq->tail = NULL;
}
获取一个节点
每次插入都需要节点申请空间。
QueueNode* BuyQueueData(QDataType x)
{
QueueNode* newnode = (QueueNode*)malloc(sizeof(QueueNode));
assert(newnode);
newnode->data = x;
newnode->next = NULL;
return newnode;
}
队尾 入 队列
void QueuePush(Queue* pq, QDataType x)
{
assert(pq);
QueueNode* newnode = BuyQueueData(x);
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(!QueueEmpty(pq));
return pq->head->data;
}
// 获取队列队尾元素
QDataType QueueBack(Queue* pq)
{
assert(pq);
assert(!QueueEmpty(pq));
return pq->tail->data;
}
获取队列中有效元素个数
int QueueSize(Queue* pq)
{
assert(pq);
int n = 0;
QueueNode* cur = pq->head;
while (cur)
{
++n;
cur = cur->next;
}
return n;
}
检测队列是否为空
如果为空返回非零结果,如果非空返回0
bool QueueEmpty(Queue* pq)
{
assert(pq);
return pq->head == NULL;
}
相关OJ题
由于我们需要用到栈和队列的相关函数,所以我们需要将此前写的栈和队列函数复制到题中
1.括号的匹配
nullhttps://leetcode.cn/problems/valid-parentheses/
此题的思路:遇到左括号就入栈,遇到右括号就出栈匹配
某些特殊情况:只遇到右括号,前面没有左括号,不匹配,返回flase
如果栈不是空,说明栈中还有 左括号未出栈 , 没有匹配,返回是false
bool isValid(char * s)
{
ST st;
StackInit(&st);
while(*s)
{
if(*s == '('|| *s == '[' || *s == '{')
{
StackPush(&st,*s);
++s;
}else
{
//遇到 右括号,
//前面没有左括号,不匹配,返回flase
if(StackEmpty(&st)){
StackDestroy(&st);
return false;
}
STDataType top = StackTop(&st);
StackPop(&st);
if((*s == '}' && top != '{')
||(*s == ']' && top != '[')
||(*s == ')' && top != '('))
{
StackDestroy(&st);
return false;
}else
{
++s;
}
}
}
//如果栈不是空,说明栈中还有 左括号未出栈
//没有匹配,返回是false
bool ret = StackEmpty(&st);
if(ret == 0){
return false;
}
StackDestroy(&st);
return true;
}
2.用队列实现栈
队列为先进先出,栈为先进后出。
核心思路
1.入数据,往不为空的队列入、保持另一个队列为空
2.出数据的时候,依次出队头的数据,转移另一个队列保存, 只剩最后一个时,Pop掉
故此我们需要两个队列q1、q2。 假设q2为空。
将q1的元素进入q2中,当q1为最后一个元素的时候,释放掉。
反之也是如此。谁空进谁,然后释放最后一个元素。
力扣https://leetcode.cn/problems/implement-stack-using-queues/1.首先创建两个队列
typedef struct {
Queue q1;
Queue q2;
} MyStack;
MyStack* myStackCreate() {
MyStack* st = (MyStack*)malloc(sizeof(MyStack));
QueueInit(&st->q1);
QueueInit(&st->q2);
return st;
}
2.入列
入数据,往不为空的队列入、保持另一个队列为空
void myStackPush(MyStack* obj, int x) {
if(!QueueEmpty(&obj->q1)){
QueuePush(&obj->q1,x);
}
else{
QueuePush(&obj->q2,x);
}
}
3.出数据的时候,依次出队头的数据,转移另一个队列保存, 只剩最后一个时,Pop掉
假设q1为空。如果q1不为空,那边放入q2中
int myStackPop(MyStack* obj) {
Queue* emptyQ = &obj->q1;
Queue* nonemptyQ = &obj->q2;
if(!QueueEmpty(&obj->q1)){
emptyQ = &obj->q2;
nonemptyQ = &obj->q1;
}
//每次取非空数列的 放到空数列,直接到剩下一个数据
while(QueueSize(nonemptyQ)>1){
QueuePush(emptyQ,QueueFront(nonemptyQ));
QueuePop(nonemptyQ);
}
int top = QueueFront(nonemptyQ);
QueuePop(nonemptyQ);//干掉最后进来的数据 后进先出
return top;
}
4.获取队列队尾元素即实现的栈的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);
}
用栈实现队列
力扣https://leetcode.cn/problems/implement-queue-using-stacks/核心思路与队列实现栈相似
1.创建两个栈
typedef struct {
ST pushST;
ST popST;
} MyQueue;
MyQueue* myQueueCreate() {
MyQueue* obj = (MyQueue*)malloc(sizeof(MyQueue));
StackInit(&obj->pushST);
StackInit(&obj->popST);
return obj;
}
2.如数据
void myQueuePush(MyQueue* obj, int x) {
StackPush(&obj->pushST,x);
}
3.出数据
int myQueuePop(MyQueue* obj) {
//如果popST中没有数据,就将pushST中倒过去,popST中的数据就符合先进先出
if(StackEmpty(&obj->popST))
{
while(!StackEmpty(&obj->pushST))
{
StackPush(&obj->popST,StackTop(&obj->pushST));
StackPop(&obj->pushST);
}
}
int front = StackTop(&obj->popST);
StackPop(&obj->popST);
return front;
}
4.队列的实现
int myQueuePeek(MyQueue* obj) {
if(StackEmpty(&obj->popST))
{
while(!StackEmpty(&obj->pushST))
{
StackPush(&obj->popST,StackTop(&obj->pushST));
StackPop(&obj->pushST);
}
}
return StackTop(&obj->popST);
}
5判断是否为空与释放空间
bool myQueueEmpty(MyQueue* obj) {
return StackEmpty(&obj->pushST) && StackEmpty(&obj->popST);
}
void myQueueFree(MyQueue* obj) {
StackDestroy(&obj->pushST);
StackDestroy(&obj->popST);
free(obj);
}
3.循环队列
力扣https://leetcode.cn/problems/design-circular-queue/
该情况下,无法区分空间空和满。
解决方案:
1.增加一个size 用来计数,
2.多开一个空间,但不存储数据
1.创建
typedef struct {
int* a;
int front;
int tail;
int k;
} MyCircularQueue;
MyCircularQueue* myCircularQueueCreate(int k) {
MyCircularQueue* cq = ( MyCircularQueue*)malloc(sizeof(MyCircularQueue));
cq->a = malloc(sizeof(int)*(k+1));
cq->front = cq->tail = 0;
cq->k = k;
return cq;
}
2.判断队列空或者满
bool myCircularQueueIsEmpty(MyCircularQueue* obj) {
return obj->front == obj->tail;
}
bool myCircularQueueIsFull(MyCircularQueue* obj) {
return (obj->tail+1)%(obj->k+1) == obj->front;
}
3.插入一个数据
bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {
if(myCircularQueueIsFull(obj))
{
return false;
}
obj->a[obj->tail] = value;
++obj->tail;
if(obj->tail == obj->k+1){//当长度0的时候
obj->tail =0;
}
return true;
}
4.从循环队列中删除一个元素。如果成功删除则返回真。
bool myCircularQueueDeQueue(MyCircularQueue* obj) {
if(myCircularQueueIsEmpty(obj)){
return false;
}
++obj->front;
if(obj->front == obj->k+1){
obj->front =0;
}
return true;
}
5.获取首队列元素
int myCircularQueueFront(MyCircularQueue* obj) {
if(myCircularQueueIsEmpty(obj)){
return -1;
}
return obj->a[obj->front];
}
6.获取队尾元素
int myCircularQueueRear(MyCircularQueue* obj) {
if(myCircularQueueIsEmpty(obj))
return -1;
if(obj->tail == 0)
return obj->a[obj->k];
else
return obj->a[obj->tail-1];
}
7.释放空间
void myCircularQueueFree(MyCircularQueue* obj) {
free(obj->a);
free(obj);
}