栈和队列----栈详解

文章详细介绍了栈的概念,包括栈的LIFO特性以及两种常见的实现方式:数组栈和链表栈。在数组栈中,讨论了如何通过动态数组实现栈的初始化、销毁、进栈、出栈等操作,并处理满栈时的扩容问题。链表栈部分则介绍了如何用单链表实现栈,同样涉及初始化、销毁、进栈和出栈,以及栈顶元素的获取。文章最后提供了代码示例和对栈的理解总结。
摘要由CSDN通过智能技术生成

栈和队列----栈详解

什么是栈?

栈的概念:

栈:一种特殊的线性表,其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端称为栈顶,另一端称为栈底。栈中的数据元素遵守后进先出LIFO(Last In First Out)的原则。

栈的特性:

压栈:栈的插入操作叫做进栈/压栈/入栈,入数据在栈顶

出栈:栈的删除操作叫做出栈。出数据也在栈顶

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pGYM9HM4-1683533995068)(C:\Users\l1777\AppData\Roaming\Typora\typora-user-images\image-20230508135044201.png)]

栈的不同实现形式:

  • 数组栈

​ 即用顺序表实现的栈,性能方面优于链表栈。由于栈的特性,从栈中存取数据只能在栈顶,所以就不存在在栈底或栈的中间位置存取。将顺序表的头部做栈底,尾部做栈顶,栈存取数据更加方便。

  • 链表栈

​ 用链表实现的栈,性能比数组栈低。若用尾做栈顶,尾插尾删,要设计成双向链表,否则删数据效率低,若用链表头做栈顶,头插头删,就可以设计成单链表

数组栈的实现和详解

思路:使用数组实现栈,定义一个可以动态存储的数组,可以定义一个整型变量来记录数组中的元素个数,也可以用它当作数组的下标,来实现对数据的快速存取。还需定义一个整型变量来记录数组的容量,来判断数组是否需要增容,最后还需要实现一个销毁函数,防止内存泄漏

栈的初始化和销毁

思路:将记录元素个数的变量,记录容量的变量,数组放在一个结构体类,方便对其操作。

typedef int DataType;

typedef struct Stack
{
	DataType* arr;
	int top;
	int capacity;
}ST;

//初始化
void Stackinit(ST* p)
{
	p->arr = NULL;
	p->capacity = p->top = 0;
}
//栈的销毁
void Stackdestory(ST*p)
{
	free(p->arr);
	p->capacity = p->top = 0;
    //可以不将p->arr置为NULL,函数调用完后,会自动销毁
}

进栈

思路:首先需判断是否已经满栈了,若满栈,需扩容,否则,直接插入。

//进栈
void Stackpush(ST*p,DataType x)
{
	assert(p);
	//当容量和数组元素个数相同时,即满栈了,需扩容。
	if (p->capacity==p->top)
	{	
        //三目运算符,若capacit为0,则赋值为4,若不为0,则直接扩容为原来的2倍。
		int newcapacity = p->capacity == 0 ? 4 : sizeof(DataType) * 2;
		p->arr = (DataType*)realloc(p->arr, sizeof(DataType) * newcapacity);
		assert(p->arr);
        //需将扩容后的值赋值给原来的capacity
		p->capacity = newcapacity;
	}
    //利用p->top作为下标直接插入
	p->arr[p->top++] = x;
}

出栈和获取栈顶元素

思路:进行这两个操作时,需先判断栈里面是否有数据,出栈只需将top的值减1就行,栈顶元素就是当前top的值减1作为下标,所对应的数组中的数据。

//出栈
void Stackpop(ST*p)
{
	assert(p);
	assert(Stackempty(p));
	p->top--;
}

//取栈顶元素
DataType Stacktop(ST*p)
{
	assert(p);
	assert(Stackempty(p));
    //数组的下标是从0开始的,top值为0时表示链表为空。
	return p->arr[p->top-1];
}

获取栈中元素个数和栈的判空

思路:top的数值就是元素个数,根据top是否为0可判断栈是否为空

//栈判空
bool Stackempty(ST*p)
{
	return p->top;
}

//取栈的元素个数
int Stacksize(ST*p)
{
	assert(p);
	return p->top;
}

打印栈中元素

思路:打印栈中元素不能直接从栈底遍历到栈顶,这样就不符合栈的性质了,只能先打印栈顶元素,然后再出栈,每次都打印栈顶元素,直到栈为空。

