顺序栈与链式栈

目录

栈的定义与结构

栈的实现

顺序栈的实现

 初始化空栈

 销毁栈

压栈

获取栈顶元素

出栈

判断栈是否为空

获取栈中有效数据的个数

链式栈的实现

初始化空栈

 销毁栈

压栈

获取栈顶元素

出栈

判断栈是否为空

获取栈中有效数据的个数

顺序栈与链栈的对比


栈的定义与结构

栈是只允许在固定的一端进行插入元素或删除元素的线性表;进行数据插入和删除操作的一端称为栈顶(top), 另一端称为栈底(bottom);栈中的数据元素遵循后进先出(Last In First Out)的原则;

栈的基本操作:

压栈:栈中插入元素的操作叫做压栈/入栈,插入数据在栈顶插入;

出栈:栈中删除元素的操作叫做出栈,删除数据也在栈顶;

栈的实现

栈可以由顺序表和链表实现,使用顺序存储结构实现栈存储结构称为顺序栈或数组栈,使用链式结构实现栈存储结构称为链式栈;

  • 顺序栈的优点:

顺序栈的底层采用的是数组,当在栈顶插入或者删除元素时,可以通过下标进行插入或者删除,方便快捷,而且由于在内存中连续存储,缓存利用率高;

  • 链式栈的缺点:

链式栈当插入数据(尾插)可以通过定义尾指针实现插入,但是当删除数据(尾删)时,需要找到 尾结点的前一个结点,而单向链表无法实现,只能采用双向链表,代价过大;

以单链表头结点作为栈顶,此时插入数据(头插),删除数据(头删),这样相比上述方案方便快捷,但是由于是链式存储,缓存利用率低;

顺序栈的实现

采用动态顺序表实现栈,当进行插入元素的操作时,若空间不够使用,可以通过动态内存函数realloc()函数扩容;

typedef int STDataType;
typedef struct Stack
{
	STDataType* nums;
	int top;//记录栈顶元素在顺序栈中的位置;
	int capacity;//记录栈的大小;
}Stack;

 初始化空栈

栈顶元素的位置与栈底元素的位置相同,说明栈为空栈;空栈时,top的取值应该为多少?

若top=0表示空栈 ,当插入一个元素A0,则top=top+1即top=1;而top记录的是栈顶元素在顺序栈中的位置,此时top指向栈顶元素的下一个位置;若top不自增,top仍然为0,那么也将无法区分栈为空还是存在一个数据;

结论:top=0时,意味着top是栈顶元素的下一个位置;

若top=-1表示空栈,当插入1个元素A0,则top=top+1即top=0;由于top记录的是栈顶元素在顺序栈中的位置,逻辑自洽;

结论:top=-1时,意味着top是栈顶元素位置;

void InitStack(Stack* ps)
{
	assert(ps != NULL);
	//开辟一定量的空间
	ps->nums = (STDataType*)malloc(4*sizeof(STDataType));
	//判断空间开辟是否成功
	if (ps->nums == NULL)
	{
		perror("malloc failed:");
		exit(-1);
	}
	ps->top = -1;
	ps->capacity = 4;
}

 销毁栈

//栈的销毁
void DestroyStack(Stack* ps)
{
	assert(ps != NULL);

	free(ps->nums);
	ps->nums = NULL;

	ps->top = 0;
	ps->capacity = 0;
}

压栈

压栈时需要先检查栈的容量,若容量已满,需要进行扩容;

  • 栈满情况:(top=capacity-1)

//压栈
void StackPush(Stack* ps, STDataType x)
{
	assert(ps != NULL);

	//栈满扩容
	if (ps->top == ps->capacity - 1)
	{
		STDataType* tmp = (STDataType*)realloc(ps->nums, sizeof(STDataType)* 2 * (ps->capacity));
		if (tmp == NULL)
		{
			perror("realloc failed:");
			exit(-1);
		}
		ps->nums = tmp;
		ps->capacity = 2 * (ps->capacity);
	}
	//插入数据
	ps->nums[ps->top + 1]=x;
	ps->top++;
}

获取栈顶元素

//获取栈顶元素
STDataType StackTop(Stack* ps)
{
	assert(ps);

	//空栈
	assert(ps->top > (-1));

	return ps->nums[ps->top];
}

出栈

void StackPop(Stack* ps)
{
	assert(ps);
	//空栈时不可进行删除操作
	assert(ps->top > -1);

	ps->top = ps->top - 1;
}

