一篇文章讲透链式栈!

目录

1.前言

2.链式栈的声明

3.栈的功能的具体实现

3.1栈的初始化

3.2判断栈是否为空

3.3返回栈顶元素

3.4返回栈的数据个数

3.5入栈

3.6出栈

3.7打印栈元素

3.8销毁栈

4.链式栈和顺序栈的区别

5.头文件


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);//销毁栈

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值