欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 财经 > 金融 > 数据结构——栈和队列

数据结构——栈和队列

2025/3/19 21:55:50 来源:https://blog.csdn.net/m0_72164003/article/details/146286829  浏览:    关键词:数据结构——栈和队列

栈和队列

1. 栈

1.1 栈的基本概念

栈:一种特殊的线性表,其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端称为栈顶,另一端称为栈底。栈中的数据元素遵守后进先出LIFO(Last In First Out)的原则。

压栈:栈的插入操作叫做进栈/压栈/入栈,入数据在栈顶

出栈:栈的删除操作叫做出栈。出数据也在栈顶

https://cdn.jsdelivr.net/gh/junmoxiao6661/pigo_image@main/202412011423807.png

https://cdn.jsdelivr.net/gh/junmoxiao6661/pigo_image@main/202412011424964.png

1.2 栈的实现

栈的基本操作如下:

https://cdn.jsdelivr.net/gh/junmoxiao6661/pigo_image@main/202412011424549.png

1.2.1 顺序栈

https://cdn.jsdelivr.net/gh/junmoxiao6661/pigo_image@main/202412011428893.png

https://cdn.jsdelivr.net/gh/junmoxiao6661/pigo_image@main/202412011429368.png

class SeqStack{
public:SeqStack():a(nullptr),_size(0),capacity(InitSize){a=(T*)malloc(sizeof(T)*InitSize);}void push(T  x){if(_size==capacity){T *temp=(T*)realloc(a,sizeof(T)*capacity*2);if(temp==nullptr){perror("malloc fail");return ;}a=temp;capacity*=2;}a[_size++]=x;}void pop(){assert(_size!=0);_size--;}T top(){assert(_size!=0);return a[_size-1];}bool empty(){return _size==0;}int size(){return _size;}~SeqStack(){}
private:T *a;int _size;int capacity;
};

1.2.2 链栈

链栈的实现就是头插法。

https://cdn.jsdelivr.net/gh/junmoxiao6661/pigo_image@main/202412011427270.png

#include <iostream>
#include <cassert>template <class T>struct SNode
{T data;SNode *next;SNode(T a) : data(a), next(nullptr) {}
};template <class T>class LinkStack
{typedef SNode<T> SNode;public:LinkStack() : _size(0), head(nullptr) {}void push(T x){SNode *node = new SNode(x);if (head == nullptr){head = node;}else{node->next = head;head = node;}_size++;}void pop(){assert(_size != 0);if (head->next == nullptr){delete head;head = nullptr;}else{SNode *temp = head->next;delete head;head = temp;}_size--;}int size(){return _size;}T top(){assert(_size != 0);return head->data;}bool empty(){return _size == 0;}~LinkStack() {}private:int _size;SNode *head;
};

2. 队列

2.1 队列的基本概念

队列:只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表,队列具有先进先出FIFO(First In First Out)。

入队列:进行插入操作的一端称为队尾

出队列:进行删除操作的一端称为队头

https://cdn.jsdelivr.net/gh/junmoxiao6661/pigo_image@main/202412011609008.gif

2.2 队列的实现

队列的基本操作如下:

https://cdn.jsdelivr.net/gh/junmoxiao6661/pigo_image@main/202412011611196.png

队列可以使用顺序、链式等结构存储,通常链式存储更为方便。

2.2.1 顺序队列

队列的顺序实现是指分配一块连续的存储单元存放队列中的元素,并附设两个指针:队头指针 front 指向队头元素,队尾指针rear指向队尾元素的下一个位置(不同教材对front 和rear的定义可能不同)。这一点很重要,一定要看清楚rear和front具体指哪个元素。

https://cdn.jsdelivr.net/gh/junmoxiao6661/pigo_image@main/202412011619205.png

初始:rear=front=0

判断队列为空:rear=front

入队操作:q[rear++]=x

出队操作:x=q[front++]

https://cdn.jsdelivr.net/gh/junmoxiao6661/pigo_image@main/202412011624012.png

当front=0,rear=M时,再有元素入队发生溢出——真溢出
当front≠0,rear=M时,再有元素入队发生溢出——假溢出
解决方法:

当rear = MAXSIZE 时,即超过队列末端时,
令rear = 0;从而使队列的首尾相连接。  
用表达式描述:       
if (rear == MAXSIZE )           rear = 0 ;       
else            rear = rear + 1 ;

2.2.2 循环队列

上面指出了顺序队列“假溢出”的问题,这里引出循环队列的概念。将顺序队列臆造为一个环状的空间,即把存储队列元素的表从逻辑上视为一个环,称为循环队列。当队首指针 Q.front=Maxsize-1后,再前进一个位置就自动到0,这可以利用除法取余运算%来实现。

