(八) 栈 ---- 栈是个好东西,为什么呢?

栈是个好东西,为什么呢?因为,站着说话不腰疼!!!
栈(stack)是限定仅在表尾进行插入或者删除操作的线性表。因此,对栈来说,表尾端有其特殊的含义,称为栈顶(top),相应的,表头端称为栈底(bottom)。不含元素的空包称为空栈。栈的特点为尾端数据后进先出(Last In First Out,简称LIFO结构)。
在这里插入图片描述
栈的存储类型有两种,一种是线性存储,即开辟一段连续的存储空间,与数组类似,利用top,bottom指针进行操作,一般情况下这种方式使用的是最多的。另外一种是栈的链式存储,这种存储方式用的不是很多。本篇文章采用的是第一种存储方式。下面用代码具体实现栈的操作。

1.结构
线性存储的栈的结构和以前的链式表的结构不怎么一样,因为他开辟了一段连续的空间,在整个程序中设定另一个结构仅仅记录栈顶、栈底以及栈的容量。

typedef  int SElemType;
typedef  int Status;

typedef struct
{
	SElemType* top;   // 栈顶指针 
	SElemType* base;  // 栈底指针
	int statacsize;   // 栈的容量
} SqStack;

#define STACK_INIT_SIZE 5   //初始化栈的容量
#define STACK_INCE_SIZE 2   //每次栈增量的大小
#define OK 1
#define ERROR 0

2.重点

  • 一定要理解线性存储栈在内存中的形态,这样有助于理解栈操作的机制,最好画图理解,下面上图。
    在这里插入图片描述
    栈初始化后,开辟了5个int类型的空间,每个int类型占4个字节,共计开辟了20个字节的空间,起始地址为0x13b81e0,结束地址为0x13b81f3,s->top、s->base都指向栈底。中间压入了一个数据后,栈顶指针对应的单元赋值1,然后栈顶指针加1。最右边的栈满的状态,大家一定要理解,当栈压入了5个元素后,栈顶指针指向0x13b81f4,这个时候,栈顶指针已经超出了所分配的单元,这种情况下会出问题吗?这个问题我也想了很久,我个人认为不会出现问题,在后边的代码中可以看到,栈顶指针指向0x13b81f4仅仅用到了探测边界,并没有取值,而且这段地址是程序在堆区分配的地址,不是代码区,所以不会出现问题。
  • 栈满的情况下要扩容,就要用到realloc函数,这个函数会出现两种情况,一种是在原有的内存分配空间后边刚好有大于增量大小的内存,就直接在原先的基础上向后扩,简单的说是栈底指针不变。第二种情况是原有的内存分配空间后边没有适合增量大小的内存,系统会在堆区重新开辟一段空间,空间长度为原先已经分配过的内存长度 加上 增量部分,分配好后这段内存的首地址已经发生变化了,必须重新指定s->base的地址,然后在根据扩容前的容量修改s->top指针,然后再修改扩容后的容量。
    在这里插入图片描述
    通过上图可以看到,小容量的栈在扩容的情况下,s->base的地址基本不会改变,通过对压栈、出栈的地址变化可以更加清楚看清内存的变化。

3.代码
sqstack.h

#pragma once

typedef  int SElemType;
typedef  int Status;

typedef struct
{
	SElemType* top;   // 栈顶指针 
	SElemType* base;  // 栈底指针
	int statacsize;
} SqStack;

#define STACK_INIT_SIZE 5
#define STACK_INCE_SIZE 2
#define OK 1
#define ERROR 0

//初始化栈
Status Init_Stack(SqStack* s);

//压栈、入栈
Status Push(SqStack* s, SElemType e);

//弹栈、出栈
Status Pop(SqStack* s, SElemType* e);

//销毁栈
Status DestoryStack(SqStack* s);

//清空栈
Status ClearStack(SqStack* s);

//栈为空返回1,否则返回0
Status IsStackEmpty(SqStack* s);

//栈的长度
int StackLength(SqStack* s);

//获得最顶端元素
Status GetTopData(SqStack* s, SElemType* e);

sqstack.c

#pragma once
#include"sqstack.h"
#include<stdio.h>
#include<stdlib.h>


extern SqStack SStack;

