The characteristics of [stack and queue] and the implementation of the basic interface

Directory

1. Stack

1.1 The concept of stack

1.2 Interface implementation of the stack

Two, the queue

2.1 The concept of queue

2.2 Interface Implementation of Queue

2.3 Difference between stack and queue

3. Stack and queue LeetCode exercises

3.1 Lituo_232. Realize queue with stack

3.2 Lituo_225. Using queues to implement stacks

3.3 Leetour_622. Design Circular Queue

3.4 Leetcode_20. Valid parentheses


one, stack

Friends who are learning data structures for the first time should not think that the stack is difficult, it is actually quite simple. Just make it clear: “Last in, first out, first in, last out” and it’s OK!

1.1 The concept of stack

For the data whose logical relationship is “one-to-one”, in addition to using sequential tables and linked lists, it can also be stored in a stack structure.

The stack is a “special” linear storage structure, and its special features are reflected in the following two places:

1. The operation of pushing elements into and out of the stack can only be done from one end, and the other end is closed, as shown in the following figure:

Usually, we refer to the process of pushing elements into the stack as “push”, “push” or “push” for short; the process of popping elements out of the stack is simply called “pop” or “pop”.

2. Regardless of storing or fetching data in the stack, the principle of “first in, last out” must be followed, that is, the element that is pushed into the stack first is popped out first. Take the stack in the above figure as an example, it is easy to see that element 1 is pushed onto the stack first, and then elements 2, 3, and 4 are pushed onto the stack in turn. On this basis, if you want to take out element 1, according to the principle of “first in, last out”, you must first pop elements 4, 3, and 2 out of the stack in turn, and then it is the turn of element 1 to pop out of the stack.

We are used to calling the open end of the stack the top of the stack, and the closed end the bottom of the stack.

From this, we can define the stack storage structure: a stack is a linear storage structure that “elements can only be accessed from one end, and the access process must follow the principle of ‘first in, last out'”.

1.2 Stack interface implementation

//Stack.h file


#pragma once


#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <stdbool.h>

typedef int DataType;
typedef struct Stack
{
DataType* data;
int top;
int capacity;
}st;


//Initialize the stack
void STInit(ST* pst);

//Push into the stack
void STPush(ST* pst, DataType x);

// pop out
void STPop(ST* pst);

// stack destruction
void STDestory(ST* pst);

//get the top element of the stack
DataType STGetTop(ST* pst);

// Check if the stack is empty
bool STEmpty(ST* pst);

//The size of the stack - the number of elements
int STSize(ST* pst);
//Stack.c file


#include "Stack.h"

void STInit(ST* pst)
{
assert(pst);
pst->data = NULL;
pst->top = 0;
pst->capacity = 0;
}

void STPush(ST* pst, DataType x)
{
assert(pst);

//Expansion
if (pst->top == pst->capacity)
{
int newCapacity = pst->capacity == 0 ? 4 : pst->capacity * 2;
DataType* tmp = (DataType*)realloc(pst->data, sizeof(DataType) * newCapacity);
if (tmp == NULL)
{
perror("realloc fail!\
");
return;
}
pst->data = tmp;
pst->capacity = newCapacity;
}

//Push into the stack
pst->data[pst->top] = x;
pst->top++;
}

void STPop(ST* pst)
{
assert(pst);

assert(!STEmpty(pst));

pst->top--;
}

void STDestory(ST* pst)
{
assert(pst);
free(pst->data);
pst->data = NULL;
pst->top = 0;
pst->capacity = 0;
}



DataType STGetTop(ST* pst)
{
assert(pst);
assert(!STEmpty(pst));

return pst->data[pst->top - 1];
}

bool STEmpty(ST* pst)
{
assert(pst);
return pst->top == 0;
}



int STSize(ST* pst)
{
assert(pst);
return pst->top;
}

two, queue

2.1 The concept of queue

The queue is used to store data with a logical relationship of “one-to-one”, and is a “special” linear storage structure. Compared with sequential lists and linked lists, the particularity of queues is reflected in the following two aspects:

1. Elements can only enter from one end of the queue and exit from the other end, as shown in the following figure:

