目录
1. 栈
栈(stack)是一种特殊的线性表,只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端称为栈顶(top),另一端称为栈底(bottom)。栈中的数据元素遵守后进先出(Last In First Out, LIFO)的原则。
栈的数学性质:n个不同元素进栈,出栈元素不同排列的个数为(卡特兰数)。
类似于线性表,栈也有顺序存储和链接存储两种存储结构。常用顺序栈。
顺序栈:
链栈:
表头为栈顶
1.1 顺序栈的存储
1.1.1 顺序栈的静态存储
#define N 10
typedef int STDataType;
typedef struct Stack
{
STDataType a[N];
int top;
// top初始化为0时,表示栈顶下一个,top的数值等于栈中元素个数
// top初始化为-1时,表示栈顶,top的数值等于栈中元素个数-1
}ST;
1.1.2 顺序栈的动态存储
typedef int STDataType;
typedef struct Stack
{
STDataType* a;
int capacity;
int top;
// top初始化为0时,表示栈顶下一个,top的数值等于栈中元素个数
// top初始化为-1时,表示栈顶,top的数值等于栈中元素个数-1
}ST;
1.2 动态顺序栈的接口实现
1.2.1 初始化
void StackInit(ST* ps)
{
assert(ps);
// 方法一:初始化时不开辟空间
ps->a = NULL;
ps->top = 0; // 初始化为0时,表示栈顶下一个;初始化为-1时,表示栈顶
ps->capacity = 0;
/*
方法二:初始化时开辟4个空间
ps->a = (STDataType*)malloc(sizeof(STDataType) * 4);
if (ps->a == NULL)
{
perror("malloc failed");
exit(-1);
}
ps->top = 0;
ps->capacity = 4;
*/
}
top初始化为0时,表示栈顶下一个:
top初始化为-1时,表示栈顶:
1.2.2 判空
bool StackEmpty(ST* ps)
{
assert(ps);
/*
if (ps->top == 0)
{
return true;
}
else
{
return false;
}
*/
return ps->top == 0;
}
1.2.3 入栈
void StackPush(ST* ps, STDataType x)
{
assert(ps);
// 检查空间
if (ps->top == ps->capacity) // 如果栈中元素个数==容量,需要扩容
{
// 如果容量为0,则新容量设为4;如果容量不为0,则扩容2倍
int newCapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;
// 扩容,开辟新空间
STDataType* tmp = (STDataType*)realloc(ps->a, newCapacity * sizeof(STDataType));
// 如果开辟空间失败,打印错误信息并退出程序
if (tmp == NULL)
{
perror("realloc failed");
exit(-1);
}
// 如果开辟空间成功,将临时指针变量的值赋给原指针变量
ps->a = tmp;
// 改变容量的值为新容量
ps->capacity = newCapacity;
}
ps->a[ps->top] = x;
ps->top++;
}
1.2.4 出栈
void StackPop(ST* ps)
{
assert(ps);
assert(!StackEmpty(ps));
ps->top--;
}
1.2.5 读栈顶元素
STDataType StackTop(ST* ps)
{
assert(ps);
assert(!StackEmpty(ps));
return ps->a[ps->top - 1];
}
1.2.6 栈中元素个数
int StackSize(ST* ps)
{
assert(ps);
return ps->top;
}
1.2.7 销毁
void StackDestroy(ST* ps)
{
assert(ps);
free(ps->a);
ps->a = NULL;
ps->top = ps->capacity = 0;
}
2. 队列
队列(queue)是一种特殊的线性表,只允许在一端进行插入数据操作,在另一端进行删除数据操作。进行插入操作的一端称为队尾(rear),进行删除操作的一端称为队头(front)。队列中的数据元素遵守先进先出(First In First Out, FIFO)的原则。
类似于线性表,队列也有顺序存储和链接存储两种存储结构。常用链式队列。
1. 顺序队列
自行规定队尾的位置,最后一个元素的位置或最后一个元素下一个的位置,一般规定队尾是最后一个元素下一个的位置。
2. 链式队列
2.1 链式队列
typedef int QDataType;
typedef struct QueueNode // 链式队列的结点
{
QDataType data;
struct QueueNode* next;
}QNode;
typedef struct Queue // 链式队列的结构
{
QNode* head;
QNode* tail;
int size;
}Queue;
2.2 链式队列的接口实现
2.2.1 初始化
void QueueInit(Queue* pq)
{
assert(pq);
pq->head = NULL;
pq->tail = NULL;
pq->size = 0;
}
2.2.2 判空
bool QueueEmpty(Queue* pq)
{
assert(pq);
return pq->head == NULL && pq->tail == NULL;
}
2.2.3 入队
void QueuePush(Queue* pq, QDataType x)
{
assert(pq);
QNode* newnode = (QNode*)malloc(sizeof(QNode));
if (newnode == NULL)
{
perror("malloc failed");
exit(-1);
}
newnode->data = x;
newnode->next = NULL;
if (pq->tail == NULL)
{
pq->head = pq->tail = newnode;
}
else
{
pq->tail->next = newnode;
pq->tail = newnode;
}
pq->size++;
}
2.2.4 出队
void QueuePop(Queue* pq)
{
assert(pq);
assert(!QueueEmpty(pq));
if (pq->head->next == NULL)
{
free(pq->head);
pq->head = pq->tail = NULL;
}
else
{
QNode* del = pq->head;
pq->head = pq->head->next;
free(del);
}
pq->size--;
}
2.2.5 读队头元素
QDataType QueueFront(Queue* pq)
{
assert(pq);
assert(!QueueEmpty(pq));
return pq->head->data;
}
2.2.6 读队尾元素
QDataType QueueBack(Queue* pq)
{
assert(pq);
assert(!QueueEmpty(pq));
return pq->tail->data;
}
2.2.7 队列中元素个数
int QueueSize(Queue* pq)
{
assert(pq);
return pq->size;
}
2.2.8 销毁
void QueueDestroy(Queue* pq)
{
assert(pq);
QNode* cur = pq->head;
while (cur)
{
QNode* del = cur;
cur = cur->next;
free(del);
// del = NULL;
// 出作用域后销毁,置不置空都可以
}
pq->head = pq->tail = NULL;
pq->size = 0;
}
2.3 循环队列
将顺序队列臆造为一个环状的空间,即把存储队列元素的表从逻辑上视为一个环,称为循环队列。
循环队列的一个好处是我们可以利用这个队列之前用过的空间。在一个普通队列里,一旦一个队列满了,我们就不能插入下一个元素,即使在队列前面仍有空间。但是使用循环队列,我们能使用这些空间去存储新的值。
- 初始时:Q.front=Q.rear=0
- 队首指针进1:Q.front=(Q.front+1)%Maxsize
- 队尾指针进1:Q.rear=(Q.rear+1)%MaxSize
- 队列长度:(Q.rear+MaxSize-Q.front)%MaxSize
为了能使用Q.front==Q.rear来区分是队空还是队满的情况,有三种处理方式:
1. 牺牲一个单元来区分队空和队满,入队时少用一个队列单元,这是一种较为普遍的做法。
队满:(Q.rear+1)%MaxSize==Q.front
队空:Q.front==Q.rear
2. 类型中增设表示元素个数的数据成员。
队满:Q.size==MaxSize
队空:Q.size==0
此时都有Q.front==Q.rear
3. 类型中增设tag数据成员,以区分是队满还是队空。
tag等于0时,若因删除导致Q.front==Q.rear,则为队空;tag等于1时,若因插入导致Q.front==Q.rear,则为队满。