【链栈的实现】--------本质为不带头结点的 头插法建立起来的单链表

1.链栈的基本属性与特征:

链栈是运算受限的单链表,只能在链表头部进行操作
在这里插入图片描述

2.链栈的相关基础操作汇总

  • 初始化操作:操作结果:构造一个空栈 S。
InitStack(LinkStack *s)
  • 判定S是否为空栈:
    初始条件:栈S 已存在
    操作结果:若栈S为空栈,则返回TRUE,否则 FALSE.
StackEmpty(LinkStack s)
  • 求栈的长度
    初始条件:栈S 已存在。
    操作结果:返回S的元素个数,即栈的长度
StackLength(LinkStack s)
  • 栈销毁操作:
    初始条件:栈S 已存在。
    操作结果:将S清为空栈,且不使用该栈顶指针。
DestroyStack(LinkStack* s) 
  • 入栈操作(重点)
    初始条件:栈S已存在 (链栈一般不存在栈满的情况)
    操作结果:插入元素e为新的栈顶元素。
Push(LinkStack *s,SElemType e)
  • 出栈操作(重点)
    初始条件:栈S 已存在且非空:
    操作结果:删除S的栈顶元素a.,并用e返回其值。
Pop(LinkStack* s, SElemType* e)
  • 取栈顶元素
    初始条件:栈S 已存在且非空。
    操作结果:用e返回S的栈顶元素,并销毁该结点空间。
GetTop(LinkStack s, SElemType* e)
  • 从栈顶开始输出整栈
    初始条件:栈S 已存在且非空。
    操作结果:从栈顶输出栈中所有元素。
PrintStack(LinkStack s)

3.链栈的整栈创建(不带头结点,含头指针 即栈顶指针)

3.1链栈的结构定义:

//1.链栈的结构定义:
//实际上就是运算受限的单链表,只能在链表头部进行操作
typedef struct StackNode
{
	SElemType data;
	struct StackNode* next;
}StackNode,*LinkStack;
//StackNode:用来定义单个链栈的结点
//*LinkStack:用来定义整个链栈(即链栈的头指针或者栈顶指针)
  • 在结构定义中,一个链栈结点里包含了,指针域,和存放栈元素数据域这两个部分
  • 而别名StackNode用来定义单个链栈结点
    LinkStack 用来定义链栈的头指针(栈顶指针)

3.2链栈的初始化

注意:传入参数时,需传入二级指针,这样才能修改栈中的数据

bool InitStack(LinkStack *s)
3.2.1算法步骤:

(1)将传入的头指针(栈顶指针)初始化为空,表示空栈

//2.链栈的初始化
bool InitStack(LinkStack *s)
{
	//[1]将传入的头指针初始化为空,表示空栈
	*s = NULL;
	return true;
}

3.3判断链栈是否为空

注意:传入参数时,不需要传入二级指针,因为判空操作只需要访问栈顶指针,并不做修改

bool StackEmpty(LinkStack s)
3.3.1算法步骤:

(1)若传入的头指针(栈顶指针)为空,则返回true,表示空栈;否则栈非空

//3.判断链栈是否为空
bool StackEmpty(LinkStack s)
{
	//[1]若传入的头指针为空,则返回true,表示空栈
	if (s == NULL)
	{
		return true;
	}
	else
	{
		return false;
	}
}

3.4求链栈的长度

注意:传入参数时,不需要传入二级指针,因为求长度只需要返回计数器的结果,并不修改栈中数据

  • 该操作与求单链表长度的操作完全一致
int StackLength(LinkStack s)
3.4.1算法步骤:

(1)定义一个临时指针p,并让其指向首元结点
(2)初始化计数器为0,因为没有排除空链栈的可能
(3)循环遍历,统计结点数

//4.求链栈的长度
int StackLength(LinkStack s)
{
	//[1]定义一个临时指针p,并让其指向首元结点
	StackNode * p = s;
	//[2]初始化计数器为0,因为没有排除空链栈的可能
	int count = 0;

	//[3]循环遍历,统计结点数
	while (p != NULL)//若为空,直接返回i=0,不会进入循环
	{
		count++;//进入循环,证明存在首元结点,故先叠加
		p = p->next;//当p指向空,计数器已经将表长记录成功
	}

	return count;
}

3.5销毁链栈

注意:传入参数时,需要传入二级指针,因为销毁栈需要将栈顶指针置空

bool DestroyStack(LinkStack* s) 
3.5.算法步骤

(1)定义临时指针p,用来链栈中遍历所有结点
(2)定义临时指针temp,temp用来销毁空间
(3)循环销毁每个结点
(4)循环结束后,将头指针置为空,表示空栈(同时避免了悬挂指针问题)

