系统性学习数据结构-第三讲-栈和队列
系统性学习数据结构-第三讲-栈和队列
- 1. 栈
- 1.1 栈和队列
- 1.2 栈的实现
- 2. 队列
- 2.1 概念与结构
- 2.2 队列的实现
- 3. 栈和队列算法题
- 3.1 [有效的括号](https://leetcode.cn/problems/valid-parentheses/description/)
- 3.2 [用队列实现栈](https://leetcode.cn/problems/implement-stack-using-queues/description/)
- 3.3 [用栈实现队列](https://leetcode.cn/problems/implement-queue-using-stacks/description/)
1. 栈
1.1 栈和队列
栈:⼀种特殊的线性表,其只允许在固定的⼀端进行插入和删除元素操作。进行数据插入和删除操作的⼀端称为栈顶,另⼀端称为栈底。
栈中的数据元素遵守后进先出 LIFO(Last In First Out)的原则。
压栈:栈的插入操作叫做进栈/压栈/入栈,入数据在栈顶。
出栈:栈的删除操作叫做出栈。出数据也在栈顶。
栈底层结构选型
栈的实现⼀般可以使用数组或者链表实现,相对而言数组的结构实现更优⼀些。因为数组在尾上插入数据的代价比较小。
1.2 栈的实现
stack. h
typedef int STDataType;
typedef struct Stack
{STDataType* a;int top;int capacity;
}ST;
// 初始化栈
void STInit(ST* ps);
// 销毁栈
void STDestroy(ST* ps);
// ⼊栈
void STPush(ST* ps, STDataType x);
//出栈
void STPop(ST* ps);
//取栈顶元素
STDataType STTop(ST* ps);
//获取栈中有效元素个数
int STSize(ST* ps);
//栈是否为空
bool STEmpty(ST* ps);
Stack.c
#include "Stack.h"void STInit(ST* ps)
{ps->arr = NULL;ps->capacity = ps->top = 0;
}void STDestroy(ST* ps)
{if(ps->arr)free(ps);ps->arr = NULL;ps->capacity = ps->top = 0;
}void StackPush(ST* ps, STDataType x)
{assert(ps);if (ps->capacity == ps->top){int NewCapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;STDataType* newStack = (STDataType*)realloc(ps->arr, sizeof(STDataType) * NewCapacity);if (newStack == NULL){perror("malloc fail:");exit(1);}ps->arr = newStack;ps->capacity = NewCapacity;}ps->arr[ps->top++] = x;
}void StackPop(ST* ps)
{assert(ps);ps->top--;
}bool StackEmpty(ST* ps)
{assert(ps);return ps->top == 0;
}STDataType StackTop(ST* ps)
{assert(ps);return ps->arr[ps->top - 1];
}int STSize(ST* ps)
{assert(ps);return ps->top;
}
2. 队列
2.1 概念与结构
概念:只允许在⼀端进行插入数据操作,在另⼀端进行删除数据操作的特殊线性表,队列具有先进先出 FIFO (First In First Out)
⼊队列:进行插入操作的⼀端称为队尾
出队列:进行删除操作的一端成为队头
队列底层结构选型
队列也可以数组和链表的结构实现,使用链表的结构实现更优⼀些,因为如果使用数组的结构,出队列在数组头上出数据,效率会比较低。
2.2 队列的实现
Queue.h
typedef int QDataType;
//队列结点结构
typedef struct QueueNode
{int val;struct QueueNode* next;
}QNode;
//队列结构
typedef struct Queue
{QNode* phead;QNode* ptail;int size;
}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);
Queue.c
#include"Queue.h"void QueueInit(Queue* pq)
{pq->head = pq->list = NULL;pq->size = 0;
}void QueueDestroy(Queue* pq)
{assert(pq);QueueNode* pcur = pq->head;while (pcur){QueueNode* next = pcur->next;free(pcur);pcur = next;}pq->head = pq->list = NULL;
}void QueuePush(Queue* pq, QDataTpe x)
{assert(pq);QueueNode* newnode = (QueueNode*)malloc(sizeof(QueueNode));if (newnode == NULL){perror("malloc fail:");exit(1);}newnode->data = x;newnode->next = NULL;if (pq->head == NULL){pq->head = pq->list = newnode;pq->size++;}else{pq->list->next = newnode;pq->size++;pq->list = pq->list->next;}
}bool QueueEmpty(Queue* pq)
{assert(pq);return pq->size == 0;
}void QueuePop(Queue* pq)
{assert(!(QueueEmpty(pq)));if (pq->list == pq->head){free(pq->head);pq->head = pq->list = NULL;}else{QueueNode* next = pq->head->next;free(pq->head);pq->head = next;}pq->size--;
}QDataTpe QueueFront(Queue* pq)
{assert(!(QueueEmpty(pq)));return pq->head->data;
}QDataTpe QueueBack(Queue* pq)
{assert(!(QueueEmpty(pq)));return pq->list->data;
}int QueueSize(Queue* pq)
{return pq->size;
}
3. 栈和队列算法题
3.1 有效的括号
typedef char StackDateTpye;
typedef struct Stack
{StackDateTpye* arr;int top; //指向栈顶的位置int capacity; //容量
}Stack;
//栈的初始化
void StackInit(Stack* st)
{assert(st);st->arr = NULL;st->capacity = 0;st->top = 0;
}//入栈-栈顶
void StackPush(Stack* st,StackDateTpye data)
{assert(st);if (st->top == st->capacity){int NewCapacity = st->capacity == 0 ? 4 : 2 * st->capacity;StackDateTpye* New = (StackDateTpye*)realloc(st->arr, NewCapacity*sizeof(Stack));if (New == NULL){perror("realloc fail:");exit(1);}st->arr = New; //将数组换到新地址st->capacity = NewCapacity; //将容量更新}st->arr[st->top++] = data; //将栈顶位置更新
}
//判断栈是否为空
bool StackEmpty(Stack* st)
{assert(st);return st->top == 0;
}//出栈
void StackPop(Stack* st)
{assert(!StackEmpty(st));st->top--;
}//取栈顶数据
StackDateTpye StackTopData(Stack* st)
{assert(!StackEmpty(st));return st->arr[st->top-1];
}void STDestroy(Stack* ps)
{if (ps->arr)free(ps->arr);ps->arr = NULL;ps->top = ps->capacity = 0;
}
//获取栈中有效元素个数
int StackSize(Stack* st)
{assert(st);return st->top;
}
bool isValid(char* s) {Stack* st=(Stack*)malloc(sizeof(Stack));StackInit(st);while(*s!='\0'){if(*s=='('||*s=='['||*s=='{'){StackPush(st,*s);} else{ if(StackEmpty(st)){STDestroy(st);return false;}if((*s==')'&&StackTopData(st)!='(')||(*s==']'&&StackTopData(st)!='[')||(*s=='}'&&StackTopData(st)!='{')){STDestroy(st);return false;}elseStackPop(st);}s++;}bool ret=StackEmpty(st)?true:false;STDestroy(st);return ret;}
在解答这道题时,我们使用上我们刚刚实现的栈结构,遇见左括号就入栈,遇见右括号就取栈顶与之配对,如果是对应的括号,
那就进行出栈操作,最后对栈进行判空,若为空则为有效的括号。
3.2 用队列实现栈
typedef int QDataTpe;
typedef struct QueueNode //队列节点的结构
{QDataTpe data;struct QueueNode* next;
}QueueNode;typedef struct Queue //队列的结构
{QueueNode* head; //队头QueueNode* list; //队尾int size; //有效数据个数
}Queue;bool QueueEmpty(Queue* pq)
{assert(pq);return pq->size == 0;
}void QueueInit(Queue* pq)
{pq->head = pq->list = NULL;pq->size = 0;
}void QueuePush(Queue* pq, QDataTpe x)
{assert(pq);QueueNode* newnode = (QueueNode*)malloc(sizeof(QueueNode));if (newnode == NULL){perror("malloc fail:");exit(1);}newnode->data = x;newnode->next = NULL;if (pq->head == NULL){pq->head = pq->list = newnode;pq->size++;}else{pq->list->next = newnode;pq->size++;pq->list = pq->list->next;}
}int QueueSize(Queue* pq)
{return pq->size;
}QDataTpe QueueFront(Queue* pq)
{assert(!(QueueEmpty(pq)));return pq->head->data;
}void QueuePop(Queue* pq)
{assert(!(QueueEmpty(pq)));if (pq->list == pq->head){free(pq->head);pq->head = pq->list = NULL;}else{QueueNode* next = pq->head->next;free(pq->head);pq->head = next;}pq->size--;
}QDataTpe QueueBack(Queue* pq)
{assert(!(QueueEmpty(pq)));return pq->list->data;
}void QueueDestroy(Queue* pq)
{assert(pq);QueueNode* pcur = pq->head;while (pcur){QueueNode* next = pcur->next;free(pcur);pcur = next;}pq->head = pq->list = NULL;
}typedef struct {Queue PushQueue;Queue PopQueue;
} MyStack;MyStack* myStackCreate() {MyStack* mystack = (MyStack*)malloc(sizeof(MyStack));QueueInit(&mystack->PushQueue);QueueInit(&mystack->PopQueue);return mystack;
}void myStackPush(MyStack* obj, int x) {QueuePush(&obj->PushQueue, x);
}int myStackPop(MyStack* obj) {while(QueueSize(&obj->PushQueue) != 1){int data = QueueFront(&obj->PushQueue);QueuePop(&obj->PushQueue);QueuePush(&obj->PopQueue, data);}int data = QueueFront(&obj->PushQueue);QueuePop(&obj->PushQueue);while(QueueSize(&obj->PopQueue) != 0){int data = QueueFront(&obj->PopQueue);QueuePop(&obj->PopQueue);QueuePush(&obj->PushQueue, data);}return data;
}int myStackTop(MyStack* obj) {return QueueBack(&obj->PushQueue);
}bool myStackEmpty(MyStack* obj) {if(QueueEmpty(&obj->PushQueue) && QueueEmpty(&obj->PopQueue))return true;elsereturn false;
}void myStackFree(MyStack* obj) {QueueDestroy(&obj->PushQueue);QueueDestroy(&obj->PopQueue);obj = NULL;
}/*** 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);
*/
在这道题中,我们创建两个队列,栈遵循先进后出的规律,所以仅仅一个队列是无法完成要求的,定义一个栈为 PushQueue
,
对于入栈的数据我们直接入到这个队列中,定义一个栈为 PopQueue
,对于出栈操作,我们就将 PushQueue
中除了队头的数据,
全部迁移到 PopQueue
中,然后对PushQueue
进行出队操作, 最后再将 PopQueue
中的数据再迁移回来即可,若要取栈顶元素,
我们就重复上述步骤,但不进行出队操作即可。
3.3 用栈实现队列
typedef int STDataType;
typedef struct Stack
{STDataType* arr;int top; //指向栈顶的位置int capacity; //容量
}ST;void STInit(ST* ps)
{ps->arr = NULL;ps->capacity = ps->top = 0;
}void STDestroy(ST* ps)
{if(ps->arr)free(ps);ps->arr = NULL;ps->capacity = ps->top = 0;
}void StackPush(ST* ps, STDataType x)
{assert(ps);if (ps->capacity == ps->top){int NewCapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;STDataType* newStack = (STDataType*)realloc(ps->arr, sizeof(STDataType) * NewCapacity);if (newStack == NULL){perror("malloc fail:");exit(1);}ps->arr = newStack;ps->capacity = NewCapacity;}ps->arr[ps->top++] = x;
}void StackPop(ST* ps)
{assert(ps);ps->top--;
}bool StackEmpty(ST* ps)
{assert(ps);return ps->top == 0;
}STDataType StackTop(ST* ps)
{assert(ps);return ps->arr[ps->top - 1];
}int STSize(ST* ps)
{assert(ps);return ps->top;
}typedef struct {ST PushStack;ST PopStack;
} MyQueue;MyQueue* myQueueCreate() {MyQueue* myQueue = (MyQueue*)malloc(sizeof(MyQueue));STInit(&myQueue->PushStack);STInit(&myQueue->PopStack);return myQueue;
}void myQueuePush(MyQueue* obj, int x) {StackPush(&obj->PushStack, x);
}int myQueuePop(MyQueue* obj) {while(STSize(&obj->PushStack) != 1){int top = StackTop(&obj->PushStack);StackPush(&obj->PopStack, top);StackPop(&obj->PushStack);}int final = StackTop(&obj->PushStack);StackPop(&obj->PushStack);while(STSize(&obj->PopStack) != 0){int top = StackTop(&obj->PopStack);StackPush(&obj->PushStack, top);StackPop(&obj->PopStack);}return final;
}int myQueuePeek(MyQueue* obj) {while(STSize(&obj->PushStack) != 1){int top = StackTop(&obj->PushStack);StackPush(&obj->PopStack, top);StackPop(&obj->PushStack);}int final = StackTop(&obj->PushStack);while(STSize(&obj->PopStack) != 0){int top = StackTop(&obj->PopStack);StackPush(&obj->PushStack, top);StackPop(&obj->PopStack);}return final;
}bool myQueueEmpty(MyQueue* obj) {if(StackEmpty(&obj->PopStack) && StackEmpty(&obj->PushStack))return true;elsereturn false;
}void myQueueFree(MyQueue* obj) {free(obj);obj = NULL;
}/*** 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);
*/
对于用栈实现队列,我们采用的思路与用队列实现栈并无太大差别,阅读代码即可,在这里不再赘述。