数据结构:队列 和 栈 的详解

Posted 正义的伙伴啊

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了数据结构:队列 和 栈 的详解相关的知识,希望对你有一定的参考价值。

栈的结构和概念

:一种特殊的线性表,其中只允许在固定的一端进行插入和删除元素。进行数据插入和删除操作的一段称为栈顶,另一端称为栈底 。栈中的元素遵循先进后出的原则。
压栈:栈的插入操作叫做 进栈/入栈/压栈,入数据在栈顶
出栈:栈的删除操作叫做出栈,出数据也在栈顶

栈的实现

栈的实现一般可以使用数组或者链表,但是数组的结构要更优一点。因为数组在尾上插入数据的代价比较小。

其实两种数据结构都可以实现链表,但是为什么选择数组呢
因为由于栈的特性,使得如果使用链表,那么栈顶也就是出入元素的位置在链表的末尾,每次入栈、出栈都要从头节点遍历整个链表。

栈的实现:

#define _CRT_SECURE_NO_WARNINGS 1

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


typedef int Datatype;
typedef struct Stack
{
	Datatype* a;
	int top;
	int Capacity;
}Stack;

void StackInit(Stack* p);   //初始化栈
void StackPush(Stack* p, Datatype x);  //入栈操作
void StackPop(Stack* p);    //出栈操作
Datatype StackTop(Stack* p);   //返回栈顶元素的值
int StackEmpty(Stack* p);    //检查栈是否为空
void StackDestroy(Stack* p);   //销毁栈
void StackPrint(Stack* p);     //打印栈的元素

以上时栈的结构和具体的基本操作

下面是这些具体操作的实现

#define _CRT_SECURE_NO_WARNINGS 1
#include"stack.h"
void StackInit(Stack* p)
{
	assert(p);
	p->a = (Datatype *)malloc(4*sizeof(Datatype));
	p->top = 0;
	p->Capacity = 4;
}
void StackPush(Stack* p, Datatype x)
{
	assert(p);
	if (p->top == p->Capacity)  //检查是否要增容
	{
		p->a = (Datatype *)realloc(p->a,2 * p->Capacity*sizeof(Datatype));
		p->Capacity = p->Capacity * 2;
	}
	p->a[p->top] = x;
	p->top++;
}
void StackPop(Stack* p)
{
	assert(p);
	if(p->top)
	p->top = p -> top - 1;
}
void StackPrint(Stack* p)
{
	assert(p);
	for (int i = 0; i < p->top; i++)
	{
		printf(" %d ", p->a[i]);
	}
	printf("\\n");
}
Datatype StackTop(Stack* p)
{
	return p->a[p->top - 1];
}
int StackEmpty(Stack* p)
{
	return p->top == 0;
}
void StackDestroy(Stack* p)
{
	free(p->a);
	p->a = NULL;
	p->top = 0;
	p->Capacity = 0;
}

栈的一些应用

括号匹配问题

leetcode——原题

//上面要引用栈的实现和相应实现的函数,为了能更清晰的看判断函数,我就不在前面复制了
bool isValid(char * s){
    Stack ps;
    StackInit(&ps);
    while(*s)
    {
        if(*s=='(' || *s=='{' || *s=='[')
        {
            StackPush(&ps,*s);
            s++;
        }
        else
        {
            if(StackEmpty(&ps))
            return false;
            char top=StackTop(&ps);
            StackPop(&ps);
            if((top=='[' && *s!=']')||(top=='{' && *s!='}')||(top=='(' && *s!=')'))
            return false;
            s++;
        }
    }
    if(StackEmpty(&ps))
    return true;
    return false;
}

这题的思路是:

  • 循环遍历字符串s,如果*s是 ‘(’ 、’{’、 ‘[’ 中的任意一个,就将该字符入栈
  • 如果*s不是以上字符也就是’)’ 、’}’、 ‘]’ ,那就那他和栈顶的元素进行匹配,如果匹配了就将栈顶的元素出栈,不匹配就直接return false结束判断。循环完成返回return true

