【LeetCode之栈和队列】:关于栈和队列经典的OJ题(用C语言实现,附图详解)

1、括号匹配问题

LeetCode链接: 【20. 有效的括号】
在这里插入图片描述
这道题就是经典的利用栈解决问题的例子;思路如下:
遍历一遍字符串,如果遇倒左括号就入栈,如果遇倒右括号取栈顶的元素进行匹配并出栈顶的元素,如果相匹配就继续,不匹配就返回false。
但是要注意这样只能检验出左右括号个数相等的情况下才可以,如果左右括号个数不相等呢?

  • 如果左括号多于右括号,并且遍历结束后它们都是匹配的,这种情况并不是完全匹配的,因为栈里还有元素剩余;所以遍历字符串后要加一个判断栈是否为空,只要栈为空了才是全部匹配完,没有剩余。 比如:“[[[[[((()))”
  • 如果右括号多余左括号,有可能导致栈是空的,栈里面没有元素可以取了,这种情况也是不匹配的;所以取栈顶元素的前面要加一个判断栈是否为空,如果栈为空说明右括号多余左括号,是不匹配的。比如:”((([[[ ]]]]]]]]]"

代码实现如下:

//C语言中没有栈,我们先实现一个栈
//构建栈如下:
typedef char STDataType;
struct Stack
{
    
    
	STDataType* a;
	int top;
	int capacity;
};
typedef struct Stack ST;

//初始化栈
void StackInit(ST* ps);
//销毁栈
void StackDestroy(ST* ps);
//入栈
void StackPush(ST* ps, STDataType x);
//出栈
void StackPop(ST* ps);
//获取栈的元素个数
int StackSize(ST* ps);
//获取栈顶的元素
STDataType StackTop(ST* ps);
//判断栈是否为空
bool StackEmpty(ST* ps);

void StackInit(ST* ps)
{
    
    
	ps->a = NULL;
	ps->capacity = 0;
	ps->top = 0;
}

void StackDestroy(ST* ps)
{
    
    
	free(ps->a);
	ps->a = NULL;
	ps->capacity = ps->top = 0;
}

void StackPush(ST* ps,STDataType x)
{
    
    
	assert(ps);
	if (ps->capacity == ps->top)
	{
    
    
		ps->capacity = ps->capacity > 0 ? ps->capacity * 2 : 2;
		STDataType* tmp = 
		(STDataType*)realloc(ps->a, ps->capacity * sizeof(STDataType));
		if (tmp == NULL)
		{
    
    
			return;
		}
		else
		{
    
    
			ps->a = tmp;
		}
	}

	ps->a[ps->top] = x;
	ps->top++;
}

void StackPop(ST* ps)
{
    
    
	assert(ps);
	assert(ps->top > 0);
	ps->top--;
}

int StackSize(ST* ps)
{
    
    
	assert(ps);
	assert(ps->top > 0);
	return ps->top;
}

STDataType StackTop(ST* ps)
{
    
    
	assert(ps);
	assert(ps->top > 0);
	return ps->a[ps->top - 1];
}

bool StackEmpty(ST* ps)
{
    
    
	assert(ps);
	return ps->top == 0;
}



bool isValid(char * s){
    
    
    ST st;
    StackInit(&st);
    while(*s!='\0')
    {
    
    
        switch(*s)
        {
    
    
            //压栈
            case '(':
            case '[':
            case '{':
            StackPush(&st,*s);
            s++;
            break;
            //取栈顶的数据进行匹配
            case ')':
            case ']':
            case '}':
            //出栈,从栈里面取做括号,栈里面的元素不能为空,
            //防止右括号过多,导致取到栈里面没有元素可以取,这也是不匹配
            if(StackEmpty(&st))
            {
    
    
                StackDestroy(&st);
                return false;
            }
            //取栈顶的数据和字符比较
            char top=StackTop(&st);
            StackPop(&st);
            if(top=='(' && *s==')' || 
               top=='[' && *s==']' ||
               top=='{' && *s=='}'
            )
            {
    
    
                s++;
            }
            else
            {
    
    
                StackDestroy(&st);
                return false;
            }
        }
    }
     
     //最后要判断栈是否为空
     //栈是空的说明才完全匹配,如果还要剩余说明没有是左括号多了,不匹配
    if(StackEmpty(&st))
    {
    
    
       StackDestroy(&st);
        return true;
    }
    else
    {
    
    
       StackDestroy(&st);
        return false;
    }

}

2、用队列实现栈

LeetCode链接: 【225. 用队列实现栈】
在这里插入图片描述
思路: 这是要我们利用队列的性质去实现栈,队列的性质是先入先出,而栈的性质是后入先出;
所以我们利用两个队列来实现栈,一个为空队列,一个不为空队列;入栈:就往不为空的队列里面入,而 出栈就是: 只保留不为空的队列的最后一个元素,前面的元素都插入空队列中;然后再把最后一个元素出掉就是出栈了。

在这里插入图片描述
代码实现如下:

//C语言中没有队列,我们先构建一个队列的各种接口
//队列实现如下:
typedef int DataType;
typedef struct Node
{
    
    
	struct Node* next;
	DataType data;
}Node;

typedef struct Queue
{
    
    
	Node* phead;
	Node* tail;
}Queue;

//初始化队列
void QueueInit(Queue* pq);  
//销毁队列
void QueueDestroy(Queue* pq);
//队列插数据
void QueuePush(Queue* pq,DataType x);
//队列出数据
void QueuePop(Queue* pq);
//取队尾数据
DataType QueueBack(Queue* pq);
//取队头数据
DataType QueueFront(Queue* pq);
//取队列中元素个数
int QueueSize(Queue* pq);
//判断队列是否为空
bool QueueEmpty(Queue* pq);

void QueueInit(Queue* pq)
{
    
    
	assert(pq);
	pq->phead = NULL;
	pq->tail = NULL;
}

void QueuePush(Queue* pq, DataType x)
{
    
    
	Node* tmp = (Node*)malloc(sizeof(Node));
	if (tmp == NULL)
	{
    
    
		perror("malloc ");
		exit(-1);
	}
	tmp->data = x;
	tmp->next = NULL;
	if (pq->tail == NULL)
	{
    
    
		pq->phead = tmp;
		pq->tail = tmp;
	}
	else
	{
    
    
		pq->tail->next = tmp;
		pq->tail = tmp;
	}
}

void QueuePop(Queue* pq)
{
    
    
	assert(pq);
	assert(pq->phead);
	// 一个节点
	if (pq->phead->next == NULL)
	{
    
    
		free(pq->phead);
		pq->phead = NULL;
		pq->tail = NULL;
	}
	//多个节点
	else
	{
    
    
		Node* tmp = pq->phead->next;
		free(pq->phead);
		pq->phead = tmp;
	}
}

int QueueSize(Queue* pq)
{
    
    
	assert(pq);
	int count = 0;
	Node* cur = pq->phead;
	while (cur != NULL)
	{
    
    
		cur = cur->next;
		count++;
	}
	return count;
}

bool QueueEmpty(Queue* pq)
{
    
    
	assert(pq);
	return pq->phead == NULL;
}

DataType QueueFront(Queue* pq)
{
    
    
	assert(pq);
	assert(pq->phead);
	return pq->phead->data;
}

DataType QueueBack(Queue* pq)
{
    
    
	assert(pq);
	assert(pq->phead);
	return pq->tail->data;
}

void QueueDestroy(Queue* pq)
{
    
    
	assert(pq);
	Node* tmp = pq->phead;
	while (tmp != NULL)
	{
    
    
		Node* next = tmp->next;
		free(tmp);
		tmp = next;
	}
	pq->phead = pq->tail = NULL;
}
//上面都是在实现队列的各种接口

//下面是利用两个队列来实现栈
//用两给队列来实现栈,两个队列不断倒元素即可
typedef struct {
    
    
    Queue q1;
    Queue q2;
} MyStack;


MyStack* myStackCreate() {
    
    
    MyStack* tmp=(MyStack*)malloc(sizeof(MyStack));
    if(tmp==NULL)
    {
    
    
        perror("erron ");
        exit(-1);
    }
    QueueInit(&tmp->q1);
    QueueInit(&tmp->q2);
    return tmp;
}

//往不为空的队列插数据,如果两个队列都为空,任意一个都行
void myStackPush(MyStack* obj, int x) {
    
    
    Queue* empty=&obj->q1;
    Queue* nonempty=&obj->q2;
    if(QueueEmpty(&obj->q2))
    {
    
    
        Queue* empty=&obj->q2;
        Queue* nonempty=&obj->q1;
    }
    QueuePush(nonempty,x);
  

}

//先把不为空的队列往空队列里面倒数据,倒到只剩下1个元素
//而这不为空的队列最后一个元素就是栈顶的元素,出掉即可
int myStackPop(MyStack* obj) {
    
    
    Queue* empty=&obj->q1;
    Queue* nonempty=&obj->q2;
    if(QueueEmpty(&obj->q2))
    {
    
    
         empty=&obj->q2;
         nonempty=&obj->q1;
    }
    while(QueueSize(nonempty)>1)
    {
    
    
        QueuePush(empty,QueueFront(nonempty));
        QueuePop(nonempty);
    }
    //最后一个元素就是栈顶的元素
    //出掉即可,保证队列有一个为空队列
    int tmp=QueueBack(nonempty);
    QueuePop(nonempty);
    return tmp;

}

//直接取不为空的队列的尾元素即可
int myStackTop(MyStack* obj) {
    
    
   Queue* empty=&obj->q1;
   Queue* nonempty=&obj->q2;
   if(QueueEmpty(&obj->q2))
    {
    
    
        empty=&obj->q2;
        nonempty=&obj->q1;
    }
    return QueueBack(nonempty);
}

bool myStackEmpty(MyStack* obj) {
    
    
    //两给队列都为空,栈才是空
    return QueueEmpty(&obj->q1)  && QueueEmpty(&obj->q2);
}

void myStackFree(MyStack* obj) {
    
    
    QueueDestroy(&obj->q1);
    QueueDestroy(&obj->q2);
    free(obj);
}

3、用栈实现队列

LeetCode链接: 【232. 用栈实现队列】
在这里插入图片描述
思路分析如下:
在这里插入图片描述
代码实现如下:

//C语言中没有栈,我们先实现一个栈
//构建栈如下:
typedef int STDataType;
struct Stack
{
    
    
	STDataType* a;
	int top;
	int capacity;
};
typedef struct Stack ST;

//初始化栈
void StackInit(ST* ps);
//销毁栈
void StackDestroy(ST* ps);
//入栈
void StackPush(ST* ps, STDataType x);
//出栈
void StackPop(ST* ps);
//获取栈的元素个数
int StackSize(ST* ps);
//获取栈顶的元素
STDataType StackTop(ST* ps);
//判断栈是否为空
bool StackEmpty(ST* ps);

void StackInit(ST* ps)
{
    
    
	ps->a = NULL;
	ps->capacity = 0;
	ps->top = 0;
}

void StackDestroy(ST* ps)
{
    
    
	free(ps->a);
	ps->a = NULL;
	ps->capacity = ps->top = 0;
}

void StackPush(ST* ps,STDataType x)
{
    
    
	assert(ps);
	if (ps->capacity == ps->top)
	{
    
    
		ps->capacity = ps->capacity > 0 ? ps->capacity * 2 : 2;
		STDataType* tmp = 
		(STDataType*)realloc(ps->a, ps->capacity * sizeof(STDataType));
		if (tmp == NULL)
		{
    
    
			return;
		}
		else
		{
    
    
			ps->a = tmp;
		}
	}

	ps->a[ps->top] = x;
	ps->top++;
}

void StackPop(ST* ps)
{
    
    
	assert(ps);
	assert(ps->top > 0);
	ps->top--;
}

int StackSize(ST* ps)
{
    
    
	assert(ps);
	assert(ps->top > 0);
	return ps->top;
}

STDataType StackTop(ST* ps)
{
    
    
	assert(ps);
	assert(ps->top > 0);
	return ps->a[ps->top - 1];
}

bool StackEmpty(ST* ps)
{
    
    
	assert(ps);
	return ps->top == 0;
}



//两个栈,把不为空的栈往为空的栈里面倒过来就是队列了
//设计两个栈,一个用来入数据,一个用来出数据
//入数据就入pushst栈,出数据就出popst栈
typedef struct {
    
    
    ST Pushst; //入数据栈
    ST Popst;  //出数据栈
} MyQueue;

MyQueue* myQueueCreate() {
    
    
    MyQueue* tmp=(MyQueue*)malloc(sizeof(MyQueue));
    if(tmp==NULL)
    {
    
    
        perror("erron ");
        exit(-1);
    }
    StackInit(&tmp->Pushst);
    StackInit(&tmp->Popst);
    return tmp;
}

void myQueuePush(MyQueue* obj, int x) {
    
    
    StackPush(&obj->Pushst,x);
}

//要检验出数据的栈是否为空,没有数据了就从入数据的栈里面取
int myQueuePop(MyQueue* obj) {
    
    
    if(StackEmpty(&obj->Popst))
    {
    
    
        while(!StackEmpty(&obj->Pushst))
        {
    
    
            StackPush(&obj->Popst,StackTop(&obj->Pushst));
            StackPop(&obj->Pushst);
        }
    }
    int tmp=StackTop(&obj->Popst);
    StackPop(&obj->Popst);
    return tmp;
}

//取队列的头元素,检验出数据的栈是否为空,没有数据了就从入数据的栈里面取
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);
}

