目录
1.前言
在上一篇文章中我们已经学习了顺序栈,这篇文章我们来学习链式栈。
两种栈的实现方式大同小异,但为了学习到更多的方法,在此篇文章中,我们使用另外一种push栈的方法。
2.链式栈的声明
链式栈就是用链表来实现的栈。
所以首先我们要定义一个链表结构 ,然后定义一个栈结构。
此时栈中的每个元素都是一个链表结点。
typedef int STDataType;
typedef struct SListNode
{
STDataType data;
struct SListNode* next;
}SListNode;
typedef struct Stack
{
SListNode* top;//栈顶位置
int size;//元素个数
}Stack;
另外,值得一提的是,链式栈的指针是从栈顶指向栈底的。
如下图所示:
3.栈的功能的具体实现
3.1栈的初始化
在顺序栈的初始化中,我们采用的方法是给数组malloc一定空间。在这里,由于我们不是使用数组保存数据的,因此这种方法便不奏效了。
我们直接初始化我们定义的栈结构体变量即可。
void StackInit(Stack* st)
{
assert(st);
st->top = NULL;
st->size = 0;
}
3.2判断栈是否为空
判断top是不是等于空即可
bool StackEmpty(Stack* st)
{
return (st->top == NULL);
}
3.3返回栈顶元素
返回栈顶元素,就是返回top位置结点的data。
STDataType StackTop(Stack* st)
{
assert(st);
assert(!StackEmpty(st));
return st->top->data;
}
3.4返回栈的数据个数
返回栈的数据个数,就是返回size
int StackSize(Stack* st)
{
return st->size;
}
3.5入栈
链式表的入栈,首先应该创建一个链表结点。
由于我们在入栈时需要用到这个链表结点,因此我们应该给创建链表结点的函数一个返回值。
创建完这个结点之后,我们要注意链表中的next结点和data结点需要更新。
SListNode* ListCreat(STDataType x)
{
SListNode* newnode = (SListNode*)malloc(sizeof(STDataType));
if (newnode == NULL)
{
perror("malloc fail");
return;
}
newnode->next = NULL;
newnode->data = x;
return newnode;
}
在我们入栈时,首先要判断这个栈是不是空栈,如果是空栈,我们就让创建的结点成为这个栈的第一个结点;不然的话,我们就需要让创建的结点成为新的栈顶,之后再更新栈顶了。
这里需要大家的是,链式栈是由栈顶指向栈尾的,因此我们需要先将newnode的next指针更新,然后再更新链表的top结点。
最后,我们需要将size++,否则返回数据个数时会出现错误。
void StackPush(Stack* st, STDataType x)
{
assert(st);
SListNode* newnode = ListCreat(x);
if (StackEmpty(st))
{
st->top = newnode;
}
else
{
newnode->next = st->top;
st->top = newnode;
}
st->size++;
}
3.6出栈
删除栈顶元素,我们首先应判断这个栈是不是一个空栈。
之后,我们就可以删除掉st->top的链表结点了。
删除掉这个结点之后,我们需要更新top和size。
但是在更新top时我们发现,我们会找不到新的top结点,
因此我们需要先定义一个指针找到top的next结点位置。
void StackPop(Stack* st)
{
assert(st);
assert(!StackEmpty(st));
SListNode* next = st->top->next;
free(st->top);
st->top = next;
st->size--;
}
3.7打印栈元素
打印栈,一个结点一个结点的打印即可。
void StackPrint(Stack* st)
{
assert(st);
assert(!StackEmpty(st));
for (SListNode* top = st->top; top != NULL; top = top->next)
{
printf("%d\n", top->data);
}
}
3.8销毁栈
销毁链式栈实际上就是销毁链表
void StackDestroy(Stack* st)
{
assert(st);
SListNode* top = st->top;
while (top != NULL)
{
SListNode* next = top->next;
free(top);
top = next;
}
st->size = 0;
}
4.链式栈和顺序栈的区别
时间效率:1.顺序表可以直接访问到下标,因此时间效率很高
2.如果将链表的头结点作为栈顶,时间效率也是不错的;但是如果让链表的尾结点作为栈顶,每次寻找栈顶都需要通过遍历寻找尾结点的方式才可行了。但由于两种方式每次都需要扩容操作,时间效率也就比较低了。
空间效率:1.顺序表扩容时可能导致空间的浪费。
2.链式表是按需扩容的,不会造成空间的浪费。但是由于链式表中需要额外的定义指针,因此链式表可能会更占用更多的空间。
5.头文件
#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include<stdbool.h>
typedef int STDataType;
typedef struct SListNode
{
STDataType data;
struct SListNode* next;
}SListNode;
typedef struct Stack
{
SListNode* top;//栈顶位置
int size;//元素个数
}Stack;
void StackInit(Stack* st);//初始化栈
bool StackEmpty(Stack* st);//判断栈是否为空
STDataType StackTop(Stack* st);//返回栈顶元素
int StackSize(Stack* st);//栈的大小
void StackPush(Stack* st, STDataType x);//入栈
void StackPop(Stack* st);//出栈
void StackPrint(Stack* st);//打印
void StackDestroy(Stack* st);//销毁栈