简易栈结构的搭建

栈结构的搭建

栈的定义

  • 一种可以实现“先进后出”的存储结构
  • 类似于一个箱子
  • 分为静态栈和动态栈两种,区别在于其内核不一样,前者内核是数组,后者内核是链表,造成的结果就是前者的内存是固定的,后者的内存可以不断变化

图示

在这里插入图片描述

diy 过程

  1. 创建栈结构:
    • 栈的核心就在于两个指针的使用,pTop 以及 pBottom,所有的算法都基于这两个指针
    • 这两个指针实则就是两个链表节点的指针
\\ 定义链表节点结构体:
typedef struct Node{
	int data;
	struct Node * pNext;
}NODE, * PNODE;

\\ 定义栈结构体:
typedef struct stack{
	PNODE pTop;
	PNODE pBottom;
}STACK, * PSTACK;
  1. 栈的初始化:
    • 结合图示结构来看:
      在这里插入图片描述

    • 实则就是为 pTop 和 pBottom 分配动态内存,使他们都指向“头结点” pHead ,注意 pHead 不存放有效数据,其作用是为了方便操作

void init(PSTACK pS);

int main(void)
{
	PSTACK S;//定义一个栈结构指针
	init(S);

	return 0;
}

void init(PSTACK pS)
{
	PNODE pHead = (PNODE)malloc(sizeof(NODE));
	if ( pHead == NULL )
	{
		printf("动态内存申请失败!程序终止!\n");
		exit(-1);
	}
	else
	{
		pHead->pNext = NULL;
		//初始化 pHead 后没有其他栈节点,即此时还没有有效值
		pS->pTop = pHead; 
		pS->pBottom = pHead;
	}
	return;
}
  1. 压栈
    • 压栈:将数据填入栈结构的操作,其特点是将新的节点放在头结点的后面
    • 结合图示来看,其过程就是:创建一个新的节点,向其中填入有效值,将新节点的指针域指向pTop,再将 pTop 指向新的节点
      在这里插入图片描述
void push(PSTACK pS, int val);

int main(void)
{
	PSTACK S;//定义一个栈结构指针
	init(S);//初始化栈
	push(S,1);//压栈

	return 0;
}

void push(PSTACK pS, int val)
{
	//创建新节点
	PNODE pNew = (PNODE)malloc(sizeof(NODE));
	if ( pNew == NULL )
	{
		printf("动态内存申请失败!程序终止!\n");
		exit(-1);
	}
	//将新节点的指针域指向 pTop
	pNew->pNext = pS->pTop;
	//向新节点填入数据
	pNew->data = val;
	//将 pTop 指向新节点,相当于”箱子顶部“ 
	pS->pTop = pNew;
	return;
}
  1. 遍历栈
    • 从”箱子顶部“按顺序取值,直到”箱子底部“
    • 按照图示,其算法其实就是:创建一个指针p,将 pTop 的值赋给它,随后让它不断往pNext走,相当于它从箱子顶部出发,只要 (p != pBottom),就说明指针 p 还没有到达箱子底部,否则遍历结束
bool empty(PSTACK pS); //由于是遍历过程,所以需要判断是否是空栈
void traverse(PSTACK pS);

int main(void)
{
	PSTACK S;//定义一个栈结构指针
	init(S);//初始化栈
	push(S,1);//压栈
	traverse(S);//遍历

	return 0;
}

bool empty(PSTACK pS)
{
	//空栈的判断比较简单,直接看”箱子顶部“和”箱子底部“的值是不是相等
	if ( pS->pTop == pS->pBottom )
	{
		return true;
	}
	else
	{
		return false;
	}
}

void traverse(PSTACK pS)
{
	if ( empty(pS) )
	{
		printf("空栈!!\n");
	}
	else
	{
		PNODE p = pS->pTop;
		//创建一个指针
		//为什么不直接用 pTop ?
		//答:如果用 pTop,那么当 pTop 往 pNext 走的时候,栈的结构就被破坏了,再也找不到原来 pTop 的指针了
		while ( p != pS->pBottom )
		{
			printf("%d ", p->data);
			p = p->pNext;
		}
		printf("\n");
	}
	return;
}
  1. 出栈
    • 出栈:将数据从栈中取出,相当于从箱子顶部取东西,然后箱子顶部这个位置要往下移
    • 在图示中,它是与 push 相反的箭头(懒的另外画了 : )
      在这里插入图片描述
bool pop(PSTACK pS, int *pVal);
//pop是取值,所以需要返回bool值,空栈可没法取值

int main(void)
{
	PSTACK S;//定义一个栈结构指针
	init(S);//初始化栈
	push(S,1);//压栈
	traverse(S);//遍历
	if ( pop(S,&val) )
	{
		printf("出栈成功!出栈元素为 %d \n", val);
	}
	else
	{
		printf("出栈失败!\n");
	}

	return 0;
}

bool pop(PSTACK pS, int *pVal)
{
	if ( empty(pS) )
	{
		return false;
	}
	else
	{
		//创建一个指针,与遍历类似
		PNODE r = pS->pTop;
		//将取出的值返回给用户
		*pVal = r->data;
		//将 pTop 下移,与遍历不同,此时pTop的位置要改变了,原来的箱子顶可不是现在的箱子顶了,箱子变矮了:(
		pS->pTop = r->pNext;
		//释放原来的箱子顶
		free(r);
		//虽然释放了,但是 r 里还有值,所以需要赋个 NULL
		r = NULL; 
		return true;
	}
}
  1. 清栈
    • 顾名思义,清空这个栈,使得栈变为和初始化后的一样,重点在于需要释放原来栈里面的空间,不然遇到个大点的栈,内存可经不起折腾
    • 算法就是遍历和出栈的结合
void clear(PSTACK pS);

int main(void)
{
	PSTACK S;//定义一个栈结构指针
	init(S);//初始化栈
	push(S,1);//压栈
	traverse(S);//遍历
	if ( pop(S,&val) )
	{
		printf("出栈成功!出栈元素为 %d \n", val);
	}
	else
	{
		printf("出栈失败!\n");
	}
	clear(S);//清栈

	return 0;
}

void clear(PSTACK pS)
{
	if ( empty(pS) )
	//先判断是不是个空栈,空栈就不用清啦,加快效率
	{
		return;
	}
	else 
	{
		PNODE p = pS->pTop;
		//不定义 p 也行,只是为了方便理解
		//将这个算法理解成遍历和取值的结合就好,没取一个释放掉之后,将箱子顶往下移动一个单位 
		PNODE q;
		
		while ( p != pS->pBottom )
		{
			q = p->pNext;
			free(p);
			p = q;
		}
		pS->pTop = pS->pBottom;
		return;
	}
}

总结

  • 栈结构总体来说还是比较简单的,箱子的比喻其实还不是很恰当,当成一个积木塔更恰当,一块一块积木(栈节点)搭成一个塔
  • 其实内核就是链表啦,比链表简单
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值