bool myQueueEmpty(MyQueue* obj) {
    
    
    return StackEmpty(&obj->Pushst) && StackEmpty(&obj->Popst);
}

void myQueueFree(MyQueue* obj) {
    
    
    StackDestroy(&obj->Pushst);
    StackDestroy(&obj->Pushst);
    free(obj);
}

4、设计循环队列

LeetCode链接: 【622. 设计循环队列】
在这里插入图片描述
做这道题之前,我们先来了解什么是循环队列?它的解释如下:
循环队列是把顺序队列首尾相连,把存储队列元素的表从逻辑上看成一个环,成为循环队列。
循环队列就是将队列存储空间的最后一个位置绕到第一个位置,形成逻辑上的环状空间,供队列循环使用。在循环队列结构中,当存储空间的最后一个位置已被使用而再要进入队运算时,只需要存储空间的第一个位置空闲,便可将元素加入到第一个位置,即将存储空间的第一个位置作为队尾。
循环队列可以更简单防止伪溢出的发生,但队列大小是固定的。
在这里插入图片描述
所以循环队列的结构搞清楚了,写起来就比较简单了,循环队列有两种实现方式,可以使用数组实现,也可以使用循环链表实现。下面分别来介绍。
链表实现如下:
在这里插入图片描述

//先创建一个结点类型
typedef struct Node
{
    
    
    struct Node* next;
    int data;
}Node;


