第1关:顺序栈的基本操作
任务描述
本关任务是实现顺序栈的基本操作函数,以实现判断栈是否为满、是否为空、求栈元素个数、进栈和出栈等功能。
相关知识
栈的基本概念
栈是一种特殊的线性表,其特殊性体现在元素插入和删除运算上,它的插入和删除运算仅限定在表的某一端进行,不能在表中间和另一端进行。
栈的插入操作称为进栈(或入栈),删除操作称为出栈(或退栈)。
允许进行插入和删除的一端称为栈顶,另一端称为栈底。
处于栈顶位置的数据元素称为栈顶元素。
不含任何数据元素的栈称为空栈。
栈的示意图:
栈的基本运算
栈的基本运算主要包括以下6种:
初始化栈InitStack(st)。建立一个空栈st。
销毁栈DestroyStack(st)。释放栈st占用的内存空间。
进栈Push(st,x)。将元素x插入栈st中,使x成为栈st的栈顶元素。
出栈Pop(st,x)。当栈st不空时,将栈顶元素赋给x,并从栈中删除当前栈顶。
取栈顶元素GetTop(st,x)。若栈st不空,取栈顶元素x并返回1;否则返回0。
判断栈空StackEmpty(st)。判断栈st是否为空栈。
栈既可以采用顺序存储,也可以采用链接存储来实现。栈的顺序存储结构称为顺序栈。
顺序栈的类型定义
下面给出了一种基于顺序存储的栈的实现方案,将栈元素存储在一片连续的空间:
#define STACK_INIT_SIZE 100 //存储空间初始分配量
#define STACKINCREMENT 10 //存储空间分配增量
typedef int SElemType; // 定义栈元素类型为整型
typedef struct
{
SElemType *base; //栈的基址即栈底指针
SElemType *top; //栈顶指针
int stacksize; //当前分配的空间
}SqStack;
void input(SElemType &s);
void output(SElemType s);
编程要求
根据提示,在右侧编辑器补充代码,编写顺序栈的基本运算操作函数。
void InitStack(SqStack &S); // 构造一个空栈S
void DestroyStack(SqStack &S); // 销毁栈S,S不再存在
void ClearStack(SqStack &S); // 把S置为空栈
int StackEmpty(SqStack S); // 若栈S为空栈,则返回TRUE,否则返回FALSE
int StackLength(SqStack S); // 返回S的元素个数,即栈的长度
int GetTop(SqStack S,SElemType &e); // 若栈不空,则用e返回S的栈顶元素,并返回OK;否则返回ERROR
void Push(SqStack &S,SElemType e); // 插入元素e为新的栈顶元素
int Pop(SqStack &S,SElemType &e); // 若栈不空,则删除S的栈顶元素,用e返回其值,并返回OK;否则返回ERROR
void StackTraverse(SqStack S,void(*visit)(SElemType)); // 从栈底到栈顶依次对栈中每个元素调用函数visit()输出
测试说明
平台会对你编写的代码进行测试:
测试输入:
12
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:否)
销毁栈后,s.top=0 s.base=0 s.stacksize=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
#define OVERFLOW -1
#define STACK_INIT_SIZE 100 //存储空间初始分配量
#define STACKINCREMENT 10 //存储空间分配增量
typedef int SElemType; // 定义栈元素类型为整型
/* 顺序栈类型定义 */
typedef struct
{
SElemType *base; //栈的基址即栈底指针
SElemType *top; //栈顶指针
int stacksize; //当前分配的空间
}SqStack;
void input(SElemType &s);
void output(SElemType s);
void InitStack(SqStack &S);// 构造一个空栈S
void DestroyStack(SqStack &S); // 销毁栈S,S不再存在
void ClearStack(SqStack &S); // 把S置为空栈
int StackEmpty(SqStack S); // 若栈S为空栈,则返回TRUE,否则返回FALSE
int StackLength(SqStack S); // 返回S的元素个数,即栈的长度
int GetTop(SqStack S,SElemType &e); // 若栈不空,则用e返回S的栈顶元素,并返回OK;否则返回ERROR
void Push(SqStack &S,SElemType e); // 插入元素e为新的栈顶元素
int Pop(SqStack &S,SElemType &e); // 若栈不空,则删除S的栈顶元素,用e返回其值,并返回OK;否则返回ERROR
void StackTraverse(SqStack S,void(*visit)(SElemType)); // 从栈底到栈顶依次对栈中每个元素调用函数visit()
int main()
{
int j;
SqStack s;
SElemType e;
InitStack(s);
int i;
cin>>i;
for(j=0;j<i;j++)
{
input(e);
Push(s,e);
}
printf("栈中元素依次为:");
StackTraverse(s,output);
Pop(s,e);
cout<<endl;
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);
printf("销毁栈后,s.top=%u s.base=%u s.stacksize=%d\n",s.top,s.base, s.stacksize);
}
/*****SElemType类型元素的基本操作*****/
void input(SElemType &s)
{
cin>>s;
}
void output(SElemType s)
{
cout<<s<<" ";
}
/*****顺序栈的基本操作*****/
void InitStack(SqStack &S)
{
// 构造一个空栈S
/********** Begin **********/
S.base=new SElemType[STACK_INIT_SIZE];
if(!S.base) exit(OVERFLOW);
S.top=S.base;
S.stacksize=STACK_INIT_SIZE;
/********** End **********/
}
void DestroyStack(SqStack &S)
{
// 销毁栈S,S不再存在
/********** Begin **********/
if(S.base)
{
delete S.base;
S.base=S.top=NULL;
S.stacksize=0;
}
/********** End **********/
}
void ClearStack(SqStack &S)
{
// 把S置为空栈
/********** Begin **********/
if(S.base) S.top=S.base;
/********** End **********/
}
int StackEmpty(SqStack S)
{
// 若栈S为空栈,则返回TRUE,否则返回FALSE
/********** Begin **********/
if(S.top==S.base)
return true;
else return false;
/********** End **********/
}
int StackLength(SqStack S)
{
// 返回S的元素个数,即栈的长度
/********** Begin **********/
return S.top-S.base+1;
/********** End **********/
}
int GetTop(SqStack S,SElemType &e)
{
// 若栈不空,则用e返回S的栈顶元素,并返回OK;否则返回ERROR
/********** Begin **********/
if(S.base!=S.top)
{
e=*S.top;
return OK;
}
else return false;
/********** End **********/
}
void Push(SqStack &S,SElemType e)
{
// 插入元素e为新的栈顶元素
/********** Begin **********/
*S.top++=e;
/********** End **********/
}
int Pop(SqStack &S,SElemType &e)
{
// 若栈不空,则删除S的栈顶元素,用e返回其值,并返回OK;否则返回ERROR
/********** Begin **********/
e=*--S.top;
S.top--;
return true;
/********** End **********/
}
void StackTraverse(SqStack S,void(*visit)(SElemType))
{
// 从栈底到栈顶依次对栈中每个元素调用函数visit()
/********** Begin **********/
while(S.base!=S.top)
{
cout<<*S.base<<" ";
S.base++;
}
/********** End **********/
}
第2关:栈的应用-进制转换
任务描述
本关任务:利用顺序栈将一个非负的十进制整数N转换为对应的十六进制数。非负的十进制整数N从键盘输入,转换结果从屏幕输出。
相关知识
进制转换
在较复杂的数据处理过程中,通常需要保存多个临时产生的数据。 如果先产生的数据后进行处理,那么需要用栈来保存这些数据。
十进制整数转换为 N 进制整数采用“除 N 取余,逆序排列”法。
具体做法是:
将 N 作为除数,用十进制整数除以 N,可以得到一个商和余数;
保留余数,用商继续除以 N,又得到一个新的商和余数;
仍然保留余数,用商继续除以 N,还会得到一个新的商和余数; ……
如此反复进行,每次都保留余数,用商接着除以 N,直到商为 0 时为止。
将十进制数159转换成八进制数的过程如下:
上述计算过程是低位到高位顺序产生八进制数的各个数位,而打印输出,一般来说应从高位到低位进行,恰好和计算过程相反,完全可以利用栈先进后出的特性来实现。
多文件结构
通常,在一个 C++ 程序中,只包含两类文件—— .cpp 文件和 .h文件。其中,.cpp 文件被称作 C++ 源文件,里面放的都是 C++ 的源代码;而 .h文件则被称作 C++ 头文件,里面放的也是 C++ 的源代码。
C++ 语言支持"分别编译"。也就是说,一个程序所有的内容,可以分成不同的部分分别放在不同的.cpp 文件里。.cpp 文件里的东西都是相对独立的,在编译(compile)时不需要与其他文件互通,只需要在编译成目标文件后再与其他的目标文件做一次链接(link)就行了。
所谓的头文件,一般放一些重复使用的代码,例如函数定义,宏的定义等等,但头文件不用被编译。当某一个.cpp 源文件需要它们时,它们就可以通过一个宏命令 "#include" 包含进这个.cpp 文件中,从而把它们的内容合并到.cpp 文件中去。当 .cpp 文件被编译时,这些被包含进去的 .h文件的作用便发挥了。
现在把顺序栈所有基本操作函数的定义放在 sqstack.cpp 里:
/* sqstack.cpp */
void InitStack(SqStack &S)
{
if(!(S.base=(SElemType *)malloc(STACK_INIT_SIZE*sizeof(SElemType))))
exit(OVERFLOW); // 存储分配失败
S.top=S.base;
S.stacksize=STACK_INIT_SIZE;
}
void DestroyStack(SqStack &S)
{
free(S.base);
S.base=NULL;
S.top=NULL;
S.stacksize=0;
}
void ClearStack(SqStack &S)
{
S.top=S.base;
}
int StackEmpty(SqStack S)
{
if(S.top==S.base)
return TRUE;
else
return FALSE;
}
int StackLength(SqStack S)
{
return S.top-S.base;
}
int GetTop(SqStack S,SElemType &e)
{
if(S.top>S.base)
{
e=*(S.top-1);
return OK;
}
else
return ERROR;
}
void Push(SqStack &S,SElemType e)
{
if(S.top-S.base>=S.stacksize) // 栈满,追加存储空间
{
S.base=(SElemType *)realloc(S.base,(S.stacksize+STACKINCREMENT)*sizeof(SElemType));
if(!S.base)
exit(OVERFLOW); // 存储分配失败
S.top=S.base+S.stacksize;
S.stacksize+=STACKINCREMENT;
}
*(S.top)++=e;
}
int Pop(SqStack &S,SElemType &e)
{
if(S.top==S.base)
return ERROR;
e=*--S.top;
return OK;
}
void StackTraverse(SqStack S,void(*visit)(SElemType))
{
while(S.top>S.base)
visit(*S.base++);
printf("\n");
}
/* end of sqstack.cpp */
C语言的头文件(*.h文件)也可以自己写的,以扩展名.h保存就行了。头文件的作用就是被其他的 .cpp 包含进去的。它们本身并不参与编译,但实际上,它们的内容却在多个 .cpp 文件中得到了编译。通过"定义只能有一次"的规则,我们很容易可以得出,头文件中应该只放变量和函数的声明,而不能放它们的定义。当使用#include语句将头文件引用时,相当于将头文件中所有内容,复制到#include处。为了避免因为重复引用而导致的编译错误,头文件常具有以下格式:
#ifndef LABEL
#define LABEL
//代码部分
#endif
其中,LABEL为一个唯一的标号,命名规则跟变量的命名规则一样,常根据它所在的头文件名来命名。
如果头文件的文件名叫做sqstack.h,那么可以这样使用:
#ifndef __SQSTACK_H__
#define __ SQSTACK _H__
//代码部分
#endif
意思就是,如果没有定义__SQSTACK_H__,则定义__SQSTACK_H__,并编译下面的代码部分,直到遇到#endif。这样,当重复引用时,由于__SQSTACK_H__已经被定义,则下面的代码部分就不会被编译了,这样就避免了重复定义。 我们把顺序栈所有基本操作函数的声明放在头文件 sqstack.h 中:
/* sqstack.h */
// 函数结果状态代码
#ifndef __SQSTACK_H__
#define __SQSTACK_H__
#define TRUE 1
#define FALSE 0
#define OK 1
#define ERROR 0
#define OVERFLOW -1
#define STACK_INIT_SIZE 100 //存储空间初始分配量
#define STACKINCREMENT 10 //存储空间分配增量
typedef struct
{
SElemType *base; //栈的基址即栈底指针
SElemType *top; //栈顶指针
int stacksize; //当前分配的空间
}SqStack;
void InitStack(SqStack &S);
void DestroyStack(SqStack &S);
void ClearStack(SqStack &S);
int StackEmpty(SqStack S);
int StackLength(SqStack S);
int GetTop(SqStack S,SElemType &e);
void Push(SqStack &S,SElemType e);
int Pop(SqStack &S,SElemType &e);
void StackTraverse(SqStack S,void(*visit)(SElemType));
#endif
/* end of sqstack.h */
现在如果我们要调用顺序栈的基本操作函数,那么就只需要把头文件包含进来:
/* main.cpp */
#include "sqstack.h"
int main()
{
}
/* end of main.cpp */
这样,便是一个完整的程序了。需要注意的是, .h文件不用写在编译器的命令之后,但它必须要在编译器找得到的地方(比如跟 main.cpp 在一个目录下)main.cpp 和 sqstack.cpp 都可以分别通过编译,生成 main.o 和 sqstack.o,然后再把这两个目标文件进行链接,程序就可以运行了。
编程要求
根据提示,在右侧编辑器补充代码,完成将一个非负的十进制整数N转换为对应的十六进制数函数的定义,具体要求如下:
void conversion(unsigned n) // 对于输入的任意一个非负十进制整数,打印输出与其等值的十六进制数
要注意的是八进制的数字是:0、1、2、3、4、5、6、7。
十六进制中,用A来表示10,B表示11,C表示12,D表示13,E表示14,F表示15,因此有 0~F 共16个数字,基数为16,加法运算时逢16进1,减法运算时借1当16。例如,数字 0、1、6、9、A、D、F、419、EA32、80A3、BC00 都是有效的十六进制。注意,十六进制中的字母不区分大小写,ABCDEF 也可以写作 abcdef。
测试说明
平台会对你编写的代码进行测试:
测试输入:10 预期输出:A
测试输入:100 预期输出:64
开始你的任务吧,祝你成功!
代码示例
#include <stdio.h>
#include<stdlib.h>
#include <iostream>
using namespace std;
typedef int SElemType; // 定义栈元素类型为整型
#include "sqstack.h" // 顺序栈的类型定义
void conversion(unsigned n)
{
// 对于输入的任意一个非负10进制整数,打印输出与其等值的16进制数
/********** Begin **********/
SqStack s;
InitStack(s);
SElemType temp;
SElemType hah;
SElemType e;
if(n<0)
{
temp=-n;
hah=1;
}
else temp=n;
if(temp<16)
{
switch(temp)
{
case 10:cout<<"A";break;
case 11:cout<<"B";break;
case 12:cout<<"C";break;
case 13:cout<<"D";break;
case 14:cout<<"E";break;
case 15:cout<<"F";break;
}
}
else
{
while(temp) {
hah=temp%16;
Push(s, hah);
temp /= 16;
}
while(s.base!=s.top)
{
Pop(s,e);
if(e>=10)
{
switch(e)
{
case 10:cout<<"A";break;
case 11:cout<<"B";break;
case 12:cout<<"C";break;
case 13:cout<<"D";break;
case 14:cout<<"E";break;
case 15:cout<<"F";break;
}
}
else
{
cout<<e;
}
}
}
/********** End **********/
}
int main()
{
unsigned n; // 非负整数
scanf("%u",&n); // 输入非负十进制整数n
conversion(n);
}
第3关:栈的应用-回文的判断
任务描述
本关任务:若一个字符串的正序与倒序相同,则称其为回文字符串。现在输入一个字符串,使用栈判断它是否是回文字符串。
相关知识
所谓回文字符串,就是一个字符串,从左到右读和从右到左读是完全一样的,比如:“level” 、 “aaabbaaa”
如何用栈来判断字符串是否为回文字符串,有两种方案:
可先将字符串全部字符连续进栈,产生的连续出栈序列和输入序列正好相反,如果所有元素连续出栈产生的序列和原字符串从头到尾的字符依次相同,表示原字符串是一个回文串,否则,不是回文串。
还可以优化第1种方案,先将一半的字符串存进去,出栈后再与剩下的一半进行比较,完全相同则为回文字符串,否则不是回文串。
编程要求
根据提示,在右侧编辑器补充代码,完成回文字符串的判断。
测试说明
平台会对你编写的代码进行测试:
测试输入: thisistrueurtsisiht
预期输出:
thisistrueurtsisiht
YES
测试输入:
thisisnottrue
预期输出:
thisisnottrue
NO
输入格式: 在一行中给出一个不超过80个字符长度的、以回车结束的非空字符串。
输出格式: 在第1行中输出字符串。如果它是回文字符串,在第2行中输出YES,否则输出NO。
开始你的任务吧,祝你成功!
代码示例
#include<stdio.h>
#include<string.h>
#include<iostream>
using namespace std;
#include"sqstack.h" //头文件sqstack.h为顺序栈的定义和基本操作的实现
typedef char SElemType;// 定义栈元素类型为字符型
# define MAXSIZE 80
int main()
{
/********** Begin **********/
char a[80],s[80];
int i,len,mid,next,top;
cin.getline(a,80);
puts(a);
len=strlen(a);
mid=len/2-1;
top=0;
for(i=0;i<=mid;i++)
{
s[++top]=a[i];
}
if(len%2==0)
{
next=mid+1;
}
else
{
next=mid+2;
}
for(i=next;i<=len-1;i++,top--)
{
if(a[i] != s[top])
{
break;
}
}
if(top==0)
printf("YES\n");
else
printf("NO\n");
return 0;
}