本文最后更新于 2026-06-20T22:30:10+08:00
1. 栈
如何解决括号匹配问题? 所有左括号存入栈中,每来一个右括号,匹配成功则成对弹出,否则报错
特性:后进先出(Last In First Out, LIFO)
- 栈的首部为栈底,尾部为栈顶
- 插入删除均在栈顶进行,分别称为出栈(pop)和进栈(push)
- 此外还有取栈顶(top)和判断是否为空栈的操作
1.1 顺序栈:顺序存储的栈
- bottom=0;
- 栈空标志:top=-1;
- 栈满标志:top=maxSize-1;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
| class seqStack{ private: int *elem; int top_p; int maxSize; void doubleSpace(); public: seqStack(int initSize=10); ~seStack(); bool isEmpty() const; void push(const int &x); int pop(); int top() const; }
seqStack::seqStack(int initSize){ elem=new int [initSize]; maxSize=initSize; top_p=-1; }
~seqStack(){ delete[] elem; }
void seqStack::doubleSpace(){ int *tmp=elem; elem=new int [2*maxSize]; for(int i=0; i<maxSize; i++) elem[i]=tmp[i]; maxSize*=2; delete[] tmp; }
void seqStack::push(const int &x){ if(top_p==maxSize-1) doubleSpace(); elem[++top_p]=x; }
int seqStack::pop(){ return elem[top_p--]; }
bool seqStack::isEmpty() const{ return top_p==-1; }
int seqStack:: top() const{ return elem[top_p]; }
|
扩容时间复杂度是o(n),其他是o(1)
1.2 链式栈:
==栈顶指针top指向处于栈顶的结点,即链表中的首节点==
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
| class node{ int data; node *next; node(const int &data, node *N=NULL){ data=x; next=N; } node():next(NULL){} ~node(){} };
class linkStack{ private: node *top_p; public: linkStack(); ~linkStack(); bool isEmpty()const; void push(const int &x); int pop(); int top() const; }
linkStack::linkStack(){ top_p=NULL; }
linkStack::~linkStack(){ node *tmp; while(top_p!=NULL){ tmp=top_p; top_p=top_p->next; delete tmp; } }
void linkStack::push (const int &x){ top_p=new node (x,top_p); }
int linkStack::pop(){ node *tmp=top_p; int x=tmp->data; top_p=top_p->next; delete tmp; return x; }
bool linkStack::isEmpty() const{ return top_p==NULL;' }
int linkStack::top() const{ return top_p->data; }
|
应用:表达式求值
三种表达形式:
- 中缀表达式:运算符在操作数中间
- 后缀表达式(逆波兰式):运算符在操作数后
- 前缀表达式(波兰式):运算符在操作数前
使用后缀表达式
中缀转后缀方法:数字顺序不变,根据实际运算顺序调整符号顺序:
- 初始化空栈,用于保存运算符,初始化一个空输出队列
- 从左到右扫描中缀表达式:遇到操作数直接加入输出队列,遇到(入栈,遇到),弹出栈顶元素至输出队列,直到遇到(,(出栈但是不输出,遇到运算符,比较优先级,弹出栈顶优先级大于等于当前运算符的元素,再入栈当前运算符
- 扫描完成后,将栈中剩余运算符依次弹出至输出队列
后缀式计算方法:
- 初始化空栈,用于保存操作数
- 从左到右扫描后缀表达式,遇到操作数入栈,遇到运算符,弹出栈顶两个元素,计算结果入栈
- 最终栈顶元素为计算结果
2. 队列
一段插入,另一端删除
先进先出(First In First Out, FIFO)
2.1 顺序队列:
队列中元素个数最多为maxSize个,下标范围从0到maxSize-1,front指向队首元素,rear指向队尾元素
采用循环数组,且规定front指向的单元不能存储队列元素,只起到一个标志作用,表示后面一个是对头元素。
故队列满的条件是:(rear+1) % MaxSize == front;
队列为空的条件为 front == rear。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
| class seqQueue{ private: int *elem; int maxSize; int front,rear; void doublespace(); public: seqQueue(int size=10); ~seqQueue(); bool isEmpty() const; void enQueue(const int &x); in tdeQueue(); int getHead() const; }
seqQueue::seqQueue(int size){ elem=new int [size]; maxSize=size; front=rear=0; }
seqQueue::~seqQueue(){ delete[] elem; }
bool seqQueue::isEmpty() const{ return front==rear; }
int seqQueue::deQueue(){ front=(front+1)%maxSize; return elem[front]; }
int seqQueue::getHead() const{ return elem[(front+1)%maxSize]; }
void seqQueue::enQueue(const int &x){ if((rear+1)%maxSize==front) doubleSpace(); rear=(rear+1)%maxSize; elem[rear]=x; }
void seqQueue::doubleSpace(){ int *tmp=elem; elem=new elemType[2*maxSize]; for(int i=1;i<=maxSize;++i) elem[i]=tmp[(front+i)%maxSize]; front=0; rear=maxSize; maxSize*=2; delete tmp; }
|
2.2 链式队列
队首指针front指向队首结点,队尾指针rear指向队尾节点。不存在队满的情况,队空时,front=rear=NULL
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
| class linkQueue{ private: struct node{ int data; node *next; node(const int &x, node *N=NULL):data(x),next(NULL){} ~node(){} }; node *front, *rear; public: linkQueue(); ~linkQueue; bool isEmpty() const; void enQueue(const int &x); int deQueue(); int getHead() const; }
linkQueue::linkQueue(){ front=rear=NULL; }
linkQueue::~linkQueue(){ node *tmp; while(front!=NULL){ tmp=front; front=front->next; delete tmp; } }
bool linkQueue::isEmpty() const{ return front==NULL }
int linkQueue::getHead() const{ return front->data; }
void linkQueue::enQueue(const int &x){ if(rear==NULL) front=rear=new node(x); else rear=rear->next=new node(x); }
int linkQueue::deQueue(){ node *tmp=front; int value=front->data; front=front->next; if(front==NULL) rear=NULL; delete tmp; return value; }
|
2.3 优先队列
进入队列的元素有优先级,==优先级越高,出队越早,优先级越低,出队越晚==
顺序优先队列:
方案一:进队时不排序,出队时找到优先级最高的出队,可以不用使用循环队列,直接把队尾插入出队元素的存储位置,队空时rear=0,队满时rear=maxsize
方案二:进队时就将元素按优先级由小到大插入,出队时删去队头元素即可,采用循环队列
两种方法时间复杂度均为o(n)
2.4 单调队列(了解即可)
一个满足单调性质的双向队列,队内元素排列顺序满足单调递增或递减。若心如对元素破坏单调性,弹出对内元素,直至满足单调性再入队
可用于求一个序列中,连续k个数的最大值和最小值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| # include <iostream> using namespace std; const int MAXN=100000;
int n,k;
int a[MAXN]; int ans[MAXN];
int main(){ cin>>n>>k; for(int i=1;i<=n;++i){ cin>>a[i]; } int head=1; int tail=0; int q[MAXN]={0}; for(int i=1;i<=n;++i){ while (head<=tail && a[q[tail]]>=a[i]) --tail; q[++tail]=i; while (q[head]<=i-k) ++head; if(i>=k) ans[i]=a[q[head]]; } for(int i=k;i<=n;++i){ cout<<ans[i]<<" "; } return 0; }
|