数据结构:栈

目录

一 栈的基本概念

1.栈的定义:

二 栈的顺序存储结构

1.栈的存储结构:

2.栈的所有接口:

3.栈的初始化:

4.栈顶指针初值为-1或者0的区别:

5.栈的销毁:

6.入栈:

7.出栈:

8.获取栈顶元素:

9.获取有效数据个数:

10:判断栈是否为空:

11.打印栈:

三 栈的链式存储结构

1.栈的存储结构:

3.栈的所有接口:

4.栈的初始化:

5.栈的销毁:

6.入栈:

7.出栈:

8.获取栈顶元素:

9.获取有效数据个数:

10.判断栈是否为空:

四 顺序存储和链式存储实现栈的优缺点比较

1.顺序存储(基于数组的栈):

2.链式存储(基于链表的栈):

五 总结


一 栈的基本概念

1.栈的定义:

栈(Stack)是只允许在一端进行插入或删除操作的线性表。首先栈是一种线性表,但限定这种线性表只能在某一端进行插入和删除操作。
栈顶(Top) 线性表允许进行插入删除的那一端
栈底(Bottom) 固定的,不允许进行插入和删除的另一端。
空栈 不含任何元素。

二 栈的顺序存储结构

1.栈的存储结构:

typedef int STDataType;
typedef struct Stack
{
	STDataType* a;
	int top;
	int capacity;
}ST;

2.栈的所有接口:

//栈的初始化、销毁
void STInit(ST* ps);
void STDestroy(ST* ps);

//栈的入栈、出栈
void STPush(ST* ps, STDataType x);
void STPop(ST* ps);

//获取栈顶元素、有效数据个数
STDataType STTop(ST* ps);
int STSize(ST* ps);

//判断栈是否为空
bool STEmpty(ST* ps);

3.栈的初始化:

void STInit(ST* ps)
{
	ps->a = NULL;
	ps->capacity = 0;
    ps->top = -1;//ps->top = 0;
}

 输出:

4.栈顶指针初值为-1或者0的区别:

当栈顶指针初始值为-1时:

进栈时:

因为此时栈为空且栈顶指针指向-1所以我们先让栈顶指针先自增然后把值赋给栈顶元素。

ps->a[++ps->top] = x;

出栈时:

先取栈顶元素然后再自减。

x = ps->arr[ps->top--];

当栈顶指针初始值为0时:

进栈时:

因为此时栈为空且栈顶指针指向0所以我们先值赋给栈顶元素然后再让栈顶指针先自增。

ps->a[ps->top++] = x;

出栈时:

先自减然后再取栈顶元素。

x = ps->arr[--ps->top];

其实谁便用哪个都行看自己喜好,它们无非就是先自增和后自增,自减也和自增一样。

5.栈的销毁:

void STDestroy(ST* ps)
{
	assert(ps);//判断ps指针是否为空
	free(ps->a);
	ps->a = NULL;
	ps->capacity = 0;
	ps->top = -1;
}

输出:

6.入栈:

void STPush(ST* ps, STDataType x)
{
	assert(ps);
	     //栈扩容
		if (ps->capacity == ps->top + 1)//因为ps->capacity为0而ps->top为-1,为了能让栈扩容 
                                        //那就必须要相等
		{
			STDataType NewCapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;
			ST* tmp = (ST*)realloc(ps->a, NewCapacity * sizeof(STDataType));
			if (tmp == NULL)
			{
				perror("malloc error");
				exit(-1);
			}
			ps->a = tmp;
			ps->capacity = NewCapacity;
		}
		ps->a[++ps->top] = x;//ps->top == 0 : ps->a[ps->top++] = x;
}

 输出:

7.出栈:

void STPop(ST* ps)
{
	assert(ps);
	if (ps->top == -1)//判断栈是否为空
	{
		exit(-1);
	}
	ps->top--;
}

输出:

8.获取栈顶元素:

STDataType STTop(ST* ps)
{
	assert(ps);
	if (ps->top == -1)
	{
		exit(-1);
	}
	return ps->a[ps->top];
}

输出:

9.获取有效数据个数:

int STSize(ST* ps)
{
	assert(ps);
	ps->top++;
	return ps->top;
}

输出:

10:判断栈是否为空:

bool STEmpty(ST* ps)
{
	assert(ps);
	return ps->top == -1;//当栈为空那么返回的就是真反之则为假
}

输出:

11.打印栈:

三 栈的链式存储结构

1.栈的存储结构:

//链表存储结构
typedef int STTDataType;
typedef struct STist
{
	struct STist* next;//指针域
	STTDataType data;//数据域
}ST;
//栈存储结构
typedef struct LinkedStack 
{
	ST* top;//栈顶指针
	int size;//有效数据
} LS;

3.栈的所有接口:

//初始化、销毁
//初始化、销毁
void Init_Stack(LS* Stack);
void Destroy_Stack(ST* Stack);

