栈和队列
栈
分析
栈的数据是栈顶进,栈顶出。
我们用数组和链表都可以,但是链表因为尾插和尾删没有数组方便,所以我们用数组。
例子:如果进去的顺序是1234,出来的顺序就是4321。
我们可以用一个数组来储存数据,然后再定义一个指针指向栈顶的数据,方便出栈和入栈。
typedef int SD;//随时更改数据类型
typedef struct stack
{
SD* a;//数组
int top;//栈顶的后一个位置
int capacity;//容量
}ST;
这里用指针定义的数组,后面我们可以用扩容增加容量,这样就变成了一个数组。
初始化与销毁栈
一定要初始化空间,不然容易造成野指针的问题。
初始化
void StackInit(ST* ps)//初始化
{
assert(ps);
ps->a = NULL;
ps->capacity = 0;
ps->top = 0;
}
初始化就不开辟空间了。
销毁栈
这里和链表不一样,比较方便,释放掉起始地址就好了。
void StackDestroy(ST* ps)//销毁栈
{
assert(ps);
free(ps->a);
ps->a = NULL;
ps->capacity = 0;
ps->top = 0;
}
出栈入栈与判断栈为空
入栈
void StackPush(ST* ps, SD x)//入栈
{
assert(ps);
//扩容
if (ps->capacity == ps->top)//判断是否需要扩容
{
int count = (ps->capacity == 0 ? ps->capacity = 4 : ps->capacity * 2);//第一次扩容4个,之后的扩容的容量二倍增加
SD* y = (SD*)realloc(ps->a, count * sizeof(SD));
if (y == NULL)
{
perror("realloc fail");
exit(-1);
}
ps->a = y;
ps->capacity = count;
}
ps->a[ps->top] = x;//赋值
ps->top++;
}
例:
此时的top是4.
判断栈是否为空
bool StackEmpty(ST* ps)//判断
{
assert(ps);
return ps->top == 0;//如果为空就返回1,不为空返回0
}
出栈
void StackPop(ST* ps)//出栈
{
assert(ps);
assert(!StackEmpty(ps));
ps->top--;
}
出栈就很简单了,因为top是末尾的数据,如果想再入栈,入栈的地方是top指向的地方,会覆盖掉原来的数据。
获取栈顶元素
SD StackTop(ST* ps)//获取栈顶元素
{
assert(ps);
assert(!StackEmpty(ps));
return ps->a[ps->top - 1];
}
获取栈中有效元素个数
top也就等于栈中数据的数量。
int StackSize(ST* ps)//获取栈中有效的元素
{
assert(ps);
return ps->top;
}
队列
分析
队列是从队头出,队尾入,数组就没有链表好用了,所以我们用单向链表。
结点
typedef int SD;
typedef struct QListNode
{
struct QListNode* next;
SD data;
}QL;
因为我们需要的是头删尾插,所以不仅仅需要头结点,还需要一个尾结点,这样更方便操作。
typedef struct Queue
{
QL* head;//头结点,指向队列的队头
QL* tail;//尾结点,指向队列的队尾
int siz;//计算有多少个结点
}Qu;
初始化与销毁队列
初始化
头结点和尾结点的指向空即可,siz初始化为0用来记录结点数量。
void QueueInit(Qu* q)//初始化
{
assert(q);
q->head = q->tail = NULL;
q->siz = 0;
}
销毁队列
void QueueDestroy(Qu* q)//销毁队列
{
assert(q);
QL* cur = q->head;//这里要注意数据类型
while (cur)
{
QL* del = cur -> next;
free(cur);
cur = del;
}
q->head = q->tail = NULL;
q->siz = 0;
}
入列,出列与判断队列是否为空
入列
这里需要考虑队列是否为空的尾插。
void QueuePush(Qu* q, SD x)//入队
{
assert(q);
QL* w = (QL*)malloc(sizeof(QL));//新开辟结点
if (w == NULL)
{
perror("malloc tail");
exit(-1);
}
else
{
w->data = x;
w->next = NULL;
}
if (q->head == NULL)//如果队列为空
{
q->head = q->tail = w;
}
else//如果队列不为空
{
q->tail->next = w;
q->tail = w;
}
q->siz++;
}
判断
不为空返回0,为空返回非零。
bool QueueEmpty(Qu* q)//判断
{
assert(q);
return q->head == NULL;
}
出列
这里就是典型的头删了,只不过需要注意的是,tail是不会在这个函数中移动的,所以当释放掉tail指向的结点时,要把tail置为空,不然会成为野指针。
void QueuePop(Qu* q)//出队
{
assert(q);
assert(!QueueEmpty(q));
if (q->head == NULL)//防止tail成为野指针
{
free(q->head);
q->head = q->tail = NULL;
}
QL* cur = q->head;
q->head = q->head->next;
free(cur);
cur = NULL;
q->siz--;
}
获取队列头部,尾部元素
这里就体现了head和tail的好处。
头部
SD QueueFront(Qu* q)//获取队头元素
{
assert(q);
assert(!QueueEmpty(q));
return q->head->data;
}
尾部
SD QueueBack(Qu* q)//获取队尾元素
{
assert(q);
assert(!QueueEmpty(q));
return q->tail->data;
}
获取队列中有效元素个数
直接返回之前用于记录数量的siz就可以了。
int QueueSize(Qu* q)//获取队列中有效元素个数
{
assert(q);
return q->siz;
}
本文含有隐藏内容,请 开通VIP 后查看