【数据结构】LeetCode栈和队列经典题型

目录

1.有效的括号

 2.用队列实现栈​编辑

3.用栈实现队列​编辑

4.设计循环队列

1.有效的括号

【数据结构】LeetCode栈和队列经典题型_第1张图片

思路:因为左括号要和右括号顺序和类型都匹配,那么当从第一个右括号开始就要和左括号里最后一个匹配,如果匹配就下一个继续往后走。这样我们非常容易想到栈,左括号入栈,当遇到右括号就出栈,然后判断是否匹配,若不匹配就返回false,匹配就看下一个,直到栈为空。【数据结构】LeetCode栈和队列经典题型_第2张图片

代码:因为这里是C语言实现所有需要创建栈来使用,就用前面讲过的栈来用

typedef char STDataType;

//栈必须符合后进先出的原则
//创建动态栈空间
typedef struct Stack
{
	STDataType* val;
	int top;//栈顶
	int capacity;//栈容量
}ST;

//初始化
void StackInit(ST* ps);
//销毁栈空间
void StackDestroy(ST* ps);
//插入数据
void StackPush(ST* ps,STDataType x);
//删除数据
void StackPop(ST* ps);
//拿出栈顶的数据
STDataType StackTop(ST* ps);
//判断栈是否为空
bool StackEmpty(ST* ps);
//计算栈的长度
int StackSize(ST* ps);

//初始化
void StackInit(ST* ps)
{
	assert(ps);
	ps->val = NULL;
	ps->top = 0;
	ps->capacity = 0;
}

//销毁栈空间
void StackDestroy(ST* ps)
{
	assert(ps);
	free(ps->val);
	ps->val = NULL;
	ps->top = ps->capacity = 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 = (STDataType*)realloc(ps->val,sizeof(STDataType) * newCapacity);
		if (tmp == NULL)
		{
			perror("realloc");
			return;
		}
		ps->val = tmp;
		ps->capacity = newCapacity;
	}
	//栈顶的数据元素给值
	ps->val[ps->top] = x;
	++ps->top;
}

//删除数据
void StackPop(ST* ps)
{
	assert(ps);
	//判断栈是否为空
	assert(!StackEmpty(ps));

	--ps->top;
}

//找到栈顶的数据
STDataType StackTop(ST* ps)
{
	assert(ps);
	assert(!StackEmpty(ps));

	return ps->val[ps->top-1];
}

//判断栈是否为空
bool StackEmpty(ST* ps)
{
    assert(ps);

    return ps->top == 0;
}

//计算栈的长度
int StackSize(ST* ps)
{
	assert(ps);

	return ps->top;
}

bool isValid(char * s){
    ST st;
    StackInit(&st);
    while(*s)
    {
        if(*s == '(' || *s == '{' || *s == '[')
        {
            StackPush(&st,*s);
            ++s;
        }
        else
        {
            //在判断右括号之前如果栈里没有东西也返回false
            if(StackEmpty(&st))
            {

                StackDestroy(&st);
                return false;
            }
            //比较
            STDataType top = StackTop(&st);
            StackPop(&st);
            if((top == '(' && *s == ')')
            || (top == '{' && *s == '}')
            || (top == '[' && *s == ']'))
            {
                ++s;
            }
            else
            {
                //销毁空间返回false
                StackDestroy(&st);
                return false;
            }
        }
    }
    //如果出来了判断栈是否为空,如果为空的话就代表全部匹配
    bool ret = StackEmpty(&st);
    StackDestroy(&st);
    return ret;
}

 2.用队列实现栈【数据结构】LeetCode栈和队列经典题型_第3张图片

 思路:用两个队列来实现后进先出的队列。插入数据时,需要往不为空的队列里插入。删除时需要倒数据,返回栈顶也就是返回队列的尾,判断栈是否为空只需判断两个队列是否为空。下图是移除并返回栈顶元素的思路。【数据结构】LeetCode栈和队列经典题型_第4张图片

 代码:

