赞
踩
任务描述
本关任务是实现链栈的基本操作函数,以实现判断栈是否为空、求栈的长度、进栈、出栈以及获取栈顶元素等功能。
相关知识
链式存储的栈
栈的链式存储结构是采用某种链表结构,栈的链式存储结构简称为链栈。 这里采用单链表作为链栈,该单链表是不带头结点的。
可以用不带头结点的单链表存储链栈,设计初始化栈、判断栈是否为空、进栈和出栈等相应算法。
不带头结点的单链表,在进行插入、删除、查值等操作运算时需要对表首进行特殊处理。
不带头结点的单链表基本操作
链栈就是不带头结点的单链表,不带头结点的单链表的基本操作如下:
struct LNode
{
ElemType data;
LNode *next;
};
typedef LNode * LinkList; // 另一种定义LinkList的方法
// 不带头结点的单链表的部分基本操作(9个)
void InitList(LinkList &L)
{ // 操作结果:构造一个空的线性表L
L=NULL; // 指针为空
}
#define DestroyList ClearList // DestroyList()和ClearList()的操作是一样的
void ClearList(LinkList &L)
{ // 初始条件:线性表L已存在。操作结果:将L重置为空表
LinkList p;
while(L) // L不空
{
p=L; // p指向首元结点
L=L->next; // L指向第2个结点(新首元结点)
free(p); // 释放首元结点
}
}
int ListEmpty(LinkList L)
{ // 初始条件:单链表L已存在。操作结果:若L为空表,则返回TRUE,否则返回FALSE
if(L)
return FALSE;
else
return TRUE;
}
int ListLength(LinkList L)
{ // 初始条件:线性表L已存在。操作结果:返回L中数据元素个数
int i=0;
LinkList p=L;
while(p) // p指向结点(没到表尾)
{
p=p->next; // p指向下一个结点
i++;
}
return i;
}
int GetElem(LinkList L,int i,ElemType &e)
{ // L为不带头结点的单链表的头指针。当第i个元素存在时,其值赋给e并返回OK,否则返回ERROR
int j=1;
LinkList p=L;
if(i<1) // i值不合法
return ERROR;
while(j<i&&p) // 没到第i个元素,也没到表尾
{
j++;
p=p->next;
}
if(j==i) // 存在第i个元素
{
e=p->data;
return OK;
}
else
return ERROR;
}
int LocateElem(LinkList L,ElemType e,int(*compare)(ElemType,ElemType))
{ // 初始条件:线性表L已存在,compare()是数据元素判定函数(满足为1,否则为0)
// 操作结果:返回L中第1个与e满足关系compare()的数据元素的位序。
// 若这样的数据元素不存在,则返回值为0
int i=0;
LinkList p=L;
while(p)
{
i++;
if(compare(p->data,e)) // 找到这样的数据元素
return i;
p=p->next;
}
return 0;
}
int ListInsert(LinkList &L,int i,ElemType e)
{ // 在不带头结点的单链线性表L中第i个位置之前插入元素e
int j=1;
LinkList p=L,s;
if(i<1) // i值不合法
return ERROR;
s=(LinkList)malloc(sizeof(LNode)); // 生成新结点
s->data=e; // 给s的data域赋值
if(i==1) // 插在表头
{
s->next=L;
L=s; // 改变L
}
else
{ // 插在表的其余处
while(p&&j<i-1) // 寻找第i-1个结点
{
p=p->next;
j++;
}
if(!p) // i大于表长+1
return ERROR;
s->next=p->next;
p->next=s;
}
return OK;
}
int ListDelete(LinkList &L,int i,ElemType &e)
{ // 在不带头结点的单链线性表L中,删除第i个元素,并由e返回其值
int j=1;
LinkList p=L,q;
if(i==1) // 删除第1个结点
{
L=p->next; // L由第2个结点开始
e=p->data;
free(p); // 删除并释放第1个结点
}
else
{
while(p->next&&j<i-1) // 寻找第i个结点,并令p指向其前趋
{
p=p->next;
j++;
}
if(!p->next||j>i-1) // 删除位置不合理
return ERROR;
q=p->next; // 删除并释放结点
p->next=q->next;
e=q->data;
free(q);
}
return OK;
}
void ListTraverse(LinkList L,void(*vi)(ElemType))
{ // 初始条件:线性表L已存在。操作结果:依次对L的每个数据元素调用函数vi()
LinkList p=L;
while(p)
{
vi(p->data);
p=p->next;
}
printf("\n");
}
链栈的基本操作
针对链栈我们定义如下操作:
typedef SElemType ElemType; // 栈结点类型和链表结点类型一致
typedef LinkList LinkStack; // LinkStack是指向栈结点的指针类型
#define InitStack InitList // InitStack()与InitList()作用相同,下同
#define DestroyStack DestroyList
#define ClearStack ClearList
#define StackEmpty ListEmpty
#define StackLength ListLength
编程要求
根据提示,在右侧编辑器 Begin-End 区间补充代码,实现4个链栈的基本操作函数,具体要求如下:
int GetTop(LinkStack S,SElemType &e); // 若栈不空,则用e返回S的栈顶元素,并返回OK;否则返回ERROR
int Push(LinkStack &S,SElemType e); // 插入元素e为新的栈顶元素
int Pop(LinkStack &S,SElemType &e)// 若栈不空,则删除S的栈顶元素,用e返回其值,并返回OK;否则返回ERROR
void StackTraverse(LinkStack S,void(*visit)(SElemType)); // 从栈底到栈顶依次对栈中每个元素调用函数visit()
平台会对你编写的代码进行测试:
测试输入: 12 47 5 8 6 92 45 63 75 38 4 29
预期输出: 栈中元素依次为:12 47 5 8 6 92 45 63 75 38 4 29
弹出的栈顶元素 e=29
栈空否:0(1:空 0:否)
栈顶元素 e=4 栈的长度为11
清空栈后,栈空否:1(1:空 0:否)
输入说明 第一行输入顺序栈的数据元素的个数n; 第二行输入顺序栈的n个整数。
输出说明 第一行输出顺序栈的所有数据元素; 第二行输出出栈的数据元素; 第三行判断栈是否为空; 第四行输出当前的栈顶元素及栈的长度; 第五行销毁栈后,判断栈是否为空;
#include <stdio.h> #include<stdlib.h> #include <iostream> using namespace std; // 函数结果状态代码 #define TRUE 1 #define FALSE 0 #define OK 1 #define ERROR 0 typedef int SElemType; void input(SElemType &s); void output(SElemType s); typedef SElemType ElemType; // 栈结点类型和链表结点类型一致 struct LNode { ElemType data; LNode *next; }; typedef LNode * LinkList; // 另一种定义LinkList的方法 // 不带头结点的单链表的部分基本操作(9个) #define DestroyList ClearList // DestroyList()和ClearList()的操作是一样的 void InitList(LinkList &L); void ClearList(LinkList &L); int ListEmpty(LinkList L); int ListLength(LinkList L); int GetElem(LinkList L,int i,ElemType &e); int LocateElem(LinkList L,ElemType e,int(*compare)(ElemType,ElemType)); int ListInsert(LinkList &L,int i,ElemType e); int ListDelete(LinkList &L,int i,ElemType &e); void ListTraverse(LinkList L,void(*vi)(ElemType)); /*****链栈的基本操作*****/ typedef LinkList LinkStack; // LinkStack是指向栈结点的指针类型 #define InitStack InitList // InitStack()与InitList()作用相同,下同 #define DestroyStack DestroyList #define ClearStack ClearList #define StackEmpty ListEmpty #define StackLength ListLength int GetTop(LinkStack S,SElemType &e); // 若栈不空,则用e返回S的栈顶元素,并返回OK;否则返回ERROR int Push(LinkStack &S,SElemType e); // 插入元素e为新的栈顶元素 int Pop(LinkStack &S,SElemType &e); // 若栈不空,则删除S的栈顶元素,用e返回其值,并返回OK;否则返回ERROR void StackTraverse(LinkStack S,void(*visit)(SElemType)); // 从栈底到栈顶依次对栈中每个元素调用函数visit() int main() { int j,n; LinkStack s; SElemType e; InitStack(s); cin>>n; for(j=1;j<=n;j++) { input(e); Push(s,e); } printf("栈中元素依次为:"); StackTraverse(s,output); Pop(s,e); printf("\n弹出的栈顶元素 e=%d\n",e); printf("栈空否:%d(1:空 0:否)\n",StackEmpty(s)); GetTop(s,e); printf("栈顶元素 e=%d 栈的长度为%d\n",e,StackLength(s)); ClearStack(s); printf("清空栈后,栈空否:%d(1:空 0:否)\n",StackEmpty(s)); DestroyStack(s); } /*****SElemType类型元素的基本操作*****/ void input(SElemType &s) { cin>>s; } void output(SElemType s) { cout<<s<<" "; } // 不带头结点的单链表的部分基本操作(9个) #define DestroyList ClearList // DestroyList()和ClearList()的操作是一样的 void InitList(LinkList &L) { // 操作结果:构造一个空的线性表L // L=NULL; // 指针为空 L = (LinkStack)malloc(sizeof(LinkStack)); L->next = NULL; } void ClearList(LinkList &L) { // 初始条件:线性表L已存在。操作结果:将L重置为空表 LinkList p; while(L) // L不空 { p=L; // p指向首元结点 L=L->next; // L指向第2个结点(新首元结点) free(p); // 释放首元结点 } } int ListEmpty(LinkList L) { // 初始条件:线性表L已存在。操作结果:若L为空表,则返回TRUE,否则返回FALSE if(L) return FALSE; else return TRUE; } int ListLength(LinkList L) { // 初始条件:线性表L已存在。操作结果:返回L中数据元素个数 int i=0; LinkList p=L->next; while(p) // p指向结点(没到表尾) { p=p->next; // p指向下一个结点 i++; } return i; } int GetElem(LinkList L,int i,ElemType &e) { // L为不带头结点的单链表的头指针。当第i个元素存在时,其值赋给e并返回OK,否则返回ERROR int j=1; LinkList p=L; if(i<1) // i值不合法 return ERROR; while(j<i&&p) // 没到第i个元素,也没到表尾 { j++; p=p->next; } if(j==i) // 存在第i个元素 { e=p->data; return OK; } else return ERROR; } int LocateElem(LinkList L,ElemType e,int(*compare)(ElemType,ElemType)) { // 初始条件:线性表L已存在,compare()是数据元素判定函数(满足为1,否则为0) // 操作结果:返回L中第1个与e满足关系compare()的数据元素的位序。 // 若这样的数据元素不存在,则返回值为0 int i=0; LinkList p=L; while(p) { i++; if(compare(p->data,e)) // 找到这样的数据元素 return i; p=p->next; } return 0; } int ListInsert(LinkList &L,int i,ElemType e) { // 在不带头结点的单链线性表L中第i个位置之前插入元素e int j=1; LinkList p=L,s; if(i<1) // i值不合法 return ERROR; s=(LinkList)malloc(sizeof(LNode)); // 生成新结点 s->data=e; // 给s的data域赋值 if(i==1) // 插在表头 { s->next=L; L=s; // 改变L } else { // 插在表的其余处 while(p&&j<i-1) // 寻找第i-1个结点 { p=p->next; j++; } if(!p) // i大于表长+1 return ERROR; s->next=p->next; p->next=s; } return OK; } int ListDelete(LinkList &L,int i,ElemType &e) { // 在不带头结点的单链线性表L中,删除第i个元素,并由e返回其值 int j=1; LinkList p=L,q; if(i==1) // 删除第1个结点 { L=p->next; // L由第2个结点开始 e=p->data; free(p); // 删除并释放第1个结点 } else { while(p->next&&j<i-1) // 寻找第i个结点,并令p指向其前趋 { p=p->next; j++; } if(!p->next||j>i-1) // 删除位置不合理 return ERROR; q=p->next; // 删除并释放结点 p->next=q->next; e=q->data; free(q); } return OK; } void ListTraverse(LinkList L,void(*vi)(ElemType)) { // 初始条件:线性表L已存在。操作结果:依次对L的每个数据元素调用函数vi() LinkList p=L; while(p) { vi(p->data); p=p->next; } printf("\n"); } int GetTop(LinkStack S,SElemType &e) { // 若栈不空,则用e返回S的栈顶元素,并返回OK;否则返回ERROR /********** Begin **********/ while(S->next) { S=S->next; } e=S->data; return e; /********** End **********/ } int Push(LinkStack &S,SElemType e) { // 插入元素e为新的栈顶元素 /********** Begin **********/ LinkStack p=S; LinkStack q=(LinkStack)malloc(sizeof(LinkStack)); while(p->next) { p=p->next; } q->data=e; q->next=p->next; p->next=q; return e; /********** End **********/ } int Pop(LinkStack &S,SElemType &e) { // 若栈不空,则删除S的栈顶元素,用e返回其值,并返回OK;否则返回ERROR /********** Begin **********/ LinkStack p=S,q; while(p->next->next) { p=p->next; } e=p->next->data; q=p->next; p->next=q->next; // free(q); return e; /********** End **********/ } void StackTraverse(LinkStack S,void(*visit)(SElemType)) { // 从栈底到栈顶依次对栈中每个元素调用函数visit() /********** Begin **********/ LinkStack p=S->next; while(p) { visit(p->data); p=p->next; } /********** End **********/ }
第2关:链栈的应用——括号匹配
本关任务:设计一个算法,判断一个可能含有花括号、中括号、和圆括号的表达式中各类括号是否匹配,若匹配,则返回1;否则返回0。其中表达式只包含三种括号,花括号{}
、中括号[]
、圆括号()
,即它仅由 (
、)
、[
、]
、{
、}
六个字符组成。
从左至右扫描一个表达式(或字符串),则每个右括号将与最近遇到的那个左括号相匹配。
在从左至右扫描表达式过程中把所遇到的左括号存放到栈中。
每当遇到一个右括号时,就将它与栈顶的左括号(如果存在)相匹配,同时从栈顶删除该左括号。
算法思想:
设置一个栈,当读到左括号时,左括号进栈。当读到右括号时,则从栈中弹出一个元素,与读到的左括号进行匹配,若匹配成功,继续读入;否则匹配失败。另外,在算法的开始和结束时,栈都应该是空的,所以匹配到最后还要判断栈是否为空,若空,则说明匹配成功,返回1。若非空,则说明匹配失败,返回0。
步骤如下:
设置一个链栈st,定义一个整型flag变量(初始为1)。
用i扫描表达式exp,当i<n并且flag=1时循环:
当遇到左括号“(
”、“[
”、“{
”时,将其进栈;
遇到“}
”、“]
”、“)
”时,出栈字符ch,若出栈失败(下溢出)或者ch不匹配,则置flag=0退出循环;
否则直到exp扫描完毕为止。
若栈空并且flag为1则返回1,否则返回0。
根据提示,在右侧编辑器 Begin - End 之间补充代码,判断表达式(或字符串)中括号是否成对出现。
平台会对你编写的代码进行测试,只有所有数据全部计算正确才能通过测试:
测试输入:{ [ ] ( ) }
预期输出:{ [ ] ( ) }是匹配的表达式
测试输入:[ ( { } ] [ ) ]
预期输出:[ ( { } ] [ ) ]不是匹配的表达式
-
- #include<stdio.h>
- #include<string.h>
- #include "slink.h" //包含前面的链栈基本运算函数
-
-
- int Match(char exp[],int n) //exp存放表达式,n是表达式长度
- {
- LinkStack st; //定义一个链栈st
- InitStack(st); //栈初始化
- int flag=1,i=0;
- char ch;
- /********** Begin **********/
- int m=0,p=0;
- for(i=0;i<n;i++)
- {
- if(exp[i]=='('||exp[i]=='['||exp[i]=='{')
- {
- Push(st,exp[i]);
- m++;
- }
- else
- {
- p++;
- GetTop(st,ch);
- if((ch=='('&&exp[i]==')')||(ch=='['&&exp[i]==']')||(ch=='{'&&exp[i]=='}'))
- {
- Pop(st,ch);
- }
- }
- }
- if(ListEmpty(st)&&(m==p))
- {
- return 1;
- }
- else
- {
- return 0;
- }
-
-
-
- /********** End **********/
- }
-
- void display(char exp[])
- {
- int n=strlen(exp);
- if (Match(exp,n))
- printf("%s是匹配的表达式\n",exp);
- else
- printf("%s不是匹配的表达式\n",exp);
- }
-
- int main()
- {
- char str[80];
- scanf("%s",str);
- display(str);
- return 0;
- }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。