初始:rear=front=0

入队操作:q[rear]=x; rear=(rear+1)%Maxsize

出队操作:x=q[front]; front=(front+1)%Maxsize

判断队空队满的方法

队满:(rear+1)%M==front

队空:rear==front

队列元素个数:(rear-front+M)%M

入队与出队操作一般是不变的,无论rear和front指哪个元素,但是队满、队空、初始条件会不一样。

https://cdn.jsdelivr.net/gh/junmoxiao6661/pigo_image@main/202412011637153.png

https://cdn.jsdelivr.net/gh/junmoxiao6661/pigo_image@main/202412011633678.png

循环队列的实现:

https://cdn.jsdelivr.net/gh/junmoxiao6661/pigo_image@main/202412011640090.png

2.2.3 链式队列

队列的链式表示称为链队列,它实际上是一个同时有队头指针和队尾指针的单链表。头指针指向队头结点,尾指针指向队尾结点,即单链表的最后一个结点。

https://cdn.jsdelivr.net/gh/junmoxiao6661/pigo_image@main/202412011643682.png

#include <iostream>
#include <cassert>
#include <cstdlib>template <class T>struct QNode
{T data;QNode *next;QNode(T a) : data(a), next(nullptr) {}
};
// 使用链式存储实现队列,便于实现其操作
template <class T>class LinkQueue
{typedef QNode<T> QNode;public:LinkQueue() : head(nullptr), tail(nullptr), _size(0) {}void push(T x)//尾插法{QNode *node = new QNode(x);if (head == nullptr){head = tail = node;}else{tail->next = node;tail = node;}_size++;}void pop(){assert(_size != 0);if (head->next == nullptr){free(head);head = nullptr;}else{QNode *temp = head->next;delete head;head = temp;}_size--;}bool empty(){return _size == 0;}size_t size(){return _size;}T front(){assert(_size != 0);return head->data;}T back(){assert(_size != 0);return tail->data;}~LinkQueue(){QNode *cur = head;while (cur){QNode *temp = cur->next;free(cur);cur = temp;}head = tail = nullptr;_size = 0;}private:QNode *head;QNode *tail;int _size;
};

3. 栈与队列的应用

3.1 括号匹配问题

给定一个只包括 '('')''{''}''['']' 的字符串 s ,判断字符串是否有效。

有效字符串需满足:

  1. 左括号必须用相同类型的右括号闭合。
  2. 左括号必须以正确的顺序闭合。
  3. 每个右括号都有一个对应的相同类型的左括号。

https://leetcode.cn/problems/valid-parentheses/description/

