目录
栈和队列是比较重要的数据结构,栈的特点是后进先出,队列的性质是先进先出,掌握栈和队列的性质是非常重要的。
1.实验学时
2学时
2.实验目的
1、理解栈和队列结构的特点。
2、掌握栈的顺序表示方法及基本操作的实现。
3、掌握循环队列的表示方法及基本操作的实现。
3.主要仪器设备及耗材
硬件设备:主流计算机一台
软件环境:WINDOWS 7、WINDOWS 10、Dev-C++、VSCode
4.实验内容
1、顺序栈的建立、取栈顶元素、入栈、出栈、求栈的长度等操作。
2、循环队列的建立、取队头元素、入队、出队等基本操作的实现。
3、二叉树的建立,二叉树的层序、先序、中序、后序遍历操作。
4、二叉树的查找,求二叉树的深度、宽度、镜像等基本操作的实现。
5.实验步骤
1、分析问题
2、写出算法
3、编制程序
4、上机调试
5、分析结果
6.程序清单
栈的代码清单:
#include <iostream>
#include <stdlib.h>
using namespace std;
#define MAXSIZE 100 //顺序栈初始化时分配的存储空间长度
typedef struct{
char *base; //栈底指针,栈中元素为字符
char *top; //栈顶指针
int stacksize; //栈可用的最大容量
}SqStack; //顺序栈结构
//初始化顺序栈S
bool InitStack(SqStack &S)
{
//请同学们在此处把该操作补充完整,并调试
S.base=new char[MAXSIZE];
if(!S.base) return false;
S.top=S.base;
S.stacksize=MAXSIZE;
return true;
}
//入栈操作,向顺序栈S的栈顶处插入新元素e,插入成功,返回true,插入失败,返回false
bool Push(SqStack &S, char e)
{
//请同学们在此处把该操作补充完整,并调试
if(S.top-S.base==S.stacksize) return false;
*S.top++=e;
return true;
}
//出栈操作,删除顺序栈S的栈顶元素,被删元素用参数e返回,删除成功,函数返回true,删除失败,函数返回false
bool Pop(SqStack &S,char &e)
{
//请同学们在此处把该操作补充完整,并调试
if(S.top==S.base) return false;
e=*(--S.top);
return true;
}
//取顺序栈S的栈顶元素,用参数e返回取得的栈顶元素
bool GetTop(SqStack S,char &e)
{
//请同学们在此处把该操作补充完整,并调试
if(S.top!=S.base)
{
e=*(S.top-1);
return true;
}
else return false;
}
//求顺序栈S的长度
int StackLength(SqStack S)
{
//请同学们在此处把该操作补充完整,并调试
if(S.top==S.base) return 0;
int j=0;
while(S.top!=S.base)
{
++j;
S.top--;
}
return j;
}
//输出顺序栈S中的元素(从栈顶到栈底的顺序输出)
void PrintStack(SqStack S)
{
//请同学们在此处把该操作补充完整,并调试
while(S.top!=S.base)
{
cout<<*(S.top-1);
S.top--;
}
}
//操作菜单
void showmenu()
{
cout<<endl;
cout<<"**************************顺序栈基本操作*************************"<<endl;
cout<<"1、入栈 2、出栈 3、输出栈中元素"<<endl;
cout<<"4、取栈顶元素 5、退出"<<endl;
cout<<endl;
}
int main()
{
SqStack S; //定义顺序栈变量S
int k;
char e;
bool flag;
InitStack(S); //初始化顺序栈S
cout<<"顺序栈S初始化成功!"<<endl;
//循环显示菜单,完成顺序栈的一系列操作
do{
showmenu();
cout<<"请选择要执行的操作序号"<<endl;
cin>>k; //k表示用户选择的操作序号
switch(k)
{
case 1: //执行入栈操作
cout<<"请输入入栈元素的值"<<endl;
cin>>e; //输入要插入到栈顶处的值
flag=Push(S,e); //执行入栈操作,,flag表示入栈操作结果,或为true,或为false
if(flag) //若入栈操作成功
cout<<"入栈操作成功"<<endl;
else
cout<<"栈已满,入栈操作失败!"<<endl;
break;
case 2: //执行出栈操作
flag=Pop(S,e); //执行出栈操作,被删栈顶元素的值用e返回,flag表示出栈操作结果,或为true,或为false
if(flag) //若出栈操作成功
cout<<"出栈操作成功,被删栈顶元素为:"<<e<<endl; //输出被删栈顶元素的值
else
cout<<"栈已空,不能执行出栈操作!"<<endl;
break;
case 3: //输出顺序栈的长度及栈中的元素
cout<<"当前栈的长度为:"<<StackLength(S)<<",栈中元素为(从栈底到栈顶方向):"<<endl;
PrintStack(S); //输出循环队列Q中的元素
break;
case 4: //输出栈顶元素
if(GetTop(S,e))
cout<<"栈顶元素为:"<<e<<endl;
else
cout<<"栈已空,没有栈顶元素"<<endl;
break;
case 5:
cout<<"谢谢使用,再见!"<<endl;
break;
default:
cout<<"操作序号错误,请输入正确的操作序号!"<<endl;
break;
}//switch
}while(k!=5);
return 1;
}
队列的代码清单:
#include <iostream>
#include <stdlib.h>
#include <time.h>
using namespace std;
#define MAXSIZE 5 //循环队列初始化时分配的存储空间长度
typedef struct{
char *base; //循环队列存储空间基址,队列中存储的元素是字符
int front; //队头指针
int rear; //队尾指针
}SqQueue; //循环队列结构
//初始化循环队列Q
bool InitQueue(SqQueue &Q)
{
//请同学们在此处把该操作补充完整,并调试
Q.base=new char[MAXSIZE];
if(!Q.base) return false;
Q.front=Q.rear=0;
return true;
}
//入队操作,向循环队列Q的队尾插入新元素e,插入成功,返回true,插入失败,返回false
bool EnQueue(SqQueue &Q, char e)
{
//请同学们在此处把该操作补充完整,并调试
if((Q.rear+1)%MAXSIZE==Q.front) return false;
Q.base[Q.rear]=e;
Q.rear=(Q.rear+1)%MAXSIZE;
return true;
}
//出队操作,删除循环队列Q的队头元素,被删元素用参数e返回,删除成功,函数返回true,删除失败,函数返回false
bool DeQueue(SqQueue &Q,char &e)
{
//请同学们在此处把该操作补充完整,并调试
if(Q.front==Q.rear)
return false;
e=Q.base[Q.front];
Q.front=(Q.front+1)%MAXSIZE;
return true;
}
//取循环队列Q的队头元素,用参数e返回取得的队头元素
bool GetHead(SqQueue Q,char &e)
{
//请同学们在此处把该操作补充完整,并调试
if(Q.front==Q.rear) return false;
e=Q.base[Q.front];
return true;
}
//取循环队列Q的队尾元素,用参数e返回取得的队尾元素
bool GetTail(SqQueue Q,char &e)
{
//请同学们在此处把该操作补充完整,并调试
if(Q.front==Q.rear) return false;
e=Q.base[Q.rear-1];
return true;
}
//输出循环队列Q中的元素(从队头到队尾的顺序输出)
void PrintQueue(SqQueue Q)
{
//请同学们在此处把该操作补充完整,并调试
int p=Q.front;
while(p!=Q.rear)
{
cout<<Q.base[p]<<" ";
p++;
}
}
//求循环队列Q的长度
int QueueLength(SqQueue Q)
{
return (Q.rear-Q.front+MAXSIZE)%MAXSIZE;
}
//操作菜单
void showmenu()
{
cout<<endl;
cout<<"***************************循环队列基本操作**************************************"<<endl;
cout<<"1、入队 2、出队 3、输出队列元素"<<endl;
cout<<"4、取队头元素 5、取队尾元素 6、退出"<<endl;
cout<<endl;
}
int main()
{
SqQueue Q; //定义循环队列Q
int k;
char e;
bool flag;
InitQueue(Q); //初始化循环队列Q
cout<<"循环队列Q初始化成功"<<endl;
//循环显示菜单,完成循环队列的一系列操作
do{
showmenu();
cout<<"请选择要执行的操作序号"<<endl;
cin>>k; //k表示用户选择的操作序号
switch(k)
{
case 1: //执行入队操作
cout<<"请输入入队元素的值"<<endl;
cin>>e; //输入要插入到队尾处的值
flag=EnQueue(Q,e); //执行入队操作,,flag表示入栈操作结果,或为true,或为false
if(flag) //若入队操作成功
cout<<"入队操作成功"<<endl;
else
cout<<"队列已满,入队操作失败!"<<endl;
break;
case 2: //执行出队操作
flag=DeQueue(Q,e); //执行出队操作,被删队头元素的值用e返回,flag表示出栈操作结果,或为true,或为false
if(flag) //若出队操作成功
cout<<"出队操作成功,被删队头元素为:"<<e<<endl; //输出被删队头元素的值
else
cout<<"队列已空,不能执行出队操作!"<<endl;
break;
case 3: //输出队列的长度及队列中的元素
cout<<"当前队列长度为:"<<QueueLength(Q)<<",队中元素为(从队头到队尾方向):"<<endl;
PrintQueue(Q); //输出循环队列Q中的元素
break;
case 4: //输出队头元素
if(GetHead(Q,e))
cout<<"队头元素为:"<<e<<endl;
else
cout<<"当前队列为空队列,没有队头元素"<<endl;
break;
case 5: //输出队尾元素
if(GetTail(Q,e))
cout<<"队尾元素为:"<<e<<endl;
else
cout<<"当前队列为空队列,没有队尾元素"<<endl;
break;
case 6:
cout<<"谢谢使用,再见!"<<endl;
break;
default:
cout<<"操作序号错误,请输入正确的操作序号!"<<endl;
break;
}//switch
}while(k!=6);
return 1;
}
二叉树的程序清单:
#include <iostream>
#include <stdlib.h>
#include<algorithm>
#include<cstring>
#define MAXTSIZE 100 //二叉树中结点数最大值
using namespace std;
//二叉链表结构
typedef struct BiTNode{
char data; //数据域,存放结点值
struct BiTNode *lchild,*rchild; //左右孩子指针
}BiTNode,*BiTree; // 二叉链表
//输入二叉树的先序遍历序列(空树用字符'#'表示),创建二叉链表T
void CreatBitree(BiTree &T)
{
char ch;
cin>>ch;
//请同学们补充后续操作
if(ch=='#') T=NULL;
else
{
T=new BiTNode;
T->data=ch;
CreatBitree(T->lchild);
CreatBitree(T->rchild);
}
}
//求二叉树T中值为ch的结点的层次,结果用p表示,curlevel表示当前T所在的层数,
//求值成功返回true,失败返回false
bool level(BiTree T,char ch,int curlevel,int &p)
{
//请同学们补充此操作
if(T==NULL)
{
return false;
}
else
{
if(T->data==ch)
{
p=curlevel;
return true;
}
curlevel++;
level(T->lchild,ch,curlevel,p);
level(T->rchild,ch,curlevel,p);
}
}
//在二叉树T中查找a和b的最近公共祖先,查找成功返回最近公共祖先的指针,查找失败返回NULL
BiTree LowerAncestor(BiTree T,char a,char b){
if(T==NULL || T->data==a || T->data==b)return T;//搜索到空树,返回NULL,搜索到a或b时,返回该结点的指针
BiTree left=LowerAncestor(T->lchild,a,b); //在左子树中搜索a和b的最近公共祖先
BiTree right=LowerAncestor(T->rchild,a,b); //在右子树中搜索a和b的最近公共祖先
if(left && right)return T; //若a和b分别在T的左、右子树中,则T就是a和b的最近公共祖先
else if(!left && right) return right; //右子树中找到的最近公共祖先就是a和b的最近公共祖先
else if(left && !right) return left; //左子树中找到的最近公共祖先就是a和b的最近公共祖先
else return NULL; //a和b没有最近公共祖先(二叉树中不存在a和b结点)
}
//中序遍历二叉树T,输出其中序遍历序列
void InOrderTraverse(BiTree &T)
{
//请同学们补充此操作
if(T)
{
InOrderTraverse(T->lchild);
cout<<T->data<<" ";
InOrderTraverse(T->rchild);
}
}
//先序遍历二叉树T,输出其先序遍历序列
void PreOrderTraverse(BiTree &T)
{
//请同学们补充此操作
if(T)
{
cout<<T->data<<" ";
PreOrderTraverse(T->lchild);
PreOrderTraverse(T->rchild);
}
}
//后序遍历二叉树T,输出其后序遍历序列
void PostOrderTraverse(BiTree T)
{
//请同学们补充此操作
if(T)
{
PostOrderTraverse(T->lchild);
PostOrderTraverse(T->rchild);
cout<<T->data<<" ";
}
}
//在二叉树T中查找值为ch的结点,查找成功,函数返回true,p指向该结点,否则函数返回false,p为空
bool SearchTree(BiTree T,char ch,BiTree &p)
{
BiTree q[100]; //q为一个顺序队列,队头和队尾为front和rear
//end记录当前层最后一个结点的位置,length表示当前层的宽度,maxwidth表示T的宽度
int front=0,rear=0,end,length=0,maxwidth=0;
q[rear]=T; //根结点入队
rear++;
end=rear; //记录当前层(第一层)的结束位置(最后一个结点的位置)
while(front!=rear) //当队列非空时,依次处理每一层中的结点
{
if(front<end) //若当前层还有结点没处理完,则当前层结点数增一
length++;
else //若当前层中结点已处理完,此时,当前层的宽度为length
{
maxwidth=max(length,maxwidth); //求当前二叉树的最大宽度
end=rear; //记录下一层的结束位置
length=1; //重新求下一层宽度前,将length重置为一
}
BiTNode *s=q[front]; //队头结点出队,用s表示该结点
front++;
if(s->data==ch)
{
p=s;
return true;
}
if(s->lchild) //若s有左孩子,则另其左孩子入队
{
q[rear]=s->lchild;
rear++;
}
if(s->rchild) //若s有右孩子,则另其右孩子入队
{
q[rear]=s->rchild;
rear++;
}
}
return false;
//补充完该操作后,请修改此行代码
}
//求二叉树T的深度
int Depth(BiTree T)
{
//请同学们补充此操作
if(T==NULL) return 0;
else
{
int l=Depth(T->lchild);
int r=Depth(T->rchild);
if(l>r) return l+1;
else return r+1;
}
return 0; //补充完该操作后,请修改此行代码
}
//求二叉树T的宽度,在层序遍历的基础上求T的宽度
int Width(BiTree T)
{
//请同学们补充此操作
BiTree q[100]; //q为一个顺序队列,队头和队尾为front和rear
//end记录当前层最后一个结点的位置,length表示当前层的宽度,maxwidth表示T的宽度
int front=0,rear=0,end,length=0,maxwidth=0;
q[rear]=T; //根结点入队
rear++;
end=rear; //记录当前层(第一层)的结束位置(最后一个结点的位置)
while(front!=rear) //当队列非空时,依次处理每一层中的结点
{
if(front<end) //若当前层还有结点没处理完,则当前层结点数增一
length++;
else //若当前层中结点已处理完,此时,当前层的宽度为length
{
maxwidth=max(length,maxwidth); //求当前二叉树的最大宽度
end=rear; //记录下一层的结束位置
length=1; //重新求下一层宽度前,将length重置为一
}
BiTNode *s=q[front]; //队头结点出队,用s表示该结点
front++;
if(s->lchild) //若s有左孩子,则另其左孩子入队
{
q[rear]=s->lchild;
rear++;
}
if(s->rchild) //若s有右孩子,则另其右孩子入队
{
q[rear]=s->rchild;
rear++;
}
}
maxwidth=max(length,maxwidth); //当队列为空时,不要忘了求最后一层的宽度,以获取最终的maxwidth
return maxwidth; //返回求得的二叉树的宽度
}
//对以T为根的二叉树进行层序遍历,需借助队列
void LevelTraverse(BiTree T)
{
BiTree q[100]; //q为一个顺序队列,队头和队尾为front和rear
//end记录当前层最后一个结点的位置,length表示当前层的宽度,maxwidth表示T的宽度
int front=0,rear=0,end,length=0,maxwidth=0;
q[rear]=T; //根结点入队
rear++;
end=rear; //记录当前层(第一层)的结束位置(最后一个结点的位置)
while(front!=rear) //当队列非空时,依次处理每一层中的结点
{
if(front<end) //若当前层还有结点没处理完,则当前层结点数增一
length++;
else //若当前层中结点已处理完,此时,当前层的宽度为length
{
maxwidth=max(length,maxwidth); //求当前二叉树的最大宽度
end=rear; //记录下一层的结束位置
length=1; //重新求下一层宽度前,将length重置为一
}
BiTNode *s=q[front]; //队头结点出队,用s表示该结点
front++;
cout<<s->data<<" ";
if(s->lchild) //若s有左孩子,则另其左孩子入队
{
q[rear]=s->lchild;
rear++;
}
if(s->rchild) //若s有右孩子,则另其右孩子入队
{
q[rear]=s->rchild;
rear++;
}
}
}
//求二叉树T中结点总数
int NodeCount(BiTree T)
{
//请同学们补充此操作
if(T==NULL) return 0;
else return NodeCount(T->lchild)+NodeCount(T->rchild)+1;
}
//求二叉树T中叶子结点总数
int LeafCount(BiTree T)
{
//请同学们补充此操作
if(T==NULL) return 0;
int cnt=0;
if((!T->lchild)&&(!T->rchild)) cnt++;
int l=LeafCount(T->lchild);
int r=LeafCount(T->rchild);
cnt+=l+r;
return cnt; //补充完该操作后,请修改此行代码
}
//求二叉树T中度为1的结点总数
int NodeCount1(BiTree T)
{
//请同学们补充此操作
if(T==NULL) return 0;
int cnt=0;
if((T->lchild==NULL&&T->rchild!=NULL)||(T->lchild!=NULL&&T->rchild==NULL))
cnt++;
cnt+=NodeCount1(T->lchild)+NodeCount1(T->rchild);
return cnt; //补充完该操作后,请修改此行代码
}
//求二叉树T中度为2的结点总数
int NodeCount2(BiTree T)
{
//请同学们补充此操作
if(T==NULL) return 0;
int cnt=0;
if(T->lchild&&T->rchild) cnt++;
cnt+=NodeCount2(T->lchild)+NodeCount2(T->rchild);
return cnt; //补充完该操作后,请修改此行代码
}
//输出根到结点ch的路径,len表示路径长度,路径存储在path中
void printPath(BiTree T,char ch,int len,char *path)
{
//请同学们补充此操作
if(T==NULL) return ;
else
{
path[len++]=T->data;
if(T->data==ch)
{
for(int i=0;i<len;i++) cout<<path[i]<<" ";
return ;
}
else
{
printPath(T->lchild,ch,len,path);
printPath(T->rchild,ch,len,path);
len--;
return ;
}
}
}
//二叉树的镜像,二叉树的镜像操作指的是把所有分支结点的左、右子树交换,操作返回镜像后二叉树的根指针
void mirror(BiTree &T,BiTree &NewT)
{
//请同学们补充此操作
if(T==NULL)
{
NewT=NULL;
return ;
}
if(T)
{
NewT=new BiTNode;
NewT->data=T->data;
mirror(T->lchild,NewT->rchild);
mirror(T->rchild,NewT->lchild);
}
}
//操作菜单
void showmenu()
{
cout<<endl;
cout<<"**************************顺序表基本操作*************************"<<endl;
cout<<"1、创建二叉树"<<endl;
cout<<"2、先序遍历二叉树"<<endl;
cout<<"3、中序遍历二叉树"<<endl;
cout<<"4、后序遍历二叉树"<<endl;
cout<<"5、层序遍历二叉树"<<endl;
cout<<"6、二叉树的查找"<<endl;
cout<<"7、求二叉树宽度"<<endl;
cout<<"8、求二叉树深度"<<endl;
cout<<"9、统计二叉树中结点总数"<<endl;
cout<<"10、统计二叉树中叶子结点、度为1的结点、度为2的结点总数"<<endl;
cout<<"11、求结点所在层数"<<endl;
cout<<"12、输出根到结点的路径"<<endl;
cout<<"13、求两个结点的最近公共祖先"<<endl;
cout<<"14、二叉树的镜像"<<endl;
cout<<"15、退出"<<endl;
cout<<endl;
}
int main()
{
BiTree T,p=NULL,ancestor; //T为本程序中要操作的二叉链表
int k,n;
char ch,a,b,path[MAXTSIZE]; //ch表示在二叉树中要查询的结点
bool flag;
//循环显示菜单,完成二叉树的一系列操作
do{
showmenu();
cout<<"请选择要执行的操作序号"<<endl;
cin>>k; //k表示用户选择的操作序号
switch(k)
{
case 1: //创建二叉树
cout<<"请输入要创建的二叉树,按先序序列输入,空树用字符'#'代替"<<endl;
CreatBitree(T); //根据输入的二叉树先序序列,创建二叉链表T
cout<<"二叉树创建成功!"<<endl;
break;
case 2: //先序遍历二叉树
cout<<endl<<"先序遍历结果为:"<<endl;
PreOrderTraverse(T); //输出二叉树T的先序遍历序列
break;
case 3: //中序遍历二叉树
cout<<endl<<"中序遍历结果为:"<<endl;
InOrderTraverse(T); //输出二叉树T的中序遍历序列
break;
case 4: //后序遍历二叉树
cout<<endl<<"后序遍历结果为:"<<endl;
PostOrderTraverse(T); //输出二叉树T的后序遍历序列
break;
case 5: //层序遍历二叉树
cout<<"层序遍历结果为:"<<endl;
LevelTraverse(T); //对二叉树T进行层序遍历
break;
case 6: //二叉树的查找
cout<<endl<<"请输入在二叉树中要查找的结点,查找二叉树中是否存在该结点:"<<endl;
rewind(stdin); //清空输入缓冲区
cin>>ch;
flag=SearchTree(T,ch,p); //在二叉树T中查找值为ch的结点
if(flag)
cout<<"在二叉树中查找成功"<<endl;
else
cout<<"二叉树中不存在值为"<<ch<<"的结点"<<endl;
break;
case 7: //求二叉树宽度
cout<<"二叉树的宽度为:"<<Width(T)<<endl;
break;
case 8: //求二叉树深度
cout<<"二叉树的深度为"<<Depth(T)<<endl;
break;
case 9: //统计二叉树中结点总数
cout<<"二叉树中总结点数为"<<NodeCount(T)<<endl;
break;
case 10: //统计二叉树中叶子结点、度为1的结点、度为2的结点总数
cout<<"叶子结点数、度为1结点数、度为2结点数分别为:"<<LeafCount(T)<<","<<NodeCount1(T)<<","<<NodeCount2(T)<<endl;
break;
case 11: //求结点所在层数
cout<<endl<<"请输入要查询的结点,求该结点的层数:"<<endl;
rewind(stdin);
cin>>ch;
level(T,ch,1,n); //求值为ch结点的层数,初始时根的层数为1,n表示ch所在的层数
cout<<"该结点所在的层数为:"<<n<<endl;
break;
case 12: //输出根到结点的路径
cout<<endl<<"请输入要查找路径的结点,系统将给出根到该结点的路径"<<endl;
rewind(stdin);
cin>>ch;
printPath(T,ch,0,path); //输出根到结点ch的路径
break;
case 13: //求两个结点的最近公共祖先
cout<<"请输入要查找最近公共祖先的两个结点"<<endl;
rewind(stdin);
cin>>a>>b;
ancestor=LowerAncestor(T,a,b);
if(ancestor)
cout<<"最近公共祖先为:"<<ancestor->data<<endl;
else
cout<<"无最近公共祖先为:"<<endl;
break;
case 14: //二叉树的镜像
BiTree NewT;
mirror(T,NewT); //对T进行镜像操作
cout<<"二叉树镜像操作完成,镜像后的二叉树层序遍历结果为:"<<endl;
LevelTraverse(NewT);
break;
case 15: //退出
cout<<"谢谢使用,再见!"<<endl;
break;
default:
cout<<"操作序号错误,请输入正确的操作序号!"<<endl;
break;
}//switch
}while(k!=15);
return 0;
}