24考研数据结构复习(4)——栈的顺序表示及其实现

本系列文章是笔者复习自用,参考教材为严蔚敏编著的《数据结构(C语言版)》,如能对大家有所帮助实属荣幸

本篇的内容主要是栈的线性表示和实现,主要包括栈的定义和常用函数的实现。

4.1 抽象类型栈的类型定义

栈和队列是两种很重要的线性结构,是两种特殊的线性表,本文先介绍一下栈。

栈是只能在表尾进行插入和删除操作的线性表,对栈来说,第一个元素所在的地方称为栈顶,最后一个元素所在的地方成为栈顶。正是因为栈只能在栈顶进行插入删除操作的特性,使得后进入栈的数据必须先被删除,新数据被删除后旧数据才能被删除,对于栈来说,插入操作被称为入栈,删除操作被称为出栈。总的来说,栈的特点就是后进先出

可以将栈看作一个只有一个开口的管道,想要取出底部的元素,只能先取出距离开口处最近的元素。

下面是栈的一些概念。

栈顶(top):按照线性表来说属于表尾,是可以进行插入和删除的一端。

栈底(base):按照线性表来说属于表头,是不能进行插入删除操作的一端。

入栈(push):相当于线性表的插入操作,将栈顶后移一位。

出栈(pop):相当于线性表的删除操作,栈顶前移一位,并不是真的删去。

4.2 栈的表示和实现

1.栈的定义

通常来说,顺序栈的定义中有三个成员,分别是代表指向栈底的指针top,指向栈顶的指针base和记录已分配给栈的空间大小的stacksize。base始终不改变,入栈出栈时top改变,重新分配空间时stacksize改变。

typedef struct{
	ElemType *base;//栈底指针,初始化之前为NULL
	ElemType *top;//栈顶指针,该指针指向栈中最顶端元素的后一位空间,这空间不属于栈
	int stacksize;//已分配的储存空间,不等于栈实际占用的
}SqStack;

2.栈的初始化

在栈的初始化过程中,需要给栈分配一个初始空间,base指向这段空间的开头,由于是空表,top和base指向同一段空间,并且,stacksize的大小等于初始空间的大小。

Status InitStack(SqStack &S){
	S.base = (ElemType *)malloc(InitSize*sizeof(ElemType));//为栈申请初始大小的空间
	if (!S.base) exit(ERROR);//创建失败则退出
	S.top = S.base;
	S.stacksize = InitSize;
	return OK;
}

3.获取栈顶元素

获得栈顶第一个元素的值,不过,top指针一般指向位于栈中最新元素的后一位,所指的空间不属于栈,所以获取的栈顶元素应该是top-1所指空间的值。

ElemType GetTop(SqStack S){
	if (S.base == S.top) exit(ERROR);//如果栈底等于栈顶,则栈空
	return *(S.top - 1);
}

4.进栈

进栈操作指的是让一个新的元素进入栈内,使其成为栈顶。

算法思想为,首先判断栈是否已满,若满了,给栈重新分配空间,确保栈内有足够空间之后,将值赋给top所指空间,后移top使其指向最后一个元素的后一位。

Status PushStack(SqStack &S, ElemType e){
	//进栈首先判断栈是否满,满则需要重新分配空间
	if (S.top - S.base >= S.stacksize){
		S.base = (ElemType *)realloc(S.base,(S.stacksize + IncreaseSize)*sizeof(ElemType));
		if (!S.base) exit(ERROR);
		S.top = S.base + S.stacksize; //由于realloc返回的很可能是一个新的地址,所以top指针需要进行迁移
		S.stacksize += IncreaseSize;
	}
	*S.top++ = e;//先给之前top的空间赋值,再top往后推
	return OK;
}

其中,S.top=S.base+S.stacksize语句值得注意。ralloc函数在执行时会判断原位置后是否有足够的连续空间,若有则直接使用后面的空间,若没有,则会在一个新的地址创建空间,然后将原数据复制过来。如果缺少此语句,top指针会停留在原地址,就有可能出错。所以,这个语句必不可少。 

5.入栈

入栈操作指的是将栈顶的一个元素删去。

算法思想为,首先判断栈是否为空,若为空则退出,否则返回top所指向空间的值,并使top前移一位。

ElemType PopStack(SqStack &S){
	//出栈首先判断是否为空,为空则不进行操作
	if (S.base == S.top) exit(ERROR);
	ElemType e = *(S.top - 1);
	S.top--;
	return e;
}

4.3 栈的相关应用

由于后进先出特性,栈这一数据类型有很多独特的应用领域,下面举出一些。

1.数制转换

众所周知,十进制数和其他进制数的转换是计算机实现计算的基本问题,下面是其中一种解决方法。

N=(N div d)×d+N mod d(其中。N为十进制数,d为转换后的进制,div为整除计算,mod为求余计算)

例如,1348转换为8进制,计算过程如下:

答案为八进制下的2504 。

这个计算过程与栈的关系为,进行求余计算之后,将结果存入栈中,直到计算结束,使结果逐个出栈。根据后进先出,若求余的结果依次为4、0、5、2,则最终输出的结果为2504,完成进制转换。代码如下:

void conversion(int N,int d){
	int e;
	SqStack S;
	InitStack(S);
	while (N)//num等于0时停止
	{
		PushStack(S, N % d);//将求余所得的数存入栈
		N=N/8;
	}
	while (S.base!=S.top)
	{
		e = PopStack(S);
		printf("%d", e);
	}
}

 2.括号匹配的验证

假设表达式中允许包含两种括号,方括号和圆括号,其套嵌的顺序随意,但必须匹配,即[(])这样的不匹配,写程序判断一个表达式是否合规。

其算法思想是,遍历该字符串,如果遇到左括号,则直接入栈;如果遇到右括号,则判断其是否与栈顶符号匹配,若匹配则出栈,否则什么都不做。如果遍历完成后,栈为空,则该表达式合法。

void main(){
	SqStack S;
	char c[10]="[[[123]]]";
	char *p=c;
	InitStack(S);
	while(*p!=NULL)
	{
		if (*p == '(' || *p == '[')
			PushStack(S, *p);
		else if (*p == ']' && GetTop(S) == '[')
			PopStack(S);
		else if (*p == ')' && GetTop(S) == '(')
			PopStack(S);
		Print(S);
		printf("\n");
		p++;
	}
	if (S.base == S.top)
		printf("该表达式合法");
	else
		printf("该表达式不合法");
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值