//创建一个循环队列类型结构体
//里面的头指针指向链表的头,尾指针指向链表的尾
typedef struct {
    
    
    Node* front;
    Node* tail;
} MyCircularQueue;


//创建循环队列
MyCircularQueue* myCircularQueueCreate(int k) {
    
    
    MyCircularQueue* tmp=(MyCircularQueue*)malloc(sizeof(MyCircularQueue));

    //构造一个环链表
    //要多构造一个结点,不然判断不了是否满了
    Node* head=(Node*)malloc(sizeof(Node));
    Node* a=head;
    Node* tail=head;
    while(k--)
    {
    
    
        a=tail;
        tail=(Node*)malloc(sizeof(Node));
        a->next=tail;
    }
    tail->next=head;

    tmp->front=head;
    tmp->tail=head;
    return tmp;

}

//向循环队列插入一个元素
bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {
    
    
    if(obj->tail->next==obj->front)
    {
    
    
        return false;
    }
    obj->tail->data=value;
    obj->tail=obj->tail->next;
    return true;

}

//从循环队列中删除一个元素
bool myCircularQueueDeQueue(MyCircularQueue* obj) {
    
    
    if(obj->front==obj->tail)
    {
    
    
        return false;
    }
    obj->front=obj->front->next;
    return true;

}