//初始化栈
Status Init_Stack(SqStack* s)
{
	s->base = (SElemType*)malloc(sizeof(SElemType) * STACK_INIT_SIZE);
	printf("分配空间大小:%d\n", sizeof(SElemType) * STACK_INIT_SIZE);
	if (!s->base)
	{
		printf("内存分配失败。。\n");
		return ERROR;
	}
	s->top = s->base;
	s->statacsize = STACK_INIT_SIZE;

	printf("s.base=0x%x,size = %d\n",s->base,sizeof(s->base));

	return OK;
}

//压栈、入栈
Status Push(SqStack* s, SElemType e)
{
	int n = 0;
	n = s->top - s->base;
	if (n >= s->statacsize)
	{
		//重新分配连续内存
		s->base = (SElemType*)realloc(s->base, (s->statacsize + STACK_INCE_SIZE)*sizeof(SElemType));
		if (!s->base)
		{
			printf("内存分配失败。。\n");
			return ERROR;
		}
		s->top = s->base + s->statacsize;
		s->statacsize = s->statacsize + STACK_INCE_SIZE;
	}
	*s->top++ = e;
	return OK;
}

//弹栈、出栈
Status Pop(SqStack* s, SElemType *e)
{
	if (s->top == s->base)
	{
		printf("栈空了,没东西了,弹。。弹个毛线。\n");
		return ERROR;
	}
	*e = *(--s->top);
	return OK;
}

//销毁栈
Status DestoryStack(SqStack* s)
{
	if (!s->base) return ERROR;
	free(s->base);
	s->base = NULL;
	s->top = s->base;
	s->statacsize = 0;
}

//清空栈
Status ClearStack(SqStack* s)
{
	if (!s->base) return ERROR;
	s->top = s->base;
	return OK;
}

//栈为空返回1,否则返回0
Status IsStackEmpty(SqStack* s)
{
	if (s->base == s->top)
	{
		return OK;
	}
	else
	{
		return ERROR;
	}
}

//栈的长度
int StackLength(SqStack* s)
{
	if (!s->base) return -1;
	return s->top - s->base;
}

//获得最顶端元素
Status GetTopData(SqStack* s, SElemType* e)
{
	if (!s->base) return ERROR;
	*e = *(s->top - 1);
	return OK;
}

测试代码

#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#include"sqstack.h"


SqStack SStack ;

int main()
{
	SElemType n;

	SStack.base = NULL;
	SStack.top = SStack.base;
	SStack.statacsize = 0;

	Init_Stack(&SStack);
	printf("初始化栈:s.base:\t0x%x,s.top:\t0x%x,s.size:\t%d\n",SStack.base,SStack.top,SStack.statacsize);
	Push(&SStack, 1);
	printf("    压栈:s.base:\t0x%x,s.top:\t0x%x,s.size:\t%d\n", SStack.base, SStack.top, SStack.statacsize);
	Push(&SStack, 2);
	printf("    压栈:s.base:\t0x%x,s.top:\t0x%x,s.size:\t%d\n", SStack.base, SStack.top, SStack.statacsize);
	Push(&SStack, 3);
	printf("    压栈:s.base:\t0x%x,s.top:\t0x%x,s.size:\t%d\n", SStack.base, SStack.top, SStack.statacsize);
	Push(&SStack, 4);
	printf("    压栈:s.base:\t0x%x,s.top:\t0x%x,s.size:\t%d\n", SStack.base, SStack.top, SStack.statacsize);
	Push(&SStack, 5);
	printf("    压栈:s.base:\t0x%x,s.top:\t0x%x,s.size:\t%d\n", SStack.base, SStack.top, SStack.statacsize);
	Push(&SStack, 6);
	printf("    压栈:s.base:\t0x%x,s.top:\t0x%x,s.size:\t%d\n", SStack.base, SStack.top, SStack.statacsize);
	
	printf("    ---------------------------------------------------------------------\n");

	printf("    出栈:\n");
	while (SStack.base != SStack.top)
	{
		Pop(&SStack, &n);
		printf("    出栈:s.base:\t0x%x,s.top:\t0x%x,s.size:\t%d,  \t%d\n", SStack.base, SStack.top, SStack.statacsize,n);
	}
	printf("\n");

	Pop(&SStack, &n);

	DestoryStack(&SStack);
	system("pause");
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值