//5.销毁链栈
//注意:这里传入二级指针,因为最终需要将头指针置为空
bool DestroyStack(LinkStack* s) 
{
	//[1]定义临时指针p,用来链栈中遍历所有结点
	StackNode* p = *s;

	//[2]定义临时指针temp,temp用来销毁空间
	StackNode* temp;

	//[3]循环销毁每个结点
	while (p != NULL) 
	{
		temp = p;
		p = p->next;
		free(temp);
	}

	//[4]循环结束后,将头指针置为空,表示空栈(同时避免了悬挂指针问题)
	*s = NULL;
	return true;
}

3.6链栈的入栈(核心1)

注意:传入参数时,需要传入二级指针,因为入栈操作需要 更改栈顶指针以及 添加栈顶元素

  • 本质为单链表的头部插入操作
  • 由于链栈一般不存在栈满的情况,故不需要特殊判断
bool Push(LinkStack *s,SElemType e)
3.6.1算法步骤:

(1)建立新结点,并初始化数据域
(2)入栈操作:
<1>修改新结点的指针域,指向当前栈顶元素
<2>修改栈顶指针,栈顶指针应指向新插入的结点

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

//6.链栈的入栈操作
//本质为单链表的头部插入操作
bool Push(LinkStack *s,SElemType e)
{
	//[1]建立新结点,并初始化数据域
	StackNode* p = (StackNode *)malloc(sizeof(StackNode));

	if (p == NULL)
	{
		printf("内存分配失败!\n");
		return false;
	}

	p->data = e;


	//[2]将新结点插入到链栈的头部
	p->next = (*s);//仅需要修改新结点的指针域即可
	*s = p;//注意修改栈顶指针,栈顶指针应指向新插入的结点
	
	return true;
}

3.7链栈的出栈(核心2)

注意:传入参数时,需要传入二级指针,因为出栈操作需要 更改栈顶指针以及 带出栈顶元素

bool Pop(SqStack* s, SElemType* e)
3.7.1算法步骤:

(1)判断是否栈空(即栈顶指针 是否指向NULL)
(2)出栈操作:
<1>先将当前栈顶指针指向的栈顶元素 赋值给e
<2>再定义并初始化临时指针p指向当前栈顶结点
<3>修改栈顶指针指向出栈后的栈顶结点
<4>销毁临时指针p指向的结点空间

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

//7.链栈的出栈操作
//本质为单链表的头部删除操作
bool Pop(LinkStack* s, SElemType* e)
{
	//[1]判断链栈是否为空,为空则返回false
	if ((*s) == NULL)
	{
		printf("空栈!\n");
		return false;
	}

	//[2]保存栈顶结点的数据
	*e = (*s)->data;

	//[3]定义并初始化临时指针p指向当前栈顶结点
	StackNode* p=(*s);

	//[4]修改栈顶指针指向出栈后的栈顶结点
	(*s) = (*s)->next;

	//[5]销毁临时指针p指向的结点空间
	free(p);

	return true;
}

3.8取栈顶元素

注意:传入参数时,不需要传入二级指针,因为只取元素,不修改栈的数据

bool GetTop(LinkStack s, SElemType* e)
3.8.1算法步骤:

(1)判断链栈是否为空,为空则返回false
(2)若链栈不为空,则直接返回栈顶元素
(栈顶元素为链表的首元结点,即链表的头指针指向的结点)

在这里插入图片描述

//8.获取栈顶元素
bool GetTop(LinkStack s, SElemType* e)
{
	//[1]判断链栈是否为空,为空则返回false
	if (s == NULL)
	{
		printf("空栈!\n");
		return false;
	}

	//[2]若链栈不为空,则直接返回栈顶元素
	//注意:栈顶元素为链表的首元结点,即链表的头指针指向的结点
	*e = s->data;

	return true;
}

3.9输出链栈中的所有元素(从顺序栈顶开始输出)

bool PrintStack(LinkStack s)
3.9.1算法步骤:

(1)定义临时指针p,用来链栈中遍历所有结点
(2)判断链栈是否为空,为空则返回false
(3)p从栈顶开始,向栈底移动并依次输出栈中的元素

//9.输出链栈中的所有元素(从栈顶开始输出)
bool PrintStack(LinkStack s)
{
	//[1]定义临时指针p,用来链栈中遍历所有结点
	StackNode* p = s;

	//[2]判断链栈是否为空,为空则返回false
	if (p == NULL)
	{
		printf("空栈!\n");
		return false;
	}

	//[2]循环输出每个结点
	while (p != NULL)
	{
		printf("%d ", p->data);
		p = p->next;
	}

	printf("\n");
	return true;
}

4.所有操作如下

//创建一个不带头结点的链栈(头指针就是栈顶指针)
//本质为限制了插入与删除操作的(头插法建立起来的 单链表)
#include<stdio.h>
#include<stdlib.h>


#define bool char
#define true 1
#define false 0

typedef int SElemType;//栈中数据元素的类型定义     

//1.链栈的结构定义:
//实际上就是运算受限的单链表,只能在链表头部进行操作
typedef struct StackNode
{
	SElemType data;
	struct StackNode* next;
}StackNode,*LinkStack;
//StackNode:用来定义单个链栈的结点
//*LinkStack:用来定义整个链栈(即链栈的头指针或者栈顶指针)