typedef int QDatatype;

//用链表实现队列
typedef struct QueueNode
{
	QDatatype val;
	struct QueueNode* next;
}QNode;

//存放链表的头结点和尾结点
typedef struct Queue
{
	QNode* head;
	QNode* 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);
bool QueueEmpty(Queue* pq);
int QueueSize(Queue* pq);
void QueueInit(Queue* pq)
{
	assert(pq);
	pq->head = pq->tail = NULL;
}

void QueueDestroy(Queue* pq)
{
	assert(pq);
	QNode* cur = pq->head;
	while (cur)
	{
		QNode* next = cur->next;
		free(cur);
		cur = next;
	}

	pq->head = pq->tail = NULL;
}

void QueuePush(Queue* pq, QDatatype x)
{
	assert(pq);
	//创建一个新结点
	QNode* newnode = (QNode*)malloc(sizeof(QNode));
	if (newnode == NULL)
	{
		printf("malloc fail\n");
		return;
	}
	newnode->val = x;
	newnode->next = NULL;
	//队列为空的创建
	if (pq->tail == NULL)
	{
		pq->head = pq->tail = newnode;
	}
	//队列不为空的创建
	else
	{
		pq->tail->next = newnode;
		pq->tail = newnode;
	}
}

void QueuePop(Queue* pq)
{
	assert(pq);

	//判断当前队列是否为空
	assert(!QueueEmpty(pq));

	//若队列中只有一个结点
	if (pq->head->next == NULL)
	{
		free(pq->head);
		pq->head = pq->tail = NULL;
	}
	//不止一个结点
	else
	{
		QNode* next = pq->head->next;
		free(pq->head);
		pq->head = next;
	}

}

QDatatype QueueFront(Queue* pq)
{
	assert(pq);
	assert(!QueueEmpty(pq));

	return pq->head->val;
}

QDatatype QueueBack(Queue* pq)
{
	assert(pq);
	assert(!QueueEmpty(pq));

	return pq->tail->val;
}

bool QueueEmpty(Queue* pq)
{
	assert(pq);

	//只用判断头是否为空就行
	return pq->head == NULL;
}

int QueueSize(Queue* pq)
{
	int size = 0;
	QNode* cur = pq->head;
	while (cur)
	{
		++size;
		cur = cur->next;
	}
	return size;
}

typedef struct {
    Queue q1;
    Queue q2;
} MyStack;

MyStack* myStackCreate() {
    MyStack* obj = (MyStack*)malloc(sizeof(MyStack));
    //初始化
    QueueInit(&obj->q1);
    QueueInit(&obj->q2);
    //返回obj的地址
    return obj;
}

void myStackPush(MyStack* obj, int x) {
    //往不为空的队列里插入数据,空的队列用来倒数据
    if(!QueueEmpty(&obj->q1))
    {
        QueuePush(&obj->q1,x);
    }
    else
    {
        QueuePush(&obj->q2,x);
    }
}

int myStackPop(MyStack* obj) {
    //讲有数据的倒到空的队列里,留一个然后删除留的那一个
    //首先假设q1为空,q2不为空
    //找到空的和不空的队列
    Queue* empty = &obj->q1;
    Queue* notempty = &obj->q2;
    if(!QueueEmpty(&obj->q1))
    {
        empty = &obj->q2;
        notempty = &obj->q1;
    }

    //倒数据
    int size = QueueSize(notempty);
    while(size>1)
    {
        //插入到空的队列中去
        QueuePush(empty,QueueFront(notempty));
        QueuePop(notempty);
        size--;
    }
    //先传给top再删除最后剩下的这个数据
    int top = QueueFront(notempty);
    QueuePop(notempty);
    return top;
}

