Data Structures: Stacks and Queues

Table of contents

1. Data structure - stack

1. Basic introduction to the stack

2. Implementation of the stack

2. Data structure - queue

1. Basic introduction to queues

2. Implementation of the queue 

3. The use of stacks (Leetcode20. Effective parentheses +225) 

1. Problem description 

2. Problem analysis

Solution code:

4. Use two queues to implement a stack (225. Use a queue to implement a stack - Leetcode)

Solution code:

5. Realize queue with two stacks (232. Realize queue with stack - Leetcode)

​Problem solution code:


1. Data structure - stack

1. Basic introduction to the stack

A stack is a data structure in which data is first in , last out, and last in first out :

  • The stack is generally implemented with an array (the stack implemented with a single linked list has obvious defects: the time complexity of the data popping operation is O(N), the reason is to find the address of the previous data at the end of the list

2. Implementation of the stack

Overall Diagram:

Stack header file:

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

typedef int STDataType;
typedef struct Stack
{
	STDataType * arry;	   //栈的堆区空间指针
	int top;               //维护栈顶下标,同时记录栈中的元素个数
	int capacity;		   //记录栈的容量
}ST;

void StackInit(ST* Stack);					//栈对象的初始化
STDataType StackTop(ST*Stack);				//返回栈顶元素
void StackPush(ST* Stack,STDataType val);   //元素入栈
void StackPop(ST* Stack);					//元素出栈
int StackSize(ST* Stack);				    //返回栈对的元素个数的接口;
bool StackEmpty(ST* Stack);					//判断栈是否为空的接口
void StackDestroy(ST* Stack);				//销毁栈

Stack initialization interface:

void StackInit(ST* Stack)
{
	assert(Stack);
	Stack->arry = NULL;
	Stack->capacity = 0;
	Stack->top = 0;
}

Return the interface of the top element of the stack:

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

The interface that returns the number of stack data:

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

The interface for judging whether the stack is empty:

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

Data push operation interface:

void StackPush(ST* Stack, STDataType val)
{
	assert(Stack);
	if (Stack->capacity == Stack->top)    //检查容量是否足够
	{
		int newcapacity = (0 == Stack->capacity) ? 4 : 2 * Stack->capacity;
        //如果容量为零,则将容量设置为4,其他情况下按照两倍扩容方式增容
		STDataType* tem = (STDataType*)realloc(Stack->arry, newcapacity*sizeof(STDataType));
		if (NULL == tem)//判断realloc是否成功
		{
			perror("realloc failed:");
			exit(-1);
		}
		Stack->capacity = newcapacity;
		Stack->arry = tem;
	}
    //元素入栈
	Stack->arry[Stack->top] = val;
	Stack->top++;
}

Data stack operation interface:

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

The interface to destroy the stack:

void StackDestroy(ST* Stack)
{
	assert(Stack);
	if (Stack->arry)
	{
		free(Stack->arry);
	}
	Stack->arry = NULL;
	Stack->capacity = 0;
	Stack->top = 0;
}

2. Data structure - queue

1. Basic introduction to queues

A queue is a data structure in which data is first in first out and last in last out .

  • Data can only enter from the end of the queue and exit from the head of the queue 
  • The queue is generally implemented with a single-linked list , because the queue involves the head deletion operation of the data , and the time complexity of the data head deletion operation of the single-linked list is O(1) .

2. Implementation of the queue 

Overall diagram of the queue:

Queue header file: 

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


typedef int QDataType;

typedef struct QueueNode         //单链表结构体
{
	struct QueueNode* next;
	QDataType data;
}QNode;

typedef struct Queue             //维护队列的结构体
{
	QNode* head;
	QNode* tail;
}Queue;


void QueueInit(Queue* queue);                  //队列的初始化接口
void QueueDestroy(Queue* queue);               //销毁队列的接口
void QueuePush(Queue* queue,QDataType val);    //数据入队接口
void QueuePop(Queue* queue);                   //数据出队接口
QDataType QueueFront(Queue* queue);            //返回队头数据接口
QDataType QueueBawck(Queue* queue);            //返回队尾数据接口
int QueueSize(Queue* queue);                   //返回队列数据个数的接口
bool QueueEmpty(Queue* queue);                 //判断队列是否为空的接口

The initialization interface of the queue:

void QueueInit(Queue* queue)       //队列初始化   
{
	assert(queue);
	queue->head = NULL;
	queue->tail = NULL;
}

The interface that returns the queue head data:

QDataType QueueFront(Queue* queue) //返回队列头部数据
{
	assert(queue);
	assert(queue->head);
	return queue->head->data;
}

The interface that returns the data at the end of the queue:

QDataType QueueBawck(Queue* queue) //返回队列尾部数据
{
	assert(queue);
	assert(queue->tail);
	return queue->tail->data;
}

The interface that returns the number of queue data:

int QueueSize(Queue* queue)        //返回队列节点个数
{
	assert(queue);
	int count = 0;
	QNode* tem = queue->head;
	while(tem)
	{
		count++;
		tem = tem->next;
	}
	return count;
}

The interface for judging whether the queue is empty:

bool QueueEmpty(Queue* queue)      //判断队列是否为空
{
	assert(queue);
	return (NULL == queue->head);
}

Data enqueue interface:

  

void QueuePush(Queue* queue,QDataType val)         //链表尾插数据(数据从队列尾入队)
{
	assert(queue);
	QNode* NewNode = (QNode*)malloc(sizeof(QNode));//申请堆区链表节点
	if (NULL == NewNode)
	{
		perror("malloc failed:");
		exit(-1);
	}
	NewNode->data = val;
	NewNode->next = NULL;

	if (NULL == queue->tail)                       //链表为空时的数据插入操作
	{
		assert(NULL == queue->head);
		queue->tail = NewNode;
		queue->head = NewNode;
	}
	else                                           //链表非空时的数据插入操作
	{
		queue->tail->next = NewNode;
		queue->tail = NewNode;
	}
}

Data dequeue interface:

void QueuePop(Queue* queue)		    //删去链表头节点(数据从队列头部出队)
{
	assert(queue);
	assert(queue->head && queue->tail);
	QNode* Next = queue->head->next;
	free(queue->head);
	queue->head = Next;
	if (NULL == queue->head)        //若删去的是链表的最后一个元素,则要将尾指针也置空
	{
		queue->tail = NULL;
	}
}

The interface for destroying the queue:

void QueueDestroy(Queue* queue)    //销毁链表(队列)
{
	assert(queue);
	QNode* tem = queue->head;
	while (tem)
	{
		QNode* Next = tem->next;
		free(tem);
		tem = Next;
	}
	queue->head = NULL;
	queue->head = NULL;
}

3. The use of stacks (Leetcode20. Effective parentheses +225) 

20. Valid Parentheses - Leetcode

1. Problem description 

 

Given a   string that  only includes  , determine whether the string is valid.'(' ,   ')' ,    '{'  ,      '}'  ,   '['  ,   ']'s

A valid string must satisfy:

  1. An opening parenthesis must be closed with a closing parenthesis of the same type.
  2. Opening parentheses must be closed in the correct order.
  3. Each closing parenthesis has a corresponding opening parenthesis of the same type.

Example:

Input: s = "()[]{}" 
Output: true

C solution interface:

bool isValid(char * s)
{

}

2. Problem analysis

Bracket matching ideas when traversing strings from left :

  • Since we cannot determine which right bracket the current left bracket will match when traversing the string , the idea of ​​matching should be to find the first right bracket first , and then look forward to the corresponding left bracket . If the match fails, then Return false, if the match is successful, continue to find the next closing bracket and repeat the above process until the end
  •  Violent matching algorithm animation analysis:
  • According to the above analysis, it can be seen that the time complexity of the violent matching method is O(N^2) 

However, if this problem is solved using an auxiliary stack , the purpose of exchanging space for time efficiency can be achieved.

The basic idea is as follows:

  • Traversing the string with a pointer , whenever we encounter an opening parenthesis , we push it onto the stack
  • Whenever a right parenthesis is encountered , we match it with the parenthesis at the top of the stack . If the match fails, it means that the string does not meet the conditions , and the interface returns false. If the match is successful, the top element of the stack is popped up , and the string is traversed to repeat the above steps. The interface returns true until '\0' is found
  • Algorithm animation diagram: 

We can use the implemented stack (in the first section of this issue) to solve problems ( by the way, we can also verify that our stack implementation has no bugs )

Solution code:

Stack for solution:

typedef char STDataType;
typedef struct Stack
{
	STDataType * arry;	   //栈的堆区空间指针
	int top;               //维护栈顶下标,同时记录栈中的元素个数
	int capacity;		   //记录栈的容量
}ST;

void StackInit(ST* Stack)
{
	assert(Stack);
	Stack->arry = NULL;
	Stack->capacity = 0;
	Stack->top = 0;
}

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

void StackPush(ST* Stack, STDataType val)
{
	assert(Stack);
	if (Stack->capacity == Stack->top)    //检查容量是否足够
	{
		int newcapacity = (0 == Stack->capacity) ? 4 : 2 * Stack->capacity;
		STDataType* tem = (STDataType*)realloc(Stack->arry, newcapacity*sizeof(STDataType));
		if (NULL == tem)
		{
			perror("realloc failed:");
			exit(-1);
		}
		Stack->capacity = newcapacity;
		Stack->arry = tem;
	}
	Stack->arry[Stack->top] = val;
	Stack->top++;
}

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


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

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



void StackDestroy(ST* Stack)
{
	assert(Stack);
	if (Stack->arry)
	{
		free(Stack->arry);
	}
	Stack->arry = NULL;
	Stack->capacity = 0;
	Stack->top = 0;
}

Two auxiliary judgment interface codes:

    //判断指针指向的括号是否为右括号的接口
    bool CheckRightQutoe(const char quote)  //左括号返回true,有括号返回false
    {
        if('{' == quote || '(' == quote || '[' == quote)
        {
            return true;
        }
        else
        {
            return false;
        }
    }
    //判断两个括号是否匹配的接口
    bool JudgeMatch(const char right,const char left)
    {
        if(('}' == right && '{' == left) || 
           (']' == right && '[' == left) || 
           (')' == right && '(' == left) 
          )
        {
            return true;
        }
        return false;
    }

Solution interface code:

bool isValid(char * s)
{
    ST ans;
    StackInit(&ans);         //创建一个栈
    int lenth = strlen(s);   //字符串有效字符个数为奇数直接返回false
    if(lenth % 2 == 1)       
    {
        return false;
    }
    char * tem = s;          //tem用于遍历字符串
    while('\0' != *tem)
    {
        if(CheckRightQutoe(*tem))  //遇到右括号则入栈
        {
            StackPush(&ans,*tem);
        }
        else                       //遇到左括号则与栈顶右括号匹配
        {
            if(StackEmpty(&ans) || !JudgeMatch(*tem,StackTop(&ans)))
            {
                return false;      //匹配前注意判断栈是否为空
            }
            StackPop(&ans);        //匹配完后弹出栈顶括号       
        }
        tem ++;
    }
    if(!StackEmpty(&ans))          //栈为空说明全部括号都完成了匹配
    {
        return false;
    }
    return true;
}
  • This method makes full use of the last-in-first-out characteristics of stack data
  • The time complexity of the algorithm is O(N)

 

4. Implement stack with two queues ( 225. Implement stack with queue - Leetcode )

This question has no practical significance. The purpose of solving it is only to deepen the understanding of the two data structures of stack and queue.

  • Use two queues to realize data first-in-last-out

Solution interface:

typedef struct 
{

} MyStack;


//创建MyStack结构体
MyStack* myStackCreate() 
{

}


//向MyStack两个队列成员中的非空队列中插入数据
//(若两个队列都为空则向任意一个队列插入数据都可以)
void myStackPush(MyStack* obj, int x) 
{

}


//删除栈顶元素(同时返回栈顶元素的值)
int myStackPop(MyStack* obj) 
{

}



//栈顶的数据其实就是非空队列尾的数据
int myStackTop(MyStack* obj) 
{

}


//返回栈顶的数据
bool myStackEmpty(MyStack* obj) 
{

}


//销毁栈
void myStackFree(MyStack* obj) 
{

}

We can set a rule for ourselves : In the 'stack' interface implemented by the queue, we can only use the standard operation interface of the queue to operate the entry and exit of elements in the 'stack'.

  • In this question, we use the queue implemented in the second section of this issue to implement the stack

The overall diagram of the data structure of the stack with two queues :

Two queues implement the definition of a stack structure:

typedef struct 
{
    Queue queue1;
    Queue queue2;
} MyStack;

Data into the 'stack' interface:

void myStackPush(MyStack* obj, int x) 
  • Insert data into non-empty queues in two queue members of MyStack
  • (If both queues are empty, you can insert data into any queue) 
//向MyStack两个队列成员中的非空队列中插入数据
//(若两个队列都为空则向任意一个队列插入数据都可以)
void myStackPush(MyStack* obj, int x) 
{
    if(!QueueEmpty(&(obj->queue1)))
    {
        QueuePush(&(obj->queue1),x);
    }
    else
    {
        QueuePush(&(obj->queue2),x);
    }
}

Data pop-up interface:

int myStackPop(MyStack* obj) 


Animated illustration of data popping process:

 

//删除栈顶元素(同时返回栈顶元素的值)
int myStackPop(MyStack* obj) 
{
    int ret = 0;
    //将非空队列里面的(除了队尾的元素)移到另外一个空队列中
    if(!QueueEmpty(&(obj->queue1)) && QueueEmpty(&(obj->queue2)))
    {
        while(obj->queue1.head != obj->queue1.tail) //转移数据
        {
            QueuePush(&(obj->queue2),(obj->queue1).head->data);
            QueuePop(&(obj->queue1));               
        }
        ret = QueueFront(&(obj->queue1));   //记录要弹出的数据
        QueuePop(&(obj->queue1));           //弹出数据
    }
    else
    {
        while(obj->queue2.head != obj->queue2.tail) //转移数据
        {
            QueuePush(&(obj->queue1),(obj->queue2).head->data);
            QueuePop(&(obj->queue2));
        }
        ret = QueueFront(&(obj->queue2));    //记录要弹出的数据
        QueuePop(&(obj->queue2));            //弹出数据
    }
    return ret;                              //返回被弹出的数据
}
  • Through the interaction of the two queues, we have completed the first-in-last-out of the data

Solution code:

Queue for problem solving:

typedef int QDataType;

typedef struct QueueNode      //队列链表节点结构体
{
	struct QueueNode* next;
	QDataType data;
}QNode;

typedef struct Queue          //维护队列的结构体
{
	QNode* head;
	QNode* tail;
}Queue;


void QueueInit(Queue* queue);               //队列初始化
void QueueDestroy(Queue* queue);            //销毁链表(队列)
void QueuePush(Queue* queue,QDataType val); //链表尾插数据(数据从队列尾入队)
void QueuePop(Queue* queue);                //删去链表头节点(数据从队列头部出队)
QDataType QueueFront(Queue* queue);         //返回队列头部数据
QDataType QueueBawck(Queue* queue);         //返回队列尾部数据
int QueueSize(Queue* queue);                //返回队列节点个数
bool QueueEmpty(Queue* queue);              //判断队列是否为空


void QueueInit(Queue* queue)       //队列初始化   
{
	assert(queue);
	queue->head = NULL;
	queue->tail = NULL;
}
void QueueDestroy(Queue* queue)    //销毁链表(队列)
{
	assert(queue);
	QNode* tem = queue->head;
	while (tem)                    //逐个节点释放链表
	{
		QNode* Next = tem->next;
		free(tem);
		tem = Next;
	}
	queue->head = NULL;
	queue->head = NULL;
}


void QueuePush(Queue* queue,QDataType val)  //链表尾插数据(数据从队列尾入队)
{
	assert(queue);
	QNode* NewNode = (QNode*)malloc(sizeof(QNode));
	if (NULL == NewNode)                    //在堆区申请节点空间
	{
		perror("malloc failed:");
		exit(-1);
	}
	NewNode->data = val;
	NewNode->next = NULL;

	if (NULL == queue->tail)                //注意判断队列是否为空队列并分类讨论完成元素插入
	{
		assert(NULL == queue->head);
		queue->tail = NewNode;              //队列为空时tail和head指向同一个节点
		queue->head = NewNode;
	}
	else
	{
		queue->tail->next = NewNode;        //队列非空时令tail指向队尾数据
		queue->tail = NewNode;
	}
}


void QueuePop(Queue* queue)		    //删去链表头节点(数据从队列头部出队)
{
	assert(queue);
	assert(queue->head && queue->tail);
	QNode* Next = queue->head->next;
	free(queue->head);
	queue->head = Next;
	if (NULL == queue->head)        //注意判断完成删除元素后队列是否变为空队列
	{
		queue->tail = NULL;
	}
}


QDataType QueueFront(Queue* queue) //返回队列头部数据
{
	assert(queue);
	assert(queue->head);
	return queue->head->data;
}
QDataType QueueBawck(Queue* queue) //返回队列尾部数据
{
	assert(queue);
	assert(queue->tail);
	return queue->tail->data;
}


int QueueSize(Queue* queue)        //返回队列节点个数
{
	assert(queue);
	int count = 0;
	QNode* tem = queue->head;
	while(tem)                      //计算节点个数
	{
		count++;
		tem = tem->next;
	}
	return count;
}

bool QueueEmpty(Queue* queue)      //判断队列是否为空
{
	assert(queue);
	return (NULL == queue->head);
}

Solution stack:

typedef struct 
{
    Queue queue1;
    Queue queue2;
} MyStack;


//创建MyStack结构体
MyStack* myStackCreate() 
{
    MyStack * tem = (MyStack *)malloc(sizeof(MyStack));
    if(NULL == tem)
    {
        perror("malloc failed:");
        exit(-1);
    }
    QueueInit(&(tem->queue1));
    QueueInit(&(tem->queue2));
    return tem;
}


//向MyStack两个队列成员中的非空队列中插入数据
//(若两个队列都为空则向任意一个队列插入数据都可以)
void myStackPush(MyStack* obj, int x) 
{
    if(!QueueEmpty(&(obj->queue1)))
    {
        QueuePush(&(obj->queue1),x);
    }
    else
    {
        QueuePush(&(obj->queue2),x);
    }
}


//删除栈顶元素(同时返回栈顶元素的值)
int myStackPop(MyStack* obj) 
{
    int ret = 0;
    //将非空队列里面的(除了队尾的元素)移到另外一个空队列中
    if(!QueueEmpty(&(obj->queue1)) && QueueEmpty(&(obj->queue2)))
    {
        while(obj->queue1.head != obj->queue1.tail) //转移数据
        {
            QueuePush(&(obj->queue2),(obj->queue1).head->data);
            QueuePop(&(obj->queue1));               
        }
        ret = QueueFront(&(obj->queue1));   //记录要弹出的数据
        QueuePop(&(obj->queue1));           //弹出数据
    }
    else
    {
        while(obj->queue2.head != obj->queue2.tail) //转移数据
        {
            QueuePush(&(obj->queue1),(obj->queue2).head->data);
            QueuePop(&(obj->queue2));
        }
        ret = QueueFront(&(obj->queue2));    //记录要弹出的数据
        QueuePop(&(obj->queue2));            //弹出数据
    }
    return ret;                              //返回被弹出的数据
}



//栈顶的数据其实就是非空队列尾的数据
int myStackTop(MyStack* obj) 
{
    if(!QueueEmpty(&(obj->queue1)))
    {
        return obj->queue1.tail->data;
    }
    else
    {
        return obj->queue2.tail->data;
    }
}


//返回栈顶的数据
bool myStackEmpty(MyStack* obj) 
{
    return QueueEmpty(&(obj->queue1)) && QueueEmpty(&(obj->queue2));
}


//销毁栈
void myStackFree(MyStack* obj) 
{
    QueueDestroy(&(obj->queue1));
    QueueDestroy(&(obj->queue2));
    free(obj);
    obj = NULL;
}

5. Use two stacks to implement queues ( 232. Use stacks to implement queues - Leetcode )

Idea diagram:

 Solution code:

typedef struct 
{
    ST PushStack;
    ST PopStack;   
} MyQueue;


MyQueue* myQueueCreate()    //队列初始化接口
{
    MyQueue* tem = (MyQueue *)malloc(sizeof(MyQueue));
    if(NULL == tem)
    {
        perror("malloc failed:");
        exit(-1);
    }
    StackInit(&(tem->PushStack));
    StackInit(&(tem->PopStack));
    return tem;
}

void myQueuePush(MyQueue* obj, int x) 
{
    StackPush(&(obj->PushStack),x);
    //数据入队
}

int myQueuePop(MyQueue* obj) 
{
    assert(!StackEmpty(&(obj->PushStack)) || !StackEmpty(&(obj->PopStack)));
    //数据出队前保证队列不为空
    if(StackEmpty(&(obj->PopStack)))  //PopStack为空则要将PushStack数据转移进来
    {
        while(!StackEmpty(&(obj->PushStack)))
        {
            StackPush(&(obj->PopStack),StackTop(&(obj->PushStack)));
            //将PushStack栈顶数据压入PopStack栈中
            StackPop(&(obj->PushStack));
            //完成数据转移后弹出PushStack的栈顶数据
        }
    }
    int ret = StackTop(&(obj->PopStack)); //记录队头元素
    StackPop(&(obj->PopStack));           //元素出队
    return ret;                           //返回队头元素
}

int myQueuePeek(MyQueue* obj) 
{
    assert(!StackEmpty(&(obj->PushStack)) || !StackEmpty(&(obj->PopStack)));
    //保证队列不为空
    if(StackEmpty(&(obj->PopStack))) //PopStack为空则要将PushStack数据转移进来
    {
        while(!StackEmpty(&(obj->PushStack)))
        {
            StackPush(&(obj->PopStack),StackTop(&(obj->PushStack)));
            //将PushStack栈顶数据压入PopStack栈中
            StackPop(&(obj->PushStack));
            //完成数据转移后弹出PushStack的栈顶数据
        }
    }
    return StackTop(&(obj->PopStack));
    //返回PopStack栈顶元素作为队列队头元素
}

bool myQueueEmpty(MyQueue* obj) 
{
    return StackEmpty(&(obj->PopStack))  && StackEmpty(&(obj->PushStack));
    //判断队列是否为空
}

void myQueueFree(MyQueue* obj) 
{
    StackDestroy(&(obj->PopStack));
    StackDestroy(&(obj->PushStack));
    free(obj);
    obj = NULL;
}
  • In the problem solution, we use the stack implemented in the first section of this issue

 

Guess you like

Origin blog.csdn.net/weixin_73470348/article/details/129144064