数据结构(C语言版)朱昌杰

  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
依次输入表达式每一个字符,若是左括号,将其入栈保存;若是右括号,则出栈左 括号,并检验与其是否匹配。循环执行,直到表达式输入结束。
在检验过程中,若遇到以下几种情况之一,就可以得出括号不匹配的结论。 当遇到某一个右括号时,栈已空,说明到目前为止,右括号多于左括号; 从栈中弹出的左括号与当前检验的右括号类型不同,说明出现了括号交叉情况; 算术表达式输入完毕,但栈中还有没有匹配的左括号,说明左括号多于右括号。
图 3.3 栈顶指针 top 与栈中数据元素的关系
2. 顺序栈的算法实现 (1)初始化栈(置栈空)
初始化栈主要是分配存储空间,并将栈顶指针置为−1。
int InitStack(SqStack *s) { //创建一个空栈由指针 s 指向 if((s=(SqStack*)malloc(sizeof(SqStack)))==NULL) return ERROR; s->top= -1; return OK; }
46
数据结构(C 语言版)
后进先出或先进后出的线性表。 基于栈的特性,栈的抽象数据类型定义如下:
ADT Stack { 数据对象 D:D={ ai|ai∈ElemSet, i=1,2,…,n, n≥0 } 数据关系 R:R={ <ai-1, ai>|ai-1, ai∈D, i=2,…,n } 约定 an 端为栈顶,a1 端为栈底。 基本操作 P: InitStack(S) 操作结果:构造一个空栈 S StackEmpty(S) 初始条件:栈 S 已存在 操作结果:若栈 S 为空栈,则返回 TRUE,否则返回 FALSE StackFull(S) 初始条件:栈 S 已存在 操作结果:若栈 S 满,则返回 TRUE,否则返回 FALSE GetTop(S, e) 初始条件:栈 S 已存在 操作结果:若栈 S 非空,用 e 返回 S 的栈顶元素 Push(S, e) 初始条件:栈 S 已存在 操作结果:若栈 S 不满,插入元素 e 为新的栈顶元素 Pop(S, e) 初始条件:栈 S 已存在且非空 操作结果:若栈 S 非空,删除 S 的栈顶元素,并用 e 返回其值
第3章 栈 和 队 列
47
图 3.3 所示为 MAXSIZE 为 6 的顺序栈中数据元素和栈顶指针 top 的变化情况。其中, 图 3.3(a)表示空栈;图 3.3(b)表示元素 a 进栈,top 指示的是当前栈顶的位置;图 3.3(c)表 示 b、c、d、e、f 依次进栈后栈满的情况;元素 f、e、d 依次出栈后的情况如图 3.3(d)所示, top 为当前栈顶元素 c 的位置;图 3.3(e)表示元素 c、b、a 继续出栈又重新回到空栈状态。
#define MAXSIZE 100 typedef struct { Elemtype data[MAXSIZE];
int top; }SqStack;
在顺序栈中,用于指示栈顶的当前位置的 top 是整型,它的实质是栈顶元素在数组 中的下标。栈顶指针 top 直接反映出栈的当前状态:空栈时,栈顶指针 top 为−1;栈满时, 栈顶指针 top 为 MAXSIZE−1;入栈时,栈顶指针 top 加 1;出栈时,栈顶指针 top 减 1。
(1)当 N≠0,则重复执行①和②:
① 将 N % r 进栈 s;
② N = N/r。
(2)将栈 s 的内容依次出栈,算法结束。
具体实现如算法来自百度文库3.1 所示。
typedef int Elemtype void conversion(int N, int r) { SqStack s;
SqStack *ps=&s; Elemtype x; InitStack(ps); while(N)
// top 加 1,栈顶位置上移 //数据 e 存入当前栈顶
(5)出栈 出栈时应首先判断栈是否为空,若栈不为空,则取出栈顶元素,将栈顶指针 top 下
移,再返回栈顶元素。
int Pop(SqStack *s,Elemtype *e) { if(StackEmpty(s)) return ERROR; *e=s->data[s->top]; s->top--; return OK; }
图 3.1 操作仅在表一端进行
3.1.1 栈的定义
栈是限制在表的一端进行插入和删除操作的线性表。能够进行操作的一端是浮动端, 称为栈顶,通常用一个“栈顶指针”指示,它的位置会随着操作而发生变化;与此相对, 表的另一端是固定端,称为栈底。如同线性表可以为空表一样,当栈中没有元素时称为 空栈。往栈中插入元素的操作称为入栈,删除栈中元素的操作称为出栈。如图 3.2 表示 了一个栈。
(4)进栈 进栈时应首先判栈满,若栈不满则将栈顶指针 top 上移,存入元素。
int Push(SqStack *s, Elemtype e) {
if(StackFull(s)) return ERROR;
//将元素 e 插入到栈中,作为新栈顶 //栈满
48
数据结构(C 语言版)
s->top++; s->data[s->top]=e; return OK; }
50
数据结构(C 语言版)
3.2.1 数制转换
数制转换问题是指对于输入的任意一个非负十进制整数 N,打印输出与其等值的 r
进制数。一般利用辗转相除法解决这个问题,以 N=1348,r=8 为例,其转换方法如下:
N
N / 8 (整除)
N % 8 (求余)
1348
168
4

168
21
0
21
2
5
2
0
2

} ADT Stack
3.1.2 栈的顺序存储结构
采用顺序存储结构的栈称为顺序栈,它需要一段连续的存储空间来存储栈中元素。 1. 顺序栈的类型定义 类似于顺序表的定义,通常用预先设定的足够大的一维数组来存放数据。由于栈顶 会随着插入和删除操作而发生变化,用整型变量 top 作为栈顶指针指示栈顶的当前位置。 栈底位置固定不变,可以设置在数组的任意端,一般将数组的下标端作为栈底。 顺序栈的类型描述如下:
(2)判栈空
int StackEmpty(LinkStack top) //判栈为空栈时返回值为真,反之为假 { return(top==NULL? TRUE:FALSE); }
图 3.4 链栈示意图
(3)进栈
void Push(LinkStack top, Elemtype e) { //将元素 e 进链栈,即在表头插入新的结点
StackNode *s; s=(StackNode*)malloc(sizeof(StackNode)); s->data=e; s->next=top; top=s; }
(4)出栈
int Pop(LinkStack top,Elemtype *e) { //若栈不为空将栈顶元素出栈,即为删除表头结点
图 3.2 栈的示意图
根据栈的运算特性,所有操作都只在栈顶进行。如果将数据元素按照 a1, a2, a3, …, an 的顺序依次入栈,则此时 a1 在栈底,an 在栈顶,如图 3.2 所示。当要取出数据时,则必 须按 an, an−1, …, a1 的顺序进行。由此可知,an 作为栈顶元素总是最后入栈的,而最先出 栈;a1 作为栈底元素总是最先入栈的,而最后出栈。栈按照这种后进先出(LIFO,Last In First Out)或者先进后出(FILO,First In Last Out)的原则来组织数据的,因此,它也被称为
具体实现如算法 3.2 所示。
typedef char Elemtype
int bracketmatching( )
{
SqStack s;
SqStack *ps=&s;
Elemtype ch;
InitStack(ps);
(2)判栈空
int StackEmpty(SqStack *s) //判栈为空栈时返回值为真,反之为假 { return(s->top==-1? TRUE:FALSE);}
(3)判栈满
int StackFull(SqStack *s) //判栈为满栈时返回值为真,反之为假 { return(s->top==MAXSIZE-1?TRUE:FALSE);}
(6)取栈顶元素
//若栈不为空,则删除栈顶元素 //栈空 //取出数据放入 e 所指单元中 // top 减 1,栈顶位置下移
int GetTop(SqStack *s,Elemtype *e) { if(StackEmpty(s)) return ERROR; *e=s->data[s->top]; return OK; }
结果为(1348)10=(2504)8。 对其计算过程进行分析可以发现,八进制的各位数实际上就是每次运算所得的余数
值,它们产生的顺序是由低位到高位的,这恰好与输出顺序相反。因此,在转换过程中
每得到一位余数就将其进栈保存,转换完毕后再依次出栈,即可得出结果。
将十进制数 N 转换为 r 进制数,算法思路如下:
//若栈不为空,则取栈顶元素 //栈空 //取出数据,top 不变
由于栈的运算特殊性,顺序栈中进栈和出栈操作并不存在移动数据的问题,因而效 率较高。但顺序栈需要预先估计准确的存储空间大小,需要预先分配一个较大空间,这 有可能造成存储空间的浪费。
3.1.3 栈的链式存储结构
若是栈中元素的数目变化范围较大或不清楚栈元素的数目,就应该考虑使用链式存 储结构。用链式存储结构表示的栈称为“链栈”。
{ Push(ps, N%r); N=N/r;
} while(!StackEmpty(ps))
{ Pop(ps,&x); printf("%d",x);
} }
//定义一个顺序栈 s //余数入栈
算法 3.1
3.2.2 括号匹配的检验
假设在一个算术表达式中,可以包含三种括号:圆括号“(”和“)”,方括号“[” 和“]”和花括号“{”和“}”,并且这三种括号可以按任意的次序嵌套使用,例如某一
第3章 栈 和 队 列
51
个算术表达式中的括号使用括号情况为“…[…{…}…[…]…]…[…]…(…)…”。括号匹 配的检验主要就是判别给定表达式中所含括号是否正确配对。
算术表达式中各种括号的使用规则为:出现左括号,必有相应的右括号与之匹配, 并且每对括号之间可以嵌套,但不能出现交叉情况。根据该规则可知,当前最晚出现的 左括号总是最先与随后出现的右括号进行匹配,这一规律与栈的“后进先出”运算特性 吻合,因此可以利用栈来解决这一问题,算法思路如下:
StackNode *p; if(StackEmpty(top)) return ERROR; *e=top->data; p=top; top=top->next; free(p); retrun OK; }
//栈空
3.2 栈的应用举例
根据栈的运算特点,在很多实际问题中都利用栈作为一个辅助的数据结构来进行求 解,下面通过几个例子进行说明。
第3章 栈 和 队 列
49
2. 链栈的算法实现 链栈的本质是简化的单链表,top 作为栈顶指针始终指向链表首结点。进栈操作就是 在链表表头插入一个新的结点,出栈操作就是删除当前的表头 结点并释放空间。 (1)初始化栈(置空栈)
LinkStack InitStack() //空栈的 top 指针为 NULL { return NULL; }
第3章 栈 和 队 列
本章知识要点: 栈和队列的基本概念 栈和队列的顺序存储结构和链式存储结构 栈和队列的应用
3.1 栈
栈是一种特殊的线性表,它的逻辑结构和存储结构与线性表相同,其特殊性体现在 “运算受限”,即无论往表中插入元素还是删除表中已有元素,都被限制在线性表的一端
进行。一般将表尾作为操作端,如图 3.1 所示。
1. 链栈的类型定义 链栈通常用一个无头结点的单链表表示,其结点结构与单链表的结点结构相同。
typedef struct node { Elemtype data;
struct node* next; }StackNode,*LinkStack; LinkStack top;
//top 为栈顶指针
由于栈中的主要运算是在栈顶进行插入、删除,对于单链表来说,在表头插入和删 除结点要比在表尾相对简单,因此将单链表表头作为栈顶,则单链表的头指针即为栈顶 指针。通常将链栈表示如图 3.4 所示。
相关文档
最新文档