//从队首获取元素
int myCircularQueueFront(MyCircularQueue* obj) {
    
    
   if(obj->front==obj->tail)
    {
    
    
        return -1;
    }
    return obj->front->data;

}

//获取队尾元素
int myCircularQueueRear(MyCircularQueue* obj) {
    
    
    if(obj->front==obj->tail)
    {
    
    
        return -1;
    }
    //找队尾的前一个结点就是队尾数据
    Node* cur=obj->front;
    while(cur->next!=obj->tail)
    {
    
    
        cur=cur->next;
    }
    return cur->data;

}

//检查循环队列是否为空
bool myCircularQueueIsEmpty(MyCircularQueue* obj) {
    
    
    return obj->front==obj->tail;

}

//检查循环队列是否已满。
bool myCircularQueueIsFull(MyCircularQueue* obj) {
    
    
    return obj->tail->next==obj->front;

}

//释放内存
void myCircularQueueFree(MyCircularQueue* obj) {
    
    
    Node* cur=obj->front;
    while(cur!=obj->tail)
    {
    
    
        Node* next=cur->next;
        free(cur);
        cur=next;
    }
    free(cur);
    free(obj);
}

数组方式实现如下:
在这里插入图片描述
代码实现如下:

//在循环队列结构中设计一个指针指向一段连续的空间
typedef struct {
    
    
    int* a;
    int maxSize;
    int head;
    int tail;
} MyCircularQueue;

