系统性学习数据结构-第三讲-栈和队列

发布于:2025-09-07 ⋅ 阅读:(14) ⋅ 点赞:(0)

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);
*/

对于用栈实现队列,我们采用的思路与用队列实现栈并无太大差别,阅读代码即可,在这里不再赘述。


网站公告

今日签到

点亮在社区的每一天
去签到