Usually, we call the end where elements enter the queue as “queue tail”, and the process of entering the queue is called “enqueue”; the end that takes elements out of the queue is called is “queue head”, and the process of dequeuing is called “dequeuing”.

2. The entry and exit of each element in the queue must follow the principle of “First in, first out”, that is, the element that enters the queue first must leave the queue first.

Take the queue shown in the above figure as an example. It is not difficult to judge from the storage status of each element in the queue. Element 1 enters the queue first, then element 2 enters the queue, and finally element 3 enters the queue. If you want to dequeue element 3 at this time, according to the “first in, first out” principle, elements 1 and 2 must be dequeued first, and then element 3 can be dequeued at last.

2.2 Queue interface implementation

//Queue.h file


#pragma once


#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#include <stdbool.h>

typedef int DataType;
typedef struct QNode
{
DataType data;
struct QNode* next;
}QNode;

typedef struct Queue
{
QNode* phead;
QNode* ptail;
int size;
} Queue;

//Initialize the queue
void QueueInit(Queue* pq);

//destroy the queue
void QueueDestory(Queue* pq);

//enqueue
void QueuePush(Queue* pq, DataType x);


//dequeue
void QueuePop(Queue* pq);

//queue head element
DataType QueueFront(Queue* pq);

// tail element
DataType QueueBack(Queue* pq);

//The size of the queue
int QueueSize(Queue* pq);

// Check if the queue is empty
bool QueueEmpty(Queue* pq);
//Queue.c file


#include "Queue.h"

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

void QueueDestory(Queue* pq)
{
assert(pq);
QNode* cur = pq->phead;
while (cur)
{
QNode* next = cur->next;
free(cur);
cur = next;
}
pq->phead = NULL;
pq->ptail = NULL;
pq->size = 0;
}

void QueuePush(Queue* pq, DataType x)
{
assert(pq);
QNode* newnode = (QNode*)malloc(sizeof(QNode));
if (newnode == NULL)
{
perror("malloc fail!\
");
return;
}
newnode->data = x;
newnode->next = NULL;


if (pq->phead == NULL)
{
assert(pq->ptail == NULL);
pq->phead = pq->ptail = newnode;
}
else
{
pq->ptail->next = newnode;
pq->ptail = newnode;
}
pq->size + + ;
}

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

// no node
assert(!QueueEmpty(pq));
// only one node
if (pq->phead->next == NULL)
{
free(pq->phead);
pq->phead = NULL;
pq->ptail = NULL;
}

else
{
QNode* next = pq->phead->next;
free(pq->phead);
pq->phead = next;
}
pq->size--;
}

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

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

int QueueSize(Queue* pq)
{
assert(pq);
return pq->size;
}

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

2.3 Difference between stack and queue

Do not confuse the stack with the queue. The stack is open at one end and closed at the other end. Elements are pushed into and out of the stack following the principle of “first in, last out”; the queue is open at both ends, but elements can only enter from one end , exit from the other end, and the entry and exit queues follow the principle of “first in, first out”.

3. Stack and queue LeetCode exercises

3.1 Leek_232. Implementing queues with stacks

typedef struct {
    ST Push ST;
    ST PopST;
} MyQueue;


MyQueue* myQueueCreate() {
    MyQueue* obj = (MyQueue*)malloc(sizeof(MyQueue));
    STInit( & obj->PushST);
    STInit( & obj->PopST);

    return obj;
}

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

int myQueuePop(MyQueue* obj) {
    int ret = myQueuePeek(obj);
    STPop( & obj->PopST);
    return ret;
}

int myQueuePeek(MyQueue* obj) {
    if(STEmpty( &obj->PopST))
    {
        while(!STEmpty( &obj->PushST))
        {
            STPush( &obj->PopST, STGetTop( &obj->PushST));
            STPop( & obj->PushST);
        }
    }
    return STGetTop( & amp; obj->PopST);
}

bool myQueueEmpty(MyQueue* obj) {
    return STEmpty( & amp;obj->PushST) & amp; & amp; STEmpty( & amp;obj->PopST);
}

