系统性学习数据结构-第三讲-栈和队列
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;
}
else
StackPop(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;
else
return 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;
else
return 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);
*/
对于用栈实现队列,我们采用的思路与用队列实现栈并无太大差别,阅读代码即可,在这里不再赘述。