bool myCircularQueueIsFull(MyCircularQueue* obj);

//创建循环队列
MyCircularQueue* myCircularQueueCreate(int k) {
    
    
    MyCircularQueue* tmp=
    (MyCircularQueue*)malloc(sizeof(MyCircularQueue)); 
    //要多开辟一个空间
    int* arr=(int*)malloc(sizeof(int)*(k+1));
    tmp->a=arr;
    tmp->maxSize=k+1;
    tmp->head=0;
    tmp->tail=0;
    return tmp;
}

//向循环队列插入一个元素
bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {
    
    
    if(myCircularQueueIsFull(obj))
    {
    
    
        return false;
    }
    obj->a[obj->tail]=value;
    obj->tail++;
    //把下标tail控制在数组中,不能越界
    if(obj->tail==obj->maxSize)
    {
    
    
        obj->tail=0;
    }
    return true;
}

//从循环队列中删除一个元素
bool myCircularQueueDeQueue(MyCircularQueue* obj) {
    
    
    if(obj->head==obj->tail)
    {
    
    
        return false;
    }
    obj->head++;
    if(obj->head==obj->maxSize)
    {
    
    
        obj->head=0;
    }
    return true; 
}

//从队首获取元素
int myCircularQueueFront(MyCircularQueue* obj) {
    
    
    if(obj->head==obj->tail)
    {
    
    
        return -1;
    }
    return obj->a[obj->head];

}

//获取队尾元素
int myCircularQueueRear(MyCircularQueue* obj) {
    
    
    if(obj->head==obj->tail)
    {
    
    
        return -1;
    }
    int i=0;
    if(obj->tail==0)
    {
    
    
        i=obj->maxSize-1;
    }
    else
    {
    
    
        i=obj->tail-1;
    }
    return obj->a[i];
}

//检查循环队列是否为空
bool myCircularQueueIsEmpty(MyCircularQueue* obj) {
    
    
    return obj->head==obj->tail;
}

//检查循环队列是否已满
bool myCircularQueueIsFull(MyCircularQueue* obj) {
    
    
   //方法1判断是否满了
   // return obj->head==(obj->tail+1)%obj->maxSize;

    
    //方法2判断是否满了
    int head=0;
    if(obj->head==0)
    {
    
    
        head=obj->maxSize-1;
    }
    else
    {
    
    
        head=obj->head-1;
    }
    return head==obj->tail;

}

void myCircularQueueFree(MyCircularQueue* obj) {
    
    
    free(obj->a);
    free(obj);
}

猜你喜欢

转载自blog.csdn.net/m0_56311933/article/details/121105215