1. 概念 : 栈 vs 队列
1.1 栈
栈是一种特殊的线性表,只能在一端进行插入删除元素的操作。可以进行数据插入删除的一端称为栈顶,另一端称为栈底。 战中的元素遵循LIFO( Last In First Out )后进先出的原则
压栈:栈的元素插入操作称为压栈 / 入栈,入数据在栈顶
出栈:栈的删除操作称为出栈,出数据也在栈顶
1.2 队列
队列也是一种特殊的线性表。队列只允许在一端进行插入操作,在另一端进行删除数据的操作
队列遵循FIFO( First In First Out ) 先进先出的原则
2. 基本结构的实现 : 栈 vs 队列
栈可以使用链表或者数组进行实现 但是数组相对更优(数组在尾插数据的时候代价较小
栈也可以使用链表或者数组实现 链表更优( 出队列的时候删除数组首元素 "头删" 效率低
2.1 栈
2.1.1 静态栈
静态栈定长,实际一般不使用
typedef int SLDataType;
#define N 10
typedef struct Stack
{
SLDataType arr[N];
int top;
}Stack;
2.1.2 动态增长栈
typedef int SLDataType;
typedef struct Stack
{
SLDataType* arr;
int top;
int capacity;
}Stack;
//初始化
void StackInit(Stack* ps){
assert(ps);
ps->arr = NULL;
ps->capacity = 0;
}
//入栈
void StackPush(Stack* ps, SLDataType x){
assert(ps);
if(ps->top == ps->capacity){
int newCapacity = ps->capacity = 0 ? 4 : 2 * ps->capacity;
SLDataType* temp = (SLDataType*)realloc(ps->arr, newCapacity * sizeof(Stack));
assert(temp);
ps->arr = temp;
ps->capacity = newCapacity;
}
ps->arr[ps->top] = x;
ps->top++;
}
//出栈
void StackPop(Stack* ps){
assert(ps);
assert(ps->top > 0);
ps->top--;
}
//获取栈顶元素
SLDataType StackTop(Stack* ps){
assert(ps);
assert(ps->top > 0);
return ps->arr[ps->top - 1];
}
//获取栈顶有效元素的个数
int StackSize(Stack* ps){
assert(ps);
return ps->top;
}
//检测栈是否为空
bool StackEmpty(Stack* ps){
assert(ps);
return ps->top == 0;
}
//销毁栈
void StackDestrory(Stack* ps){
assert(ps);
free(ps->arr);
ps->arr = NULL;
ps->top = ps->capacity = 0;
}
2.2 队列
typedef int QDataType;
typedef struct QNode{
struct QNode* next;
QDataType val;
}QNode;
typedef struct Queue{
QNode* head;
QNode* tail;
int size;
}Queue;
void QueueInit(Queue* pq){
assert(pq);
pq->head = pq->tail = NULL;
pq->size = 0;
}
void QueueDestroy(Queue* pq){
assert(pq);
QNode* cur = pq->head;
while(cur){
QNode* next = cur->next;
free(cur);
cur = next;
}
pq->head = pq->tail = NULL;
pq->size = 0;
}
void QueuePush(Queue* pq, QDataType x){
assert(pq);
QNode* node = (QNode*)malloc(sizeof(QNode));
assert(node);
node->val = x;
node->next = NULL;
if(pq->head == pq->tail)
pq->head = pq->tail = node;
else{
pq->tail->next = node;
pq->tail = node;
}
ps->size++;
}
void QueuePop(Queue* pq){
assert(pq && pq->head);
QNode* node = pq->head;
pq->head = pq->head->next;
free(node);
node = NULL;
if(pq->head == NULL)
pq->tail = NULL;
pq->size--;
}
QDataType QueueFront(Queue* pq){
assert(pq && pq->head);
return pq->head->val;
}
QDataType QueueBack(Queue* pq){
assert(pq && pq->tail);
return pq->tail->val;
}
bool QueueEmpty(Queue* pq){
assert(pq);
return pq->head == NULL;
}
int QueueSize(Queue* pq){
assert(pq);
return pq->size;
}
3. some 经典题目们 :
3.1 括号匹配问题:
“有效的括号” : 数量 and 顺序 都匹配
==== >>>>> 左括号入栈,取栈顶与右括号匹配 if no ==== 报错
===== >>>>> 栈为空 可以解决 左括号多右括号少 的问题,BUT if 右多左少 怎么办
=======》》》》 可以 一上来就先分别计算左右的数量 不符合直接false 再进行接下来操作
3.2 队列实现栈:
开两个队列 一个存数据,一个出数据的时候导数据
3.3 设计循环队列:
循环队列是空间的重复利用
数组 / 循环链表 都OK ===》》》 链表取队尾数据不方便 ==>> 双向循环链表 / backprev 指针
如果back指向尾,如何区分 1 和 空 ? ( back 不能指向 -1 back 指针 ) ===== 》》》》 back 指向尾的下一个
【 使用数组解决】
front ,back 表示下标 数组空间之后,再开一个空间 size
判满 : front == back ------ >> (back + 1) % k == front k:数组长度【不算size的空间
将存储数据的size 移动到数组首元素处
front ——> back : 有效空间