void myQueueFree(MyQueue* obj) {
    STDestory( & obj->PushST);
    STDestory( & obj->PopST);
    free(obj);
}

3.2 Likou_225. Implement stack with queue

typedef struct {
    Queue q1;
    Queue q2;

} MyStack;


MyStack* myStackCreate() {
    MyStack* obj = (MyStack*)malloc(sizeof(MyStack));
    QueueInit( & obj->q1);
    QueueInit( & obj->q2);

    return obj;
}

void myStackPush(MyStack* obj, int x) {
    if(!QueueEmpty( &obj->q1))
    {
        QueuePush( & amp; obj->q1, x);
    }
    else {
        QueuePush( & amp; obj->q2, x);
    }
}

int myStackPop(MyStack* obj) {
    Queue* EmptyQ = &obj->q1;
    Queue* NoEmptyQ = &obj->q2;
    if(!QueueEmpty( &obj->q1))
    {
        EmptyQ = &obj->q2;
        NoEmptyQ = & obj->q1;
    }

    while(QueueSize(NoEmptyQ) > 1)
    {
        QueuePush(EmptyQ, QueueFront(NoEmptyQ));
        QueuePop(NoEmptyQ);
    }
    int top = QueueFront(NoEmptyQ);
    QueuePop(NoEmptyQ);
    return top;
}

int myStackTop(MyStack* obj) {
    if(!QueueEmpty( &obj->q1))
    {
        return QueueBack( & amp; obj->q1);
    }
    else
    {
        return QueueBack( & amp; obj->q2);
    }
}

bool myStackEmpty(MyStack* obj) {
    return QueueEmpty( & amp; obj->q1) & amp; & amp; QueueEmpty( & amp; obj->q2);
}

void myStackFree(MyStack* obj) {
    QueueDestory( & obj->q1);
    QueueDestory( & obj->q2);

    free(obj);
}

3.3 Rikou_622. Design Circular Queue

typedef struct {
    int front;
    int rear;
    int k;
    int* a;
} MyCircularQueue;


MyCircularQueue* myCircularQueueCreate(int k) {
    MyCircularQueue* obj = (MyCircularQueue*)malloc(sizeof(MyCircularQueue));
    obj->a = (int*)malloc(sizeof(int) * (k + 1));
    if(obj == NULL)
    {
        perror("malloc fail!\
");
        return NULL;
    }
    obj->front = obj->rear = 0;
    obj->k = k;

    return obj;
}
bool myCircularQueueIsFull(MyCircularQueue* obj) {
    return (obj->rear + 1) % (obj->k + 1) == obj->front;
}

bool myCircularQueueIsEmpty(MyCircularQueue* obj) {
    return obj->front == obj->rear;
}


bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {
    if(myCircularQueueIsFull(obj))
        return false;
    obj->a[obj->rear] = value;
    obj->rear ++ ;
    obj->rear %= (obj->k + 1);
    return true;
}

bool myCircularQueueDeQueue(MyCircularQueue* obj) {
    if(myCircularQueueIsEmpty(obj))
    {
        return false;
    }
    obj->front + + ;
    obj->front %= (obj->k + 1);
    return true;

}

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

int myCircularQueueRear(MyCircularQueue* obj) {
    if(!myCircularQueueIsEmpty(obj))
        return obj->a[(obj->rear + obj->k) % (obj->k + 1)];
    else
        return -1;
}


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

3.4 Ritual_20. Valid brackets

bool isValid(char * s)
{
    ST st;
    STInit( &st);
    while(*s)
    {
        if(*s == '(' || *s == '[' || *s == '{')
        {
            STPush( &st, *s);
        }
        else
        {
            if(STEmpty( &st))
            {
                STDestory( &st);
                return false;
            }
            char top = STGetTop( &st);
            STPop( & st);
            if(*s == ')' & amp; & amp; top != '('
            || *s == ']' & amp; & amp; top != '['
            || *s == '}' & amp; & amp; top != '{')
            {
                STDestory( &st);
                return false;
            }
        }
        s++;
    }
    bool ret = STEmpty( &st);
    STDestory( &st);
    return ret;
}