数据结构-栈

前言

本篇文章介绍栈的基础知识,以及栈的实现,包括顺序表示和链式表示。

一、栈及其抽象数据类型

1.1 栈的基本概念

基本定义:限定只能在表的一端进行插入和删除操作的线性表(只能在栈顶操作)
逻辑结构: 一种特殊线性表,一对一关系
存储结构:顺序存储(顺序栈)或链式存储(链栈),常用顺序存储
运算规则:只能在栈顶运算,且访问结点时依照后进先出(LIFO)的原则
栈的插入操作通常称为进栈或入栈(push)
栈的删除操作通常称为退栈或出栈(pop)

可以将线性表旋转,则得到栈的示意图(图1.1)
在这里插入图片描述

图1.1 栈的示意图

1.2 栈的抽象数据类型

栈的抽象数据类定义如下:

ADT Stack
{
	数据对象:D={ai|ai ∈ElemSet,i=1,2,3,...n,n >= 0}
	数据关系:S={<ai-1,ai>|ai-1,ai∈D,i=2,...,n}
			约定an为栈顶,a1为栈底
	基本操作:
	initStack(&S)
	操作结果:构造一个空栈S
	destoryStack(&S)
	初始条件:栈S已存在
	操作结果:栈S被销毁
	isEmptyStack(S)
	初始条件:栈S已存在
	操作结果:若栈S为空栈,返回TRUE,否则返回FALSE
	lengthStack(S)
	初始条件:栈S已存在
	操作结果:返回栈S的元素个数,即栈的长度
	getTop(S,&e)
	初始条件:栈S已存在且非空
	操作结果:用e返回S的栈顶元素
	clearStack(&S)
	初始条件:栈S已存在
	操作结果:将S清为空栈
	pushStack(&S,e)
	初始条件:栈S已存在
	操作结果:插入元素e为新的栈顶元素
	popStack(&S,&e)
	初始条件:栈S已存在且非空
	操作结果:删除S的栈顶元素,并用e返回被删除的元素	
}ADT Stack

二、栈的实现

2.1 顺序表示

2.1.1 结构定义

用顺序的方式表示栈时,要分配一块连续的存储区域存放栈中的元素,用一个元素类型的指针指向栈顶位置。
顺序栈的类型定义如下

//定义转态码
#define SUCCESS 1
#define ERROR 0
#define OVERFLOW -1
//数据元素类型定义
typedef char SElemType;

//定义栈的最大的数据元素个数
#define MAXSIZE 100
//顺序栈类型定义
struct SeqStack {
	SElemType* base;     //栈底指针
	SElemType* top;		//栈顶指针
	int stackSize;		//栈可用最大容量
};
//结点结构类型
typedef struct SeqStack SeqStack;
//定义栈的结点指针类型
typedef struct SeqStack* PSeqStack;

利用一组地址连续的存储单元依次存放自栈底到栈顶的数据元素。栈底一般在低地址
top指针,指示栈顶元素在顺序栈中的位置
base指针,指针栈底元素在顺序栈中的位置
用stackSize表示栈可使用的最大容量
为了方便操作,通常top指示真正的栈顶元素之上的位置,如图2.1
在这里插入图片描述

图2.1 顺序栈示意图

假设stackSize=4,那么顺序栈的动态示意图如下(图2.2)
在这里插入图片描述

图2.2 顺序栈的动态示意图

空栈:base == top 是空栈的判断
满栈:(top-base) == stackSize 是满栈的判断

其中top-base为指针操作,两个指针相减,得到数据元素个数。
满栈的处理方法:

  1. 报错,返回操作系统(常用)
  2. 分配更大的空间,将原内容移入新栈(不常用,因为当数据元素个数过多时,移动操作耗时)

使用数组作为顺序栈存储方式的特点:简单、方便、但易产生溢出(数组大小固定)
上溢(overflow)栈已满,执行进栈操作
下溢(underflow)栈已空,执行出栈操作
注意:上溢是一种错误,使问题无法进行处理;而下溢一般认为是一种结束条件,即问题处理介绍