int myStackTop(MyStack* obj) {
    //只需要返回,不用删除,可以不用倒,直接找非空的最后一个数
    Queue* empty = &obj->q1;
    Queue* notempty = &obj->q2;
    if(!QueueEmpty(&obj->q1))
    {
        empty = &obj->q2;
        notempty = &obj->q1;
    }
    int top = QueueBack(notempty);
    return top;
}

bool myStackEmpty(MyStack* obj) {

    return QueueEmpty(&obj->q1) && QueueEmpty(&obj->q2);
}

void myStackFree(MyStack* obj) {
    //先释放两个队列
    QueueDestroy(&obj->q1);
    QueueDestroy(&obj->q2);
    //再释放obj
    free(obj);
}

3.用栈实现队列

【数据结构】LeetCode栈和队列经典题型_第5张图片

 思路:上面一题的数据需要在队列之间来回倒,这里不需要,因为队列是后进先出的,当我们将栈1的数据倒到栈2里时栈2里的数据就是逆序,若要删除只需从栈顶依次删除下来,符合队列的先进先出规则,插入数据时需要注意的是,我们一个栈只入栈,一个栈只出,只往栈1里入栈,栈2只出,当栈2有数据时就不能倒,为空时才能倒。【数据结构】LeetCode栈和队列经典题型_第6张图片

 代码:

typedef char STDataType;

//栈必须符合后进先出的原则
//创建动态栈空间
typedef struct Stack
{
	STDataType* val;
	int top;//栈顶
	int capacity;//栈容量
}ST;

//初始化
void StackInit(ST* ps);
//销毁栈空间
void StackDestroy(ST* ps);
//插入数据
void StackPush(ST* ps,STDataType x);
//删除数据
void StackPop(ST* ps);
//拿出栈顶的数据
STDataType StackTop(ST* ps);
//判断栈是否为空
bool StackEmpty(ST* ps);
//计算栈的长度
int StackSize(ST* ps);

//初始化
void StackInit(ST* ps)
{
	assert(ps);
	ps->val = NULL;
	ps->top = 0;
	ps->capacity = 0;
}

//销毁栈空间
void StackDestroy(ST* ps)
{
	assert(ps);
	free(ps->val);
	ps->val = NULL;
	ps->top = ps->capacity = 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 = (STDataType*)realloc(ps->val,sizeof(STDataType) * newCapacity);
		if (tmp == NULL)
		{
			printf("realloc fail\n");
			return;
		}
		ps->val = tmp;
		ps->capacity = newCapacity;
	}
	//栈顶的数据元素给值
	ps->val[ps->top] = x;
	ps->top++;
}

//删除数据
void StackPop(ST* ps)
{
	assert(&ps);
	//判断栈是否为空
	assert(!StackEmpty(ps));

	ps->top--;
}

//拿出栈顶的数据
STDataType StackTop(ST* ps)
{
	assert(ps);
	assert(!StackEmpty(ps));

	return ps->val[ps->top-1];
}

//判断栈是否为空
bool StackEmpty(ST* ps)
{
	assert(ps);

	return ps->top == 0;
}

//计算栈的长度
int StackSize(ST* ps)
{
	assert(ps);

	return ps->top;
}


typedef struct {
    ST s1;
    ST s2;
} MyQueue;


MyQueue* myQueueCreate() {
    MyQueue* obj = (MyQueue*)malloc(sizeof(MyQueue));

    StackInit(&obj->s1);
    StackInit(&obj->s2);
    return obj;
}

void myQueuePush(MyQueue* obj, int x) {
    //默认s1栈专门进队列,s2专门出队列
    StackPush(&obj->s1,x);
}

int myQueuePop(MyQueue* obj) {
    //s2为空时,将s1的数据倒入s2中
    if(StackEmpty(&obj->s2))
    {
        //栈底的数据留着直接返回并删除就行
        while(StackSize(&obj->s1)>1)
        {
            StackPush(&obj->s2,StackTop(&obj->s1));
            StackPop(&obj->s1);
        }
        int top = StackTop(&obj->s1);
        StackPop(&obj->s1);
        return top;
    }
    //s2不为空时从s2栈顶开始取数据
    else
    {
        int top = StackTop(&obj->s2);
        StackPop(&obj->s2);
        return top;
    }
}