//2.链栈的初始化
bool InitStack(LinkStack *s)
{
	//[1]将传入的头指针初始化为空,表示空栈
	*s = NULL;
	return true;
}


//3.判断链栈是否为空
bool StackEmpty(LinkStack s)
{
	//[1]若传入的头指针为空,则返回true,表示空栈
	if (s == NULL)
	{
		return true;
	}
	else
	{
		return false;
	}
}



//4.求链栈的长度
int StackLength(LinkStack s)
{
	//[1]定义一个临时指针p,并让其指向首元结点
	StackNode * p = s;
	//[2]初始化计数器为0,因为没有排除空链栈的可能
	int count = 0;

	//[3]循环遍历,统计结点数
	while (p != NULL)//若为空,直接返回i=0,不会进入循环
	{
		count++;//进入循环,证明存在首元结点,故先叠加
		p = p->next;//当p指向空,计数器已经将表长记录成功
	}

	return count;
}

//5.销毁链栈
//注意:这里传入二级指针,因为最终需要将头指针置为空
bool DestroyStack(LinkStack* s) 
{
	//[1]定义临时指针p,用来链栈中遍历所有结点
	StackNode* p = *s;

	//[2]定义临时指针temp,temp用来销毁空间
	StackNode* temp;

	//[3]循环销毁每个结点
	while (p != NULL) 
	{
		temp = p;
		p = p->next;
		free(temp);
	}

	//[4]循环结束后,将头指针置为空,表示空栈(同时避免了悬挂指针问题)
	*s = NULL;
	return true;
}


//6.链栈的入栈操作
//本质为单链表的头部插入操作
bool Push(LinkStack *s,SElemType e)
{
	//[1]建立新结点,并初始化数据域
	StackNode* p = (StackNode *)malloc(sizeof(StackNode));

	if (p == NULL)
	{
		printf("内存分配失败!\n");
		return false;
	}

	p->data = e;


	//[2]将新结点插入到链栈的头部
	p->next = (*s);//仅需要修改新结点的指针域即可
	*s = p;//注意修改栈顶指针,栈顶指针应指向新插入的结点
	
	return true;
}

//7.链栈的出栈操作
//本质为单链表的头部删除操作
bool Pop(LinkStack* s, SElemType* e)
{
	//[1]判断链栈是否为空,为空则返回false
	if ((*s) == NULL)
	{
		printf("空栈!\n");
		return false;
	}

	//[2]保存栈顶结点的数据
	*e = (*s)->data;

	//[3]定义并初始化临时指针p指向当前栈顶结点
	StackNode* p=(*s);

	//[4]修改栈顶指针指向出栈后的栈顶结点
	(*s) = (*s)->next;

	//[5]销毁临时指针p指向的结点空间
	free(p);

	return true;
}


//8.获取栈顶元素
bool GetTop(LinkStack s, SElemType* e)
{
	//[1]判断链栈是否为空,为空则返回false
	if (s == NULL)
	{
		printf("空栈!\n");
		return false;
	}

	//[2]若链栈不为空,则直接返回栈顶元素
	//注意:栈顶元素为链表的首元结点,即链表的头指针指向的结点
	*e = s->data;

	return true;
}

//9.输出链栈中的所有元素(从栈顶开始输出)
bool PrintStack(LinkStack s)
{
	//[1]定义临时指针p,用来链栈中遍历所有结点
	StackNode* p = s;

	//[2]判断链栈是否为空,为空则返回false
	if (p == NULL)
	{
		printf("空栈!\n");
		return false;
	}

	//[2]循环输出每个结点
	while (p != NULL)
	{
		printf("%d ", p->data);
		p = p->next;
	}

	printf("\n");
	return true;
}

int main()
{
	LinkStack s1;
	InitStack(&s1);  // 初始化栈

	// 入栈
	Push(&s1, 10);
	Push(&s1, 20);
	Push(&s1, 30);
	Push(&s1, 31);
	Push(&s1, 32);

	printf("当前栈的长度: %d\n", StackLength(s1));
	printf("\n");

	// 获取栈顶元素
	SElemType ee;
	GetTop(s1, &ee);
	printf("当前栈顶元素为:%d\n", ee);

	printf("\n");
	printf("从当前栈顶元素向下输出:\n");
	
	PrintStack(s1);

	// 出栈
	printf("\n");
	SElemType e[10];
	for (int i = 0; i < 3; i++)
	{
		Pop(&s1, &e[i]);
		printf("出栈元素: %d\n", e[i]);
	}

	printf("\n");
	printf("从当前栈顶元素向下输出:\n");
	PrintStack(s1);

	// 销毁栈
	DestroyStack(&s1);

	printf("栈是否为空: \n");
	if (StackEmpty(s1))
	{
		printf("栈为空!\n");
	}
	return 0;
}

在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值