//入栈、出栈
void push_Stack(ST* Stack, STTDataType x);
void pop_Stack(ST* Stack);

//检查栈是否为空
STTDataType STEmpty(ST* Stack);

//获取栈顶元素、有效数据个数
STTDataType STTop(ST* Stack);
STTDataType STSize(ST* Stack);

4.栈的初始化:

void Init_Stack(LS* Stack)
{
	assert(Stack);
	Stack->top = NULL;
	Stack->size = 0;
}

输出:

5.栈的销毁:

void Destroy_Stack(LS* Stack)
{
	assert(Stack);
	Stack->size = 0;
	ST* tmp = Stack->top;
	while (tmp != NULL)
	{
		ST* next = tmp->next;
		free(tmp);
		tmp = next;
	}
}

6.入栈:

void push_Stack(LS* Stack, STTDataType x)
{
	assert(Stack);
	ST* Newnode = (ST*)malloc(sizeof(ST));
	if (Newnode == NULL)
	{
		perror("malloc error");
		exit(-1);
	}
	Newnode->data = x;
    //首先就是让第一个节点指向为空(把它当成尾)然后再让新节点指向之前的节点以此类推
	Newnode->next = Stack->top;
	Stack->top = Newnode;//让Stack->top永远为第一个节点
	Stack->size++;//有效数据自增
}

输出:

7.出栈:

void pop_Stack(LS* Stack)
{
	assert(Stack);
	if (Stack->top == NULL)//判断栈顶指针是否为空
	{
		exit(-1);
	}
	ST* Ptmp = (Stack->top)->next;
	free(Stack->top);
	Stack->top = Ptmp;
	Ptmp = Ptmp->next;
}

输出:

8.获取栈顶元素:

STTDataType STTop(LS* Stack)
{
	return Stack->top->data;
}

输出:

9.获取有效数据个数:

STTDataType STSize(LS* Stack)
{
	return Stack->size;
}

输出:

10.判断栈是否为空:

STTDataType IsEmpty(LS* Stack)
{
	if (Stack->top == NULL)//判断栈顶指针是否为空
	{
		return 0;
	}
	return 1;
}

输出:

四 顺序存储和链式存储实现栈的优缺点比较

顺序存储和链式存储都可用于实现栈,各有优缺点。具体选择哪种方式取决于应用的特定需求和优先级。

1.顺序存储(基于数组的栈):

优点:

  1. 2.高效的访问和随机检索: 顺序存储允许使用索引直接访问堆栈中的任何元素,从而实现高效的随机检索元素。

  2. 可预测的内存使用: 基于数组的栈具有预定义的大小,使得估计和管理内存使用更加容易。

  3. 缓存友好型访问: 数组中的连续内存位置往往可以更好地利用 CPU 缓存。

缺点:

  1. 有限的动态调整大小: 基于数组的栈具有固定大小,在运行过程中调整大小可能效率低下,并且需要复制元素。

  2. 潜在的内存浪费: 如果栈大小估计过高,则可能由于未使用的分配空间而浪费内存。如果估计过低,则栈可能溢出,导致错误。

  3. 中间插入/删除代价高昂: 在基于数组的栈中插入或删除中间元素可能代价高昂,因为它需要移动元素以保持顺序。

2.链式存储(基于链表的栈):

优点:

  1. 动态调整大小: 链表可以根据需要动态增长或缩小,因此适用于堆栈大小不可预测的情况。

  2. 中间插入/删除高效: 在链表栈中插入或删除中间元素相对便宜,因为它只需要修改指针引用。

  3. 灵活的内存分配: 链表不需要连续的内存分配,因此适用于内存受限的环境。

缺点:

  1. 较慢的访问和检索: 访问链表堆栈中的特定元素需要从头开始遍历链表,与顺序存储相比,随机检索速度较慢。

  2. 指针开销: 链表会引入额外的内存开销来存储节点之间的指针,这在内存关键型应用程序中可能是一个因素。

  3. 缓存效率低: 链表的非连续内存布局可能导致缓存利用率低于顺序存储。

五 总结

  • 建议使用顺序存储(基于数组的栈)

    1. 随机访问和检索速度至关重要。
    2. 可预测和可控的内存使用很重要。
    3. 堆栈大小相对稳定,可以事先估计。
  • 建议使用链式存储(基于链表的栈)

    1. 需要动态的堆栈大小来处理不可预测的数据量。
    2. 预计频繁地在堆栈中间插入/删除元素。
    3. 内存限制是一个问题,需要灵活的内存分配。

对于实现栈的顺序存储和链式存储之间的选择,取决于应用的特定需求和优先级。仔细权衡访问速度、内存使用和动态功能之间的利弊,以做出最佳决策。

  • 57
    点赞
  • 52
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 30
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

POL.free

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

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

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

打赏作者

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

抵扣说明:

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

余额充值