但是还是要注意一些特殊情况:

  1. 当字符串为 “(” 时,这时就要在末尾的return true前面判断栈是否为空,如果栈不为空,说明栈里面还有未匹配的字符,所以要返回false
  2. 当字符串为")"时,我们要在第二部也就时取栈顶元素之前判断栈是否为空,如果为空的话,就说明栈里面没有与右括号所匹配的左括号,返回false

用栈实现队列

leetcode-用栈实现队列
用队列实现栈主要的难点是在 出栈 和 返回栈顶元素 两个操作上
但是两个函数思路是一摸一样的
以 出栈函数 myQueuePop函数为例

这里为了实现队列,我们要创建两个栈来相互倒来将队首元素拿出,具体操作如下:

  1. 首先将s1栈的栈顶元素入到s2栈中,然后将栈顶元素出栈。重复上述操作直到s1栈为空。

  2. 这时整个栈的元素相当于逆置了,队首元素现在在栈顶的位置,下面的操作在 myQueuePop和myQueuePeek操作略有不同

               -  myQueuePop 要把s2栈顶的值出栈
               - myQueuePeek 不用把s2的栈顶的值
    
  3. 最后首先将s2栈的栈顶元素入到s1栈中,然后将栈顶元素出栈。重复上述操作直到s2栈为空

代码:

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


typedef int Datatype;
typedef struct Stack
{
	Datatype* a;
	int top;
	int Capacity;
}Stack;
void StackInit(Stack* p)
{
	assert(p);
	p->a = (Datatype*)malloc(4 * sizeof(Datatype));
	p->top = 0;
	p->Capacity = 4;
}
void StackPush(Stack* p, Datatype x)
{
	assert(p);
	if (p->top == p->Capacity)  //检查是否要增容
	{
		p->a = (Datatype*)realloc(p->a, 2 * p->Capacity * sizeof(Datatype));
		p->Capacity = p->Capacity * 2;
	}
	p->a[p->top] = x;
	p->top++;
}
void StackPop(Stack* p)
{
	assert(p);
	if (p->top)
		p->top = p->top - 1;
}
void StackPrint(Stack* p)
{
	assert(p);
	for (int i = 0; i < p->top; i++)
	{
		printf(" %d ", p->a[i]);
	}
	printf("\\n");
}
Datatype StackTop(Stack* p)
{
	return p->a[p->top - 1];
}
int StackEmpty(Stack* p)
{
	return p->top == 0;
}
void StackDestroy(Stack* p)
{
	free(p->a);
	p->a = NULL;
	p->top = 0;
	p->Capacity = 0;
}
//以上都是栈的基本操作,下面才是队列的实现
typedef struct {
	Stack s1;
	Stack s2;

} MyQueue;

/** Initialize your data structure here. */

MyQueue* myQueueCreate() {
	MyQueue* q=(MyQueue*)malloc(sizeof(MyQueue));
	StackInit(&(q->s1));
	StackInit(&(q->s2));
	return q;
}

/** Push element x to the back of queue. */
void myQueuePush(MyQueue* obj, int x) {
	int a = 0;
	StackPush(&(obj->s1), x);
}

/** Removes the element from in front of queue and returns that element. */
int myQueuePop(MyQueue* obj) {
	while (StackEmpty(&(obj->s1))==0)
	{
		StackPush(&(obj->s2), StackTop(&(obj->s1)));
		StackPop(&(obj->s1));
	}
	Datatype a = StackTop(&(obj->s2));
	StackPop(&(obj->s2));
	while (StackEmpty(&(obj->s2))==0)
	{
		StackPush(&(obj->s1), StackTop(&(obj->s2)));
		StackPop(&(obj->s2));
	}
	return a;

}

/** Get the front element. */
int myQueuePeek(MyQueue* obj) {
	while (StackEmpty(&(obj->s1)) == 0)
	{
		StackPush(&(obj->s2), StackTop(&(obj->s1)));
		StackPop(&(obj->s1));
	}
	Datatype a = StackTop(&(obj->s2));
	while (StackEmpty(&(obj->s2)) == 0)
	{
		StackPush(&(obj->s1), StackTop(&(obj->s2)));
		StackPop(&(obj->s2));
	}
	return a;


}

/** Returns whether the queue is empty. */
bool myQueueEmpty(MyQueue* obj) {
	return StackEmpty(&(obj->s1));

}

void myQueueFree(MyQueue* obj) {
	StackDestroy(&(obj->s1));
	StackDestroy(&(obj->s2));
	obj = NULL;
}

队列

队列的概念和结构

队列: 只允许在一段进行插入数据操作,在另一端进行删除数据操作的特殊线性表,队列具有先进先出FIFO(first in first out) 入队列、进行插入操作的一端称为队尾 出队列、进行删除的一段称为队头

队列的实现

队列的实现同样可以使用数组和链表的结构实现,使用链表的结构实现更优一些,因为如果使用数组的结构,出队列在数组头上出数据,效率会比较低

#define _CRT_SECURE_NO_WARNINGS 1

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

typedef int DataType;
typedef struct BinaryTree
{
	DataType x;
	struct BinaryTree* _left;
	struct BinaryTree* _right;
}BT;

typedef BT* Datatype;
typedef struct ListNode
{
	Datatype x;
	struct ListNode* next;
}ListNode;

typedef struct Queue
{
	ListNode* head;
	ListNode* tail;
}Queue;

void QueueInit(Queue* q);  //初始化队列

void QueuePush(Queue* q, Datatype x);  // 入队

void QueuePop(Queue* q);    // 出队

Datatype QueueFront(Queue* q);  //输出队列队头的元素

Datatype QueueBack(Queue* q); // 输出队列队尾的元素
  
int QueueSize(Queue* q);    //输出队列的大小

int QueueEmpty(Queue* q);   //判断队列是否为空

void QueueDestroy(Queue* q);   //队列销毁

以上时栈的结构和具体的基本操作

下面是这些具体操作的实现

#define _CRT_SECURE_NO_WARNINGS 1

#include"Queue.h"

void QueueInit(Queue* q)
{
	ListNode* First = (ListNode*)malloc(sizeof(ListNode));
	q->head = q->tail = First;
	q->tail->next = NULL;
}

void QueuePush(Queue* q, Datatype x)
{
	assert(q);
	q->tail->x = x;
	ListNode* NewNode = (ListNode*)malloc(sizeof(ListNode));
	q->tail->next = NewNode;
	q->tail = NewNode;
	q->tail->next = NULL;
}

void QueuePop(Queue* q)
{
	assert(q);
	assert(!QueueEmpty(q));
	if (!QueueEmpty(q))
	{
		ListNode* temp = q->head->next;
		free(q->head);
		q->head = temp;
	}

}

Datatype QueueFront(Queue* q)
{
	assert(q);
	assert(!QueueEmpty(q));
	return q->head->x;
}

Datatype QueueBack(Queue* q)
{
	assert(q);
	assert(!QueueEmpty(q));
	ListNode* cur = q->head;
	while (cur->next != q->tail)
	{
		cur = cur->next;
	}
	return cur->x;
}

int QueueSize(Queue* q)
{
	if (QueueEmpty(q))
	{
		return 0;
	}
	ListNode* cur = q->head;
	int k = 0;
	while (cur != q->tail)
	{
		cur = cur->next;
		k++;
	}
	return k;
}

int QueueEmpty(Queue* q)
{
	return q->head == q->tail;
}

void QueueDestroy(Queue* q)
{
	ListNode* cur = q->head;
	while (cur != q->tail)
	{
		ListNode* temp = cur->next;
		free(cur);
		cur = temp;
	}
	free(cur);
	cur = NULL;
	q->head = NULL;
	q->tail = NULL;

}

队列的应用

用队列实现栈

leetcode——原题
实现思路: 用两个队列来实现栈的功能,栈用队列的实现不同主要在myStackPop这个函数上,该如何得到队尾的元素是本题的难点:
开卷数据结构~栈和队列详解

数据结构 Java 版详解栈和队列的实现

数据结构 Java 版详解栈和队列的实现

单调队列与单调栈用法详解

栈和队列数据结构的特点,啥情况下用到栈,啥情况下用到队列(各举3个例子)

Java数据结构栈与队列详解