判断栈是否为空

检测栈是否为空,栈为空返回true,否则返回false;

c语言默认没有布尔值(true/false),使用时需包含头文件# include <stdbool.h>

bool StackEmpty(Stack* ps)
{
	assert(ps);

	return (ps->top == -1);
}

获取栈中有效数据的个数

int StackSize(Stack* ps)
{
	assert(ps);
	return ps->top + 1;
}
  • 测试如上接口函数:
//遍历栈中元素时,通过一边获取栈顶元素,一边删除的操作实现
int main()
{
	Stack st;
	InitStack(&st);
	StackPush(&st, 10);
	StackPush(&st, 20);
	StackPush(&st, 30);
	StackPush(&st, 40);
	StackPush(&st, 50);
	while (!StackEmpty(&st))
	{
		printf("%d->", StackTop(&st));
		StackPop(&st);
	}
	printf("\n");
	DestroyStack(&st);
	return 0;
}

运行结果:

链式栈的实现

采用带哨兵位的单链表实现,见下图存储结构

//链栈数据结点类型LinkStack声明
typedef int LSDataType;
typedef struct LinkStack
{
	LSDataType data;//数据域
	struct LinkStack* next;//指针域
}LinkStack;

链栈的4要素:

1.栈空条件:phead->next=NULL

2.栈满条件:对于链栈而言,基本不存在栈满的情况,除非内存已经没有可以使用的空间,不需要考虑;

3.进栈操作:头插

4.出栈操作:头删

初始化空栈

建立一个空栈实际上是创建哨兵位的结点,并将其的next置为空指针;

//为将哨兵位结点带出,采用返回值的方案
LinkStack* InitStack(LinkStack* phead)
{
	LinkStack* phead = (LinkStack*)malloc(sizeof(LinkStack));
	if (phead == NULL)
	{
		perror("malloc fail:");
		exit(-1);
	}
	phead->next = NULL;
	return phead;
}

 销毁栈

释放所有动态开辟的结点空间;

//销毁栈
//先保存当前结点的下一个结点,然后释放当前结点;
void DestroyStack(LinkStack* phead)
{
	assert(phead);
	LinkStack* cur = phead;
	LinkStack* curnext = cur->next;
	while (curnext != NULL)
	{
		free(cur);
		cur = curnext;
		curnext = cur->next;
	}
	//此时cur指向尾结点
	free(cur);
	cur = NULL;
}

压栈

void StackPush(LinkStack* phead, LSDataType x)
{
	assert(phead);
	//开辟新结点
	LinkStack* newnode = malloc(sizeof(LinkStack));
	if (newnode==NULL)
	{
		perror("malloc failed:");
		exit(-1);
	}
	newnode->data = x;
	//头插=压栈
	newnode->next = phead->next;
	phead->next = newnode;
}

获取栈顶元素

在栈不为空的条件下,将哨兵位结点后的头结点的数据返回;

//获取栈顶元素
LSDataType LinkStTop(LinkStack* phead)
{
	assert(phead);
	//空栈
	assert(phead->next != NULL);

	return phead->next->data;
}

出栈

//出栈
void StackPop(LinkStack* phead)
{
	assert(phead);
    //空栈不可删除
	assert(phead->next != NULL);

	LinkStack* Second = phead->next->next;
	free(phead->next);//释放首结点
	phead->next = Second;
}

判断栈是否为空

检测栈是否为空,栈为空返回true,否则返回false;

//判断栈是否为空
bool StackEmpty(LinkStack* phead)
{
	assert(phead);

	return (phead->next == NULL);
}

获取栈中有效数据的个数

//获取栈中有效数据的个数
int StackSize(LinkStack* phead)
{
	assert(phead);
	int count = 0;//计数
	LinkStack* cur = phead->next;
	while (cur != NULL)
	{
		count++;
		cur = cur->next;
	}
	return count;
}

顺序栈与链栈的对比

顺序栈与链栈的时间复杂度均为O(1);

空间性能由于顺序栈是动态内存函数开辟的,势必会有一定空间的浪费,但是存取定位方便;链栈恰好弥补了上述缺点,但是链栈缓存利用率不高;

当线性表中元素个数变化大或者不知道元素个数有多少,采用链式栈;

当事先知道线性表的大致长度,采用顺序栈存储结构效率高;

 

 

 

 

 

 

  • 36
    点赞
  • 32
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 14
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 14
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小呆瓜历险记

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值