// 顺序栈实现
typedef struct Stack {char data[10010];int top;
} Stack;
void InitStack(Stack* s) { s->top = 0; }void StackPush(Stack* s, char x) {s->data[s->top] = x;s->top++;
}
void StackPop(Stack* s) { s->top--; }char StackTop(Stack* s) { return s->data[s->top - 1]; }bool StackEmpty(Stack* s) { return s->top == 0; }bool isValid(char* s) {Stack st;InitStack(&st);int n = strlen(s);for (int i = 0; i < n; i++) {if(s[i]=='('||s[i]=='{'||s[i]=='[') StackPush(&st,s[i]);else{if(StackEmpty(&st))return false;else if(s[i]==')'&&StackTop(&st)!='(')  //只考虑错误情况return false;else if(s[i]=='}'&&StackTop(&st)!='{')return false;else if(s[i]==']'&&StackTop(&st)!='[')return false;StackPop(&st);}}return StackEmpty(&st);
}

/需要注意的是如果不返回结点,那么就要使用二级指针来写,链表章节注意的问题。

// 链栈实现
typedef struct SNode{struct SNode *next;char val;
}SNode;
void InitStack(SNode* s) 
{ s->next=NULL;
}
// 需要注意的是如果不返回结点,那么就要使用二级指针来写
void StackPush(SNode** s, char x) {SNode *temp = (SNode*)malloc(sizeof(SNode));temp->val = x;temp->next = *s;*s = temp;
}void StackPop(SNode** s) {if (*s != NULL) {SNode *temp = *s;*s = (*s)->next;free(temp);}
}char StackTop(SNode* s) { return s->val; }bool StackEmpty(SNode* s) { return s->next == NULL; }bool isValid(char* s) {SNode *st=(SNode*)malloc(sizeof(SNode));InitStack(st);int n = strlen(s);for (int i = 0; i < n; i++) {if(s[i]=='('||s[i]=='{'||s[i]=='[')StackPush(&st,s[i]);else{if(StackEmpty(st))return false;else if(s[i]==')'&&StackTop(st)!='(')return false;else if(s[i]=='}'&&StackTop(st)!='{')return false;else if(s[i]==']'&&StackTop(st)!='[')return false;StackPop(&st);}}return StackEmpty(st);
}

typedef struct SNode {struct SNode* next;char val;
} SNode;
void InitStack(SNode* s) { s->next = NULL; }SNode* StackPush(SNode* s, char x) {SNode* temp = (SNode*)malloc(sizeof(SNode));temp->val = x;temp->next = s;return temp;
}
SNode* StackPop(SNode* s) {SNode* temp = s->next;free(s);return temp;
}char StackTop(SNode* s) { return s->val; }bool StackEmpty(SNode* s) { return s->next == NULL; }bool isValid(char* s) {SNode* st = (SNode*)malloc(sizeof(SNode));InitStack(st);int n = strlen(s);for (int i = 0; i < n; i++) {if (s[i] == '(' || s[i] == '{' || s[i] == '[')st = StackPush(st, s[i]);else {if (StackEmpty(st))return false;else if (s[i] == ')' && StackTop(st) != '(')return false;else if (s[i] == '}' && StackTop(st) != '{')return false;else if (s[i] == ']' && StackTop(st) != '[')return false;st = StackPop(st);}}return StackEmpty(st);
}

3.2 表达式问题

前、中、后缀表达式

3.3 栈实现队列

请你仅使用两个栈实现先入先出队列。队列应当支持一般队列支持的所有操作(pushpoppeekempty):

实现 MyQueue 类:

  • void push(int x) 将元素 x 推到队列的末尾
  • int pop() 从队列的开头移除并返回元素
  • int peek() 返回队列开头的元素
  • boolean empty() 如果队列为空,返回 true ;否则,返回 false

说明:

  • 只能 使用标准的栈操作 —— 也就是只有 push to top, peek/pop from top, size, 和 is empty 操作是合法的。
  • 你所使用的语言也许不支持栈。你可以使用 list 或者 deque(双端队列)来模拟一个栈,只要是标准的栈操作即可。

https://leetcode.cn/problems/implement-queue-using-stacks/description/

一个栈用来存数据,一个用来出数据,出数据的栈为空时,将如数据栈的元素存入出数据栈。

class MyQueue {
public:MyQueue() {}void push(int x) { s1.push(x); }int pop() {int res = peek();s2.pop();return res;}int peek() {if (s2.empty()) {while (!s1.empty()) {s2.push(s1.top());s1.pop();}}int res = s2.top();return res;}bool empty() { return s1.empty() && s2.empty(); }private:stack<int> s1;stack<int> s2;};
typedef struct {int st1[100];int st2[100];int top1;int top2;
} MyQueue;MyQueue* myQueueCreate() {MyQueue *q = (MyQueue*)malloc(sizeof(MyQueue));q->top1=0;q->top2=0;return q;
}void myQueuePush(MyQueue* obj, int x) {obj->st1[obj->top1++]=x;
}int myQueuePeek(MyQueue* obj) {if(obj->top2==0){for(int i=obj->top1-1;i>=0;i--){obj->st2[obj->top2++]=obj->st1[i];}obj->top1=0;}int res=obj->st2[obj->top2-1];return res;
}int myQueuePop(MyQueue* obj) {int res=myQueuePeek(obj);obj->top2--;return res;
}bool myQueueEmpty(MyQueue* obj) {return obj->top1==0&&obj->top2==0;
}void myQueueFree(MyQueue* obj) {free(obj);obj=NULL;
}

3.4 队列实现栈

请你仅使用两个队列实现一个后入先出(LIFO)的栈,并支持普通栈的全部四种操作(pushtoppopempty)。

实现 MyStack 类:

  • void push(int x) 将元素 x 压入栈顶。
  • int pop() 移除并返回栈顶元素。
  • int top() 返回栈顶元素。
  • boolean empty() 如果栈是空的,返回 true ;否则,返回 false

注意:

  • 你只能使用队列的标准操作 —— 也就是 push to backpeek/pop from frontsizeis empty 这些操作。
  • 你所使用的语言也许不支持队列。 你可以使用 list (列表)或者 deque(双端队列)来模拟一个队列 , 只要是标准的队列操作即可。

https://leetcode.cn/problems/implement-stack-using-queues/description/

class MyStack {
public:MyStack() {}void push(int x) {int n = q1.size();q1.push(x);for (int i = 0; i < n; i++) {  // 将元素重新pushq1.push(q1.front());q1.pop();}}int pop() {int res = top();q1.pop();return res;}int top() { return q1.front(); }bool empty() { return q1.empty(); }private:queue<int> q1;
};

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词