C语言实现数据结构——顺序栈与链栈

本人数据结构编写规则

① 在函数中判断结构体指针是否为空时采用assert函数,而不是用if语句判断。
② 函数的命名规则遵循:操作+结构类型名 的规则。例如 InitSqList 与DestroySqList。
③ 严蔚敏老师一书中很多运用了C++的语法,而我们是用C语言来实现,因此编写规则与书上会有很多不同,但是思路是一样的。例如用malloc代替new,free代替delete,引用与指针的区别等。
④ 本篇判断栈满和栈空需要用到bool变量

正文

栈是限定只在表尾插入和删除的数据结构,满足LIFO( Last In First Out )的规则。栈是操作受限的线性表,其特殊性在于栈的基本操作可以看成是线性表操作的子集
在这里插入图片描述栈的应用场景有:数制的转换,括号匹配的检验,表达式求值等场景。
在程序设计中,如果需要按照保存数据时相反的顺序来使用数据则可以利用栈来实现。
例如回文链表问题等。
剑指 Offer II 027. 回文链表

栈的实现可以用数组实现顺序栈,或者采用链式结构实现链栈

顺序栈

顺序栈函数声明

void InitStack(Stack* s);					初始化
void DestoryStack(Stack* s);				摧毁
void PushStack(Stack* s,StackDataType i);	入栈
void PopStack(Stack* s);					出栈
bool IsEmptyStack(Stack* s);				判断栈空
bool IsFullStack(Stack* s);					判断栈满
StackDataType GetTopStack(Stack* s);		取栈顶元素

顺序栈结构体定义

typedef int StackDataType;
#define IncNumStack 5
typedef struct Stack
{

	StackDataType* data;
	int top;	//标识栈顶元素的下一个位置
	int size;
	int capacity;
}Stack;

top用于表示栈顶元素的下一个位置。当top = 0时,表示栈空,当top = capacity时,表示栈满。size表示当前栈存储的元素个数,capacity表示栈的总容量。

顺序栈初始化

void InitStack(Stack* s)
{
	assert(s);
	s->data = (StackDataType*)malloc(sizeof(IncNumStack));
	s->capacity = 0;
	s->size = 0;
	s->top = 0;//top是栈顶元素的下一个位置
}

定义一个IncNumStack用于每次容量不足的时候增容。

摧毁顺序栈

void DestoryStack(Stack* s)
{
	assert(s);
	free(s->data);
	s->data = NULL;
	s->capacity = 0;
	s->size = 0;
	s->top = 0;
}

判断顺序栈满和顺序栈空

bool IsEmptyStack(Stack* s)
{
	assert(s);
	return s->top == 0;
}
bool IsFullStack(Stack* s)
{
	assert(s);
	return s->top == s->capacity;
}

入顺序栈

void PushStack(Stack* s, StackDataType i)
{
	assert(s);
	if (IsFullStack(s))
	{
		int newcapacity = s->capacity == 0 ? IncNumStack : (s->capacity + IncNumStack);
		StackDataType* tmp = (StackDataType*)realloc(s->data, newcapacity * sizeof(StackDataType));
		if (tmp == NULL)
		{
			printf("扩容失败!\n");
			exit(-1);
		}
		else
		{
			s->data = tmp;
			s->capacity = newcapacity;
		}
		printf("\n扩容成功\n");
	}
	s->data[s->top] = i;
	s->top++;
	s->size++;
}

出顺序栈

void PopStack(Stack* s)
{
	assert(s);
	if (IsEmptyStack(s))
	{
		printf("\n栈为空!弹栈失败!\n");
		return;
	}
	s->top--;
	s->size--;
}

取顺序栈顶元素

StackDataType GetTopStack(Stack* s)
{
	assert(s);
	if(!IsEmptyStack(s))
	return s->data[s->top - 1];
	else
	{
		printf("\n栈为空!获取栈顶元素失败!\n");
		return -1;
	}
}

这里注意要取top-1处的元素。

到这里顺序栈就介绍结束了。但是顺序栈和顺序表一样,受到最大空间容量的限制,因此如果不能预先估计工作场景下栈的容量大小,我们同样可以采用链式方式实现链栈。

链栈

链栈函数声明

void InitLinkStack(LinkStack** ppls);				初始化
void DestroyLinkStack(LinkStack** ppls);			摧毁
void PushLinkStack(LinkStack** ppls, LSDataType i);	入栈
void PopLinkStack(LinkStack** ppls);				出栈
LSDataType GetTopLinkStack(LinkStack** ppls);		取栈顶元素
bool IsEmptyLinkStack(LinkStack** ppls);			判断是否为空
typedef int LSDataType;

typedef struct LS
{
	LSDataType data;
	struct LS* next;
}LinkStack;

在这里插入图片描述

因为链栈不会满,所以只有判空函数。这里注意我们要传二级指针。使用时

	LinkStack* ls;
	InitLinkStack(&ls);

要像链表那样,定义一个指针,再传这个指针的地址,才能实现某些函数的功能,例如初始化。为了保证接口形式一致,我们所有函数都选择传二级指针。链表一篇我们采用了相同的方法,详情请移步单链表篇:
C语言实现数据结构——单链表

链栈初始化

void InitLinkStack(LinkStack** ppls)
{
	assert(ppls);

	*ppls= (LinkStack*)malloc(sizeof(LinkStack));
	if (!*ppls)
	{
		printf("初始化失败\n");
		exit(-1);
	}
	(*ppls)->data = 0;
	(*ppls)->next = NULL;
}

摧毁链栈

void DestroyLinkStack(LinkStack** ppls)
{
	assert(ppls);

	LinkStack* cur = *ppls;
	while (cur)
	{
		LinkStack* next = cur->next;
		free(cur);
		cur = next;
	}
	ppls = NULL;
}

入链栈

void PushLinkStack(LinkStack** ppls,LSDataType i)
{
	assert(ppls);

	LinkStack* newnode = (LinkStack*)malloc(sizeof(LinkStack));
	if (newnode)
	{
		newnode->data = i;
		newnode->next = *ppls;
		*ppls = newnode;
	}
	else
	{
		printf("结点开辟失败\n");
		return;
	}
}

出链栈

void PopLinkStack(LinkStack** ppls)
{
	assert(ppls);
	if (IsEmptyLinkStack(ppls))
	{
		printf("栈空,出栈失败\n");
		return;
	}
	LinkStack* next = (*ppls)->next;
	free(*ppls);
	*ppls = next;
}

取链栈栈顶元素

LSDataType GetTopLinkStack(LinkStack** ppls)
{
	assert(ppls);
	if (IsEmptyLinkStack(ppls))
	{
		printf("栈空,取栈顶元素失败\n");
		return -1;
	}
	return (*ppls)->data;
}

判断链栈是否为空

bool IsEmptyLinkStack(LinkStack** ppls)
{
	assert(ppls);
	return (*ppls)->next == NULL;
}

结语

顺序栈是顺序表的子集,链式栈相似的是链表的子集,栈这种数据结构能解决很多特定场景下的问题,是一种很优秀的数据结构。

学完了顺序表和链表之后,栈这两种形式敲起来得心应手。

  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值