void test()
{
	ST s;
    //初始化栈
	Stackinit(&s);
    //进栈
	Stackpush(&s, 1);
	Stackpush(&s, 2);
	Stackpush(&s, 3);
	Stackpush(&s, 4);
	Stackpush(&s, 5);
	Stackpush(&s, 6);
    //打印栈中元素个数
    printf("%d ", Stacksize(&s));
	//打印栈中的元素
	while (Stackempty(&s))
	{
        //打印栈顶元素
		printf("%d ", Stacktop(&s));
        //出栈
		Stackpop(&s);
	}
    //销毁栈
	Stackdestory(&s);
}

链表栈的实现和详解

思路:跟数组栈的思路差不多,用头插和头删来进栈和出栈,需要定义一个结点来存储数据,还需定义个结构体来存放单链表的头指针,元素个数。容量不需要定义,单链表本来就是动态的。每次出栈和进栈都需要改变头指针的指向,需传二级指针。

栈的初始化和销毁

思路:初始化栈和数组栈的初始化差不多,栈的销毁需将结点和栈free,结点是动态内存开辟的空间,栈也是。防止内存泄漏。

typedef int DataType;

//定义存储数据的结点
typedef struct SNode
{
	DataType data;
	struct SNode* next;
}SN;

typedef struct SStackp
{	
    //头指针
	SN* ptop;
	int size;
}Sp;
//初始化栈
Sp* SStackinit()
{	
	Sp* p = (Sp*)malloc(sizeof(Sp));
	assert(p);
	p->ptop = NULL;
	p->size = 0;
	return p;
}
//销毁栈
void SStackdestory(Sp*p)
{
	p->size = 0;
	SN* cur = p->ptop;
	while (cur)
	{
		SN* next = cur->next;
		free(cur);
		cur = next;
	}
	free(p);
}

进栈

思路:个人认为不需要判断是否满栈,毕竟是链表栈,要判断就得加一个记录容量的变量,偷懒不写。直接创建一个新的结点,然后将数据放进去,再链接到栈中,再让头指针指向新结点。

//进栈
void SStackpush(Sp**p,DataType x)
{
	assert(p);
    //创建新结点
	SN* tem = (SN*)malloc(sizeof(SN));
	assert(tem);
	tem->data = x;
	tem->next = NULL;
    //栈的判空,若为空,直接将头指针指向第一个新结点
	if (!SStackempty(*p))
	{
		(*p)->ptop = tem;
        //size计数,方便判空和返回栈中元素数目
		(*p)->size++;
	}
	else
	{
		tem->next = (*p)->ptop;
		(*p)->ptop = tem;
        //size计数,方便判空和返回栈中元素数目
		(*p)->size++;
	}
}

出栈和获取栈顶元素

思路:出栈要记得移动头指针和释放出栈结点的内存,栈顶元素即头指针指向的结点存储的数据,需要判空。

//出栈
void SStackpop(Sp**p)
{
	assert(p);
	assert(SStackempty(*p));
    //记录被释放结点的下一个结点的位置
	SN* tem = (*p)->ptop->next;
	free((*p)->ptop);
	(*p)->ptop = tem;
	(*p)->size--;

}

//返回栈顶元素
DataType SStacktop(Sp*p)
{
	assert(SStackempty(p));
	return p->ptop->data;
}

获取栈中元素个数和栈的判空

思路:利用定义好的size很简单的搞定。

//栈判空
bool SStackempty(Sp*p)
{
	return p->size; 
}

//栈中元素数目
int SStacksize(Sp*p)
{
	return p->size;
}

打印栈中元素

思路:和数组栈的打印一样。

void test()
{
	Sp* s= SStackinit();
	SStackpush(&s, 1);
	SStackpush(&s, 2);
	SStackpush(&s, 3);
	SStackpush(&s, 4);
	SStackpush(&s, 5);
	SStackpush(&s, 6);
	while (SStackempty(s))
	{
		printf("%d ", SStacktop(s));
		SStackpop(&s);
	}


	SStackdestory(s);


}

总结

对栈这一块还是有点模糊,总觉得用链表实现栈那一块对一些变量的定义有点模糊,估计是对结构体不熟悉,也有可能单纯的不理解栈。栈的特性很重要,栈一般都是用数组来实现,比较方便。还得练练,回去看看基础!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值