第1关:链栈的基本操作
任务描述
本关任务是实现链栈的基本操作函数,以实现判断栈是否为空、求栈的长度、进栈、出栈以及获取栈顶元素等功能。
相关知识
链式存储的栈
栈的链式存储结构是采用某种链表结构,栈的链式存储结构简称为链栈。 这里采用单链表作为链栈,该单链表是不带头结点的。
可以用不带头结点的单链表存储链栈,设计初始化栈、判断栈是否为空、进栈和出栈等相应算法。
不带头结点的单链表,在进行插入、删除、查值等操作运算时需要对表首进行特殊处理。
不带头结点的单链表基本操作
链栈就是不带头结点的单链表,不带头结点的单链表的基本操作如下:
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("弹出的栈顶元素 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; // 指针为空
}
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");
}
int GetTop(LinkStack S,SElemType &e)
{ // 若栈不空,则用e返回S的栈顶元素,并返回OK;否则返回ERROR
/********** Begin **********/
if (S)
{
e = S->data;
return OK;
}
return ERROR;
/********** End **********/
}
int Push(LinkStack &S,SElemType e)
{ // 插入元素e为新的栈顶元素
/********** Begin **********/
LinkStack p;
p = new LNode; //生成新的结点p
p->data = e;
p->next = S; //新结点插入栈顶
S = p; // 修改栈顶指针
return OK;
/********** End **********/
}
int Pop(LinkStack &S,SElemType &e)
{ // 若栈不空,则删除S的栈顶元素,用e返回其值,并返回OK;否则返回ERROR
/********** Begin **********/
LinkStack del;
if (S == NULL) return ERROR;
e = S->data;
del = S; //del接收一下要删除的栈顶指针
S = S->next;
delete del;
return OK;
/********** End **********/
}
void StackTraverse(LinkStack S,void(*visit)(SElemType))
{ // 从栈底到栈顶依次对栈中每个元素调用函数visit()
/********** Begin **********/
LinkStack P;
InitStack(P);
LinkStack p = S;
while (p)
{
Push(P, p->data);
p = p->next;
}
ListTraverse(P, visit);
/********** 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,a=0,b=0;
char ch;
/********** Begin **********/
for(int i =0 ;i<n;i++)
{
if(exp[i]=='('||exp[i]=='{'||exp[i]=='[')
{
Push(st,exp[i]);
a++;
}
else
{
b++;
GetTop(st,ch);
if((ch =='('&&exp[i]==')')||(ch == '{'&&exp[i]=='}')||(ch=='['&&exp[i]==']'))
Pop(st,ch);
}
}
if(ListEmpty(st)&&a==b)
{
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;
}