int myQueuePeek(MyQueue* obj) {
    //若s2为空时,将s1的数据全部倒入s2中
    if(StackEmpty(&obj->s2))
    {
        //将全部s1倒入s2
        while(StackSize(&obj->s1))
        {
            StackPush(&obj->s2,StackTop(&obj->s1));
            StackPop(&obj->s1);
        }
    }
    //返回s2的栈顶
    int top = StackTop(&obj->s2);
    return top;
}

bool myQueueEmpty(MyQueue* obj) {

    return StackEmpty(&obj->s1) && StackEmpty(&obj->s2);
}

void myQueueFree(MyQueue* obj) {

    StackDestroy(&obj->s1);
    StackDestroy(&obj->s2);
    free(obj);
}

4.设计循环队列

【数据结构】LeetCode栈和队列经典题型_第7张图片

思路: 当队列为循环状态时,我们头空的情况下头等于尾,那么当我们满的情况下呢?头还等于尾吗?于是我们设计了下面这个【数据结构】LeetCode栈和队列经典题型_第8张图片

这里我们用数组来实现较为简单 把上面那个圆展开就成了一个数组。【数据结构】LeetCode栈和队列经典题型_第9张图片

当插入数据时插入在tail位置,然后tail++,不过每次插入时需要判断是否已满,删除数据时将head向后移即可删除。不过需要注意的是当在边界时我们需要处理tail和head。

代码:

//用数组实现
typedef struct {
    int* a;
    int k;
    int head;
    int tail;
} MyCircularQueue;

//函数声明
bool myCircularQueueIsEmpty(MyCircularQueue* obj);
bool myCircularQueueIsFull(MyCircularQueue* obj);

MyCircularQueue* myCircularQueueCreate(int k) {
    MyCircularQueue* obj = (MyCircularQueue*)malloc(sizeof(MyCircularQueue));
    obj->a = (int*)malloc(sizeof(int)*(k+1));
    obj->k = k;
    obj->head = obj->tail = 0;

    return obj;
}

bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {
    //首先判断是否满了
    if(myCircularQueueIsFull(obj))
        return false;
    
    obj->a[obj->tail] = value;
    obj->tail++;
    
    //特殊处理,当tail>k时
    if(obj->tail == obj->k+1)
        obj->tail = 0;
    return true;
}

bool myCircularQueueDeQueue(MyCircularQueue* obj) {
    //判断是否为空
    if(myCircularQueueIsEmpty(obj))
        return false;
    
    //将head向后移
    obj->head++;

    //当head在最后一位时需要特殊处理
    if(obj->head == obj->k+1)
        obj->head = 0;
    return true;
}

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

int myCircularQueueRear(MyCircularQueue* obj) {
    if(myCircularQueueIsEmpty(obj))
        return -1;
    
    //当tail为0时返回队尾的数
    if(obj->tail == 0)
        return obj->a[obj->k];
    //返回tail前面的数据
    else
        return obj->a[obj->tail-1];
}

bool myCircularQueueIsEmpty(MyCircularQueue* obj) {
    //当head=tail时为空
    return obj->head == obj->tail;
}

bool myCircularQueueIsFull(MyCircularQueue* obj) {
    //如果tail在head前一个就说明满了
    int head = obj->head;
    int tail = obj->tail + 1
    ;
    //当tail在队尾时处理一下
    if(tail == obj->k+1)
        tail = 0;
    if(head == tail)
        return true;
    else
        return false;
}

void myCircularQueueFree(MyCircularQueue* obj) {
    //先释放数组
    free(obj->a);
    //再释放结构体
    free(obj);
}


你可能感兴趣的:(数据结构,数据结构,leetcode,c++)