文章目录
栈和队列是两种重要的线性结构。从数据结构角度看,栈和队列也是线性表,其特殊性在于栈和队列的基本操作是线性表操作的子集,它们是操作受限的线性表,因此,可称为限定性的数据结构。接下来我们将一起学习栈和队列的定义与实现。
栈
栈的概念及结构
栈是一种特殊的线性表,是限定仅在表尾进行插入或删除操作的线性表。进行数据插入和删除操作的一端称为栈顶,另一端称为栈底,不含元素的空表称为空栈。
假设栈S=(a1,a2,…,an),则称a1为栈底元素,an为栈顶元素。栈中元素按a1,a2,…,an的次序进栈,退栈的第一个元素应为栈顶元素。即栈中元素遵循后进先出LIFO(Last In First Out)的原则。
栈的实现
栈的实现一般可以使用数组或链表来实现,由于栈是在表尾实现插入删除,所以用数组操作更方便一些。因为数组在尾上插入代价更小。
下面我们将实现动态增长的数组栈。
定义顺序栈结构体
top用于记录栈顶元素的后一位置,此时top值等于栈中元素个数
typedef int STDataType;
typedef struct Stack
{
STDataType* a;
int top; // 栈顶
int capacity; // 容量
}Stack;
我们主要实现以下函数 :
(1)void StackInit(Stack* ps); // 初始化栈
(2)void StackPush(Stack* ps, STDataType data); // 入栈
(3)void StackPop(Stack* ps); // 出栈
(4)STDataType StackTop(Stack* ps); // 获取栈顶元素
(5)int StackSize(Stack* ps); // 获取栈中有效元素个数
(6)int StackEmpty(Stack* ps); // 检测栈是否为空
(7)void StackDestroy(Stack* ps); // 销毁栈
初始化栈
void StackInit(Stack* ps)
{
assert(ps);
ps->a = NULL;
ps->top = 0; //top指向栈顶元素的下一位置
ps->capacity = 0;
}
入栈
插入前先检测栈容量是否已满。如果满了就要扩容。top指向栈顶元素下一个,所以插入时先在top位置插入数据,然后再给top值加1.
void StackPush(Stack* ps, STDataType data)
{
assert(ps);
if (ps->top == ps->capacity)
{
int newcapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;
STDataType* tmp = (STDataType*)realloc(ps->a, sizeof(STDataType) * newcapacity);
if (tmp == NULL)
{
perror("realloc fail");
exit(-1);
}
ps->a = tmp;
ps->capacity = newcapacity;
}
ps->a[ps->top] = data;
ps->top++;
}
出栈
// 出栈
void StackPop(Stack* ps)
{
assert(ps);
assert(ps->top > 0);
ps->top--;
}
获取栈顶元素
}
// 获取栈顶元素
STDataType StackTop(Stack* ps)
{
assert(ps);
assert(ps->top > 0);
return ps->a[ps->top - 1];
}
获取栈中元素个数
int StackSize(Stack* ps)
{
assert(ps);
return ps->top;
}
判断栈是否为空
int StackEmpty(Stack* ps)
{
assert(ps);
if (ps->top == 0)
return 1;
return 0;
}
销毁栈
void StackDestroy(Stack* ps)
{
assert(ps);
free(ps->a);
ps->a = NULL;
ps->top = ps->capacity = 0;
}
队列
队列的概念及结构
队列是一种特殊的线性表,特殊之处在于它只允许在表的前端(front)进行删除操作,而在表的后端(rear)进行插入操作,和栈一样,队列是一种操作受限制的线性表。进行插入操作的端称为队尾,进行删除操作的端称为队头。
队列的数据元素又称为队列元素。在队列中插入一个队列元素称为入队,从队列中删除一个队列元素称为出队。因为队列只允许在一端插入,在另一端删除,所以只有最早进入队列的元素才能最先从队列中删除,故队列又称为先进先出(FIFO—first in first out)线性表。
队列的实现
同栈一样,队列也可以用数组和链表这两种结构来实现。但是队列是在对头出数据,用数组结构来实现的话出数据效率会很低,所以我们将一起学习用链表来实现队列。
我们主要实现以下函数:
(1)void QueueInit(Queue* q); // 初始化队列
(2)void QueuePush(Queueq,QDataType data);// 入队列
(3)void QueuePop(Queue q);// 队头出队列
(4)QDataType QueueFront(Queue* q);// 获取队列头部元素 (5)QDataType QueueBack(Queue* q);// 获取队列队尾元素
(6)int QueueSize(Queue* q);//获取队列中有效元素个数 (7)int QueueEmpty(Queue* q);// 检测队列是否为空
(8)void QueueDestroy(Queue* q);// 销毁队列
初始化
void QueueInit(Queue* q)
{
assert(q);
q->front = q->rear = NULL;
q->size = 0;
}
入队列
入队列分为两种情况,一是队列为空时,要注意同时修改头指针尾指针;而是队列不为空的情况,只需修改尾指针。不要忘记修改size的值。
void QueuePush(Queue* q, QDataType data)
{
assert(q);
QNode* newnode = (QNode*)malloc(sizeof(QNode));
if (newnode == NULL)
{
perror("malloc fail");
exit(-1);
}
newnode->data = data;
newnode->next = NULL;
if (q->rear == NULL)
{
q->front = q->rear = newnode;
}
else
{
q->rear->next = newnode;
q->rear = q->rear->next;
}
q->size++;
}
出队列
void QueuePop(Queue* q)
{
assert(q);
//空
assert(q->size);
QNode* tmp = q->front;
q->front = q->front->next;
free(tmp);
if (q->front == NULL)
{
q->rear = NULL;
}
q->size--;
}
获取队头元素
QDataType QueueFront(Queue* q)
{
assert(q);
assert(q->front);
return q->front->data;
}
获取队尾元素
QDataType QueueBack(Queue* q)
{
assert(q);
assert(q->rear);
return q->rear->data;
}
获取元素个数
int QueueSize(Queue* q)
{
assert(q);
return q->size;
}
判断是否为空
int QueueEmpty(Queue* q)
{
assert(q);
return q->front == NULL;
}
销毁队列
void QueueDestroy(Queue* q)
{
assert(q);
QNode* cur = q->front;
while (cur)
{
QNode* next = cur->next;
free(cur);
cur = next;
}
q->front = q->rear = NULL;
q->size = 0;
}