2.1.2 基本操作的实现

  1. 顺序栈的初始化
    step1 申请一块连续的内存空间,大小为MAXSIZE*sizeof(SElemType)
    step2 将base和top指针指向这块空间的初始位置,stackSize设置为MAXSIZE

    //4.1 顺序栈的初始化(构造一个空栈)
    void initSeqStack(PSeqStack stack)
    {
    	assert(stack);
    	stack->base = (SElemType*)malloc(sizeof(SElemType) * MAXSIZE);
    	if (NULL == stack->base)
    	{
    		printf("malloc fail!\n");
    		exit(OVERFLOW);
    	}
    	stack->top = stack->base;  //表示空栈
    	stack->stackSize = MAXSIZE;
    }
    
  2. 顺序栈的销毁
    释放动态申请的内存空间

    //4.2 顺序栈的销毁
    void destorySeqStack(PSeqStack stack)
    {
    	assert(stack);
    	free(stack->base);
    	stack->base = NULL;
    	stack->top  = NULL;
    	stack->stackSize = 0;
    }
    
  3. 顺序栈入栈
    step1 判断栈是否为满栈
    step2 入栈

    //4.6 顺序栈入栈
    //栈满,则返回ERROR
    //栈不满,则将数据元素e插入到栈顶
    int pushSeqStack(PSeqStack stack, SElemType e)
    {
    	assert(stack);
    	if ((stack->top - stack->base) == stack->stackSize) //判断满栈
    		return ERROR;
    	*(stack->top++) = e;   // *(stack->top) = e; stack->top = stack->top+1;
    	return SUCCESS;
    }
    
  4. 顺序栈出栈
    step1 判断是否为空栈
    step2 出栈

    //4.7 顺序栈出栈
    //栈空,则返回ERROR
    //栈不空,则将栈顶元素删除
    int popSeqStack(PSeqStack stack, SElemType* e)
    {
    	assert(stack);
    	if (stack->top == stack->base)  //判断空栈
    		return ERROR;
    	*e = *(--stack->top);  //stack->top = stack-top -1; *(stack->top);
    	return SUCCESS;
    }
    
  5. 获取栈顶元素
    step1 判断是否为空栈
    step2 获取栈顶元素

    //4.8 获取栈顶元素
    //获取成功 返回SUCCESS
    //获取失败 返回ERROR
    int getSElem(PSeqStack stack,SElemType* e)
    {
    	assert(stack);
    	if (stack->top == stack->base) //判断是否为空栈
    		return ERROR;
    	*e = *(stack->top - 1);
    	return SUCCESS;
    }
    

2.2 链式表示

2.2.1 结构定义

实现栈的链式存储,使用不带头结点的单链表
链栈的类型定义如下:

//栈的链式存储
//不带头结点
#define SUCCESS 1
#define ERROR 0
#define OVERFLOW -1
//数据元素类型定义
typedef char SElemType;
struct StackNode {
	SElemType data;
	struct StackNode* next;
};
//结点结构类型
typedef struct StackNode StackNode;
//定义结点指针类型
typedef StackNode* PStackNode;
//定义链栈类型
typedef StackNode* LinkStack;

由于使用不带头结点的单链表,当头指针为NULL时,则表示空栈
链栈的示意图如下(图2.3)
在这里插入图片描述

图2.3 链栈示意图

2.2.2 基本操作的实现

  1. 链栈的初始化

    //5.1 链栈的初始化
    //不带头结点
    //头指针为空,则为空栈
    void initLinkStack(LinkStack* stack)
    {
    	assert(stack);
    	*stack = NULL;
    }
    
    
  2. 销毁链栈

    //5.2 链栈的销毁
    void destoryLinkStack(LinkStack* stack)
    {
    	assert(stack);
    	PStackNode p = *stack;
    	while (p)
    	{
    		PStackNode q = p->next;
    		free(p);
    		p = q;
    	}
    	*stack = NULL;
    }
    
    
  3. 链栈的入栈
    step1 申请结点空间
    step2 将结点插入栈顶,修改头指针的指向(移动栈顶指针)

    //5.4 链栈的入栈
    int pushLinkStack(LinkStack* stack,SElemType e)
    {
    	assert(stack);
    	//新建结点
    	PStackNode newNode = (PStackNode)malloc(sizeof(StackNode));
    	if (NULL == newNode)
    	{
    		printf("malloc fail!\n");
    		return ERROR;
    	}
    	newNode->data = e;
    	newNode->next = *stack;
    	*stack = newNode;
    	return SUCCESS;
    }
    
    
  4. 链栈的出栈
    step1 释放结点空间
    step2 修改头指针指向,删除结点

    //5.5 链栈的出栈
    int popLinkStack(LinkStack* stack, SElemType* e)
    {
    	assert(stack);
    	if (NULL == *stack) //判断是否为空栈
    		return ERROR;
    	*e = (*stack)->data;
    	PStackNode p = (*stack);
    	*stack = (*stack)->next;
    	free(p);
    	return SUCCESS;
    }
    
  5. 获取栈顶元素

    //5.6 获取栈顶元素
    int getSElemLinkStack(LinkStack stack, SElemType* e)
    {
    	if (NULL == stack) //判断是否为空栈
    		return ERROR;
    	*e = stack->data;
    	return SUCCESS;
    }
    

总结

完整代码:https://gitee.com/PYSpring/data-structure/tree/master

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值