一、实验目的
1、 熟练掌栈和队列的结构特点,掌握栈和队列的顺序存储和链式存储结构和实现。
2、 学会使用栈和队列解决实际问题。
二、实验内容
1、自己确定结点的具体数据类型和问题规模:
分别建立一个顺序栈和链栈,实现栈的压栈和出栈操作。
分别建立一个顺序队列和链队列,实现队列的入队和出队操作。
2、设计算法并写出代码,实现一个十进制转换成二进制数。
3、选做题(*)
设计一个模拟饭堂排队打饭管理软件,实现“先来先打饭”的排号叫号管理。
三、实验步骤
1、依据实验内容分别说明实验程序中用到的数据类型的定义;
2、相关操作的算法表达;
3、完整程序;
4、总结、运行结果和分析。
5、总体收获和不足,疑问等。
三、设计与编码
(一)顺序栈
1.本实验用到的理论知识
(1)栈的序号用数组的下标i表示,注意对栈操作时其下标要在范围之内。
(2)建造顺序栈插入元素时,这种特殊线性表的长度不能超过数组的最大长度。2.算法设计
(1)定义SeqStack类模板
class SeqStack
{
int data[StackSize];
int top; //栈顶指针指向栈顶数组元素的下标
public:
SeqStack();
~SeqStack(){};
void Push(int x);
int Pop();
int GetTop();
int Empty();
};
(2)构造函数:无参构造函数建立一个空栈,并给头指针top赋值-1;定义了宏定义量StackSize,代表栈的长度为20。
(3)入栈:首先判断栈是否为满,若为满,则不能执行插入操作,否则,头指针top+1,并把需要入栈的元素放到top指向的位置。
(4)出栈:首先判断栈是否为空,若为空,则不能执行出栈操作,否则,先把出栈的元素的值赋给新的变量并返回其值,头指针头top-1。
(5)取栈顶元素:如果栈不为空,则返回头指针所指向的栈顶元素。
(6)判断栈是否为空:若头指针top=-1,则输出栈为空。
3.代码
#include<iostream.h>
const int StackSize=20; //顺序栈长度为20
class SeqStack
{
int data[StackSize];
int top; //栈顶指针指向栈顶数组元素的下标
public:
SeqStack();
~SeqStack(){};
void Push(int x);
int Pop();
int GetTop();
int Empty();
};
SeqStack::SeqStack()
{
top=-1;
}
void SeqStack::Push(int x) //入栈
{
if(top==StackSize-1)throw"上溢!"; //栈满
top++;
data[top]=x;
}
int SeqStack::Pop() //出栈
{
int x;
if(top==-1)throw"下溢!"; //栈空
x=data[top--];
return x;
}
int SeqStack::GetTop() //取栈顶元素
{
if(top!=-1)
return data[top];
}
int SeqStack::Empty() //判断栈是否为空
{
if(top==-1)return 1;
else return 0;
}
void main()
{
SeqStack S; //定义对象S
cout<<"此时";
if(S.Empty())
cout<<"栈为空!"<<endl;
else cout<<"栈非空!"<<endl;
cout<<"对15和10执行入栈操作!"<<endl;
//入栈
S.Push(15);
S.Push(10);
cout<<"栈顶元素为:"<<endl;
cout<<S.GetTop()<<endl; //取栈顶元素
cout<<"执行一次出栈操作!"<<endl;
S.Pop(); //出栈
cout<<"栈顶元素为:"<<endl;
cout<<S.GetTop()<<endl;
}
4.运行结果:
(1)将15.10压入栈中,输出栈顶元素为15。
(2)将栈顶元素弹出(即15),后来的弹出的栈顶元素为10。
(3)如果栈为满,执行入栈操作,会抛出异常(上溢)。
(4)如果栈为空,执行出栈操作,会抛出异常(下溢)。
(二)链栈
1.本实验用到的理论知识
(1)链栈和顺序栈不一样,链栈不需事先知道栈的长度。
(2)链栈和普通链表一样:有数据域和指针域(统称结点)。2.算法设计
(1)定义LinkStack类模板
class LinkStack
{
Node *top;
public:
LinkStack(); //构造函数
~LinkStack(){};
void Push(int x); //入栈
int Pop(); //出栈
int GetTop(); //取栈顶元素
int Empty(); //判断是否为空
};
(2) 构造函数:将top赋值为NULL。(3)入栈:用new申请一个动态存储空间,利用头指针执行入栈操作。
(4)出栈:首先判断链栈是否为空(top=NULL),若为空则不能执行出栈操作,否则:先申请一个指针p,把top指向的元素(即要删除的元素)的地址赋给p,然后调用析购函数删除p,top++指向下一个元素。
(5)取栈顶元素:若栈不为空则返回(输出)栈顶元素。
(6)判断栈是否为空:若头指针top=NULL,则输出栈为空。
3.代码
#include<iostream.h>
class Node{
friend class LinkStack; //声明友元函数
private:
int data;
Node *next; //定义Node类的next指针
};
class LinkStack
{
Node *top;
public:
LinkStack(); //构造函数
~LinkStack(){};
void Push(int x); //入栈
int Pop(); //出栈
int GetTop(); //取栈顶元素
int Empty(); //判断是否为空
};
LinkStack::LinkStack()
{
top=NULL;
}
void LinkStack::Push(int x)
{
Node *s; //链栈入栈操作
s=new Node;
s->data=x;
s->next=top;
top=s;
}
int LinkStack::Pop()
{ Node *p;
if(top==NULL)throw"下溢!"; //链栈出栈操作
int x=top->data;
p=top;
top=top->next;
delete p;
return x;
}
int LinkStack::GetTop() //取栈顶元素
{
if(top!=NULL)
return top->data;
}
int LinkStack::Empty() //判断是否为空
{
if(top==NULL) return 1;
else return 0;
}
void main()
{
LinkStack S; //创建s对象
cout<<"此时";
if(S.Empty())
cout<<"栈为空!"<<endl;
else cout<<"栈非空!"<<endl;
cout<<"对5,8和15执行入栈操作!"<<endl;
//入栈
S.Push(5);
S.Push(8);
S.Push(15);
cout<<"栈顶元素为:"<<endl;
cout<<S.GetTop()<<endl; //取栈顶元素
cout<<"执行一次出栈操作!"<<endl;
S.Pop(); //出栈
cout<<"栈顶元素为:"<<endl;
cout<<S.GetTop()<<endl;
}
4.运行结果:
(1)将5.8.15压入栈中,输出栈顶元素为15。
(2)将栈顶元素弹出(即15),后来的弹出的栈顶元素为8。
(3)如果栈为空(top=NULL),执行出栈操作,会抛出异常(下溢)。
(三)顺序队列
1.本实验用到的理论知识
(1)队列有“先进先出”的特点。
(2)队列的序号用数组的下标i表示,注意对队列操作时其下标要在范围之内。
(3)建造顺序队列插入元素时,这种特殊线性表的长度不能超过数组的最大长度。2.算法设计
(1)定义CirQueue类模板
class CirQueue
{
int data[QueueSize]; //定义存放队列元素的数组
int front,rear; //队头和队尾指针
public:
CirQueue();
~CirQueue(){} //析构函数
void EnQueue(int x);
int DeQueue();
int GetQueue();
int Empty();
};
(2)定义一个队头指针和一个队尾指针。
(3)构造函数:初始化空队列(front指针和rear指针指向同一结点)。
(4)入队:首先判断队列是否为满,若为满,则不能进行入队操作,否则,让队尾指针在循环意义下加1且在队尾处插入元素。
(5)出队:首先判断队列是否为空(rear==front),若为空则不能执行出队操作,否则,队头指针在循环意义下加1后读取并返回出队前的队头元素。
(6)取队列顶元素:若队不为空则重新定义一个变量将队头元素输出。
(7)判断栈是否为空:若头指针front==rear,则输出队列为空。
3.代码
#include<iostream.h>
const int QueueSize=5; //定义存储队列元素的数组的最大长度
class CirQueue
{
int data[QueueSize]; //定义存放队列元素的数组
int front,rear; //队头和队尾指针
public:
CirQueue();
~CirQueue(){} //析构函数
void EnQueue(int x);
int DeQueue();
int GetQueue();
int Empty();
};
CirQueue::CirQueue() //析构函数,初始化空队列
{
front=rear=QueueSize-1;
}
void CirQueue::EnQueue(int x) //入队操作,将x元素入队
{
if((rear+1)%QueueSize==front)throw"上溢!";
rear=(rear+1)%QueueSize; //队尾指针在循环意义下加1
data[rear]=x; //在队尾处插入元素
}
int CirQueue::DeQueue() //出队操作,将队头元素出队
{
if(rear==front)throw"下溢!";
front=(front+1)%QueueSize; //队头指针在循环意义下加1
return data[front]; //读取并返回出队前的队头元素
}
int CirQueue::GetQueue() //取队头元素(并不删除)
{
if(rear==front)throw"下溢!";
int i=(front+1)%QueueSize; //重新定义一个变量将队头元素输出
return data[i];
}
int CirQueue::Empty() //判断队列是否为空
{
if(front==rear)return 1;
else return 0;
}
void main()
{
CirQueue S; //定义对象
cout<<"此时";
if(S.Empty()) //判断队列是否为空
cout<<"队列为空!"<<endl;
else cout<<"队列非空!"<<endl;
cout<<"对5,8和15执行入队操作!"<<endl;
//执行入队操作
S.EnQueue(5);
S.EnQueue(8);
S.EnQueue(15);
cout<<"队头元素为:"<<endl;
cout<<S.GetQueue()<<endl; //输出队头元素
cout<<"执行一次出队操作!"<<endl;
S.DeQueue(); //执行出队操作
cout<<"队头元素为:"<<endl;
cout<<S.GetQueue()<<endl;
}
4.运行结果:
(1)将5.8.15传入队列中,输出队列顶元素为15。
(2)将队列头元素弹出(即15),后来的弹出的队头元素为8。
(3)如果队列为满((rear+1)%QueueSize==front),执行出队操作时,会抛出异常(上溢)。
(4)如果队列为空(front=rear),执行出队操作时,会抛出异常(下溢)。
(四)链队列
1.本实验用到的理论知识
(1)链队列和顺序队列不一样,链队列不需事先知道队列的长度。
(2)链队列有头指针和尾指针,方便执行插入和删除操作。2.算法设计
(1)定义LinQueue类模板
class LinkQueue
{
Node *front,*rear; //队头和队尾指针
public:
LinkQueue();
~LinkQueue(){}; //析构函数,释放链队列中各结点的存储空间
void EnQueue(int x);
int DeQueue();
int GetQueue();
int Empty();
};
(2)构造函数:创建一个头结点,再初始化初始化空队列,将队头指针和队尾指针都指向头结点。
(3)入栈:用new申请一个动态存储空间,利用尾指针把需要入队的元素插到队尾。
(4)出栈:首先判断链栈是否为空(top=NULL),若为空则不能执行出栈操作,否则:先申请一个变量暂存队头元素,将队头元素所在的结点摘链,判断出队前队列指针长度是否为1,若为1,则让队尾指针与队头指针指向的位置一样,即队列为空。
(5)取队头元素:若队列不为空则返回(输出)队列头元素。
(6)判断队列是否为空:若front=rear,则输出队列为空。
3.代码
#include<iostream.h>
class Node{
friend class LinkQueue; //声明友元函数
private:
int data;
Node *next; //定义Node类的next指针
};
class LinkQueue
{
Node *front,*rear; //队头和队尾指针
public:
LinkQueue();
~LinkQueue(){}; //析构函数,释放链队列中各结点的存储空间
void EnQueue(int x);
int DeQueue();
int GetQueue();
int Empty();
};
LinkQueue::LinkQueue() //析构函数,初始化空队列
{
Node *s=new Node; //创建一个头结点s
s->next=NULL;
front=rear=s; //将队头指针和队尾指针都指向头结点s
}
void LinkQueue::EnQueue(int x) //入队操作,将x元素入队
{
Node *s=new Node; s->data=x; //申请一个数据域为x的结点s
s->next=NULL;
rear->next=s; //将s结点插入到队尾
rear=s;
}
int LinkQueue::DeQueue() //出队操作,将队头元素出队
{
if(rear==front)throw"下溢!";
Node *p=front->next;
int x=p->data; //暂存队头元素
front->next=p->next; //将队头元素所在的结点摘链
if(p->next==NULL)rear=front; //判断出队前队列指针长度是否为1
delete p;
return x;
}
int LinkQueue::GetQueue() //取队头元素(并不删除)
{
if(rear==front)throw"下溢!";
return front->next->data;
}
int LinkQueue::Empty() //判断队列是否为空
{
if(front==rear)return 1;
else return 0;
}
void main()
{
LinkQueue S; //定义对象
cout<<"此时";
if(S.Empty()) //判断队列是否为空
cout<<"队列为空!"<<endl;
else cout<<"队列非空!"<<endl;
cout<<"对2,6,15和23执行入队操作!"<<endl;
//执行入队操作
S.EnQueue(2);
S.EnQueue(6);
S.EnQueue(15);
S.EnQueue(23);
if(S.Empty())
{
cout<<S.GetQueue();
}
cout<<"队头元素为:"<<endl;
cout<<S.GetQueue()<<endl; //输出队头元素
cout<<"执行一次出队操作!"<<endl;
S.DeQueue(); //执行出队操作
cout<<"队头元素为:"<<endl;
cout<<S.GetQueue()<<endl;
}
4.运行结果:
(1)将5.8.15传入队列中,输出队列顶元素为15。
(2)将队列头元素弹出(即15),后来的弹出的队头元素为8。
(3)如果队列为空(front=rear),执行出队操作,会抛出异常(下溢)
(五)十进制转换为二进制
1.本实验用到的理论知识
(1)利用顺序栈“先进后出”的特点把十进制转换为二进制的数正确输出。
2.算法设计
(1)定义SeqStack类模板
#include<iostream.h>
const int StackSize=20; //顺序栈长度为20
class SeqStack
{
int data[StackSize];
int top; //栈顶指针指向栈顶数组元素的下标
public:
SeqStack();
~SeqStack(){};
void Push(int x);
int Pop();
int GetTop();
int Empty();
void Transform(int y);
};
(3)构造函数:无参构造函数建立一个空栈,并给头指针赋值-1;定义了宏定义量StackSize,代表栈的长度为20。
(4)入栈:首先判断栈是否为满,若为满,则不能执行插入操作,否则,头指针top+1,并把需要入栈的元素放到top指向的位置。
(5)出栈:首先判断栈是否为空,若为空,则不能执行出栈操作,否则,先把出栈的元素的值赋给新的变量并返回其值,头指针头top-1。
(6)取栈顶元素:如果栈不为空,则返回头指针所指向的栈顶元素。
(7)判断栈是否为空:若头指针top=-1,则输出栈为空。
3.代码
#include<iostream.h>
const int StackSize=20; //顺序栈长度为20
class SeqStack
{
int data[StackSize];
int top; //栈顶指针指向栈顶数组元素的下标
public:
SeqStack();
~SeqStack(){};
void Push(int x);
int Pop();
int GetTop();
int Empty();
void Transform(int y);
};
SeqStack::SeqStack()
{
top=-1;
}
void SeqStack::Push(int x) //入栈
{
if(top==StackSize-1)throw"上溢!"; //栈满
top++;
data[top]=x;
}
int SeqStack::Pop() //出栈
{
int x;
if(top==-1)throw"下溢!"; //栈空
x=data[top--];
return x;
}
int SeqStack::GetTop() //取栈顶元素
{
if(top!=-1)
return data[top];
}
int SeqStack::Empty() //判断栈是否为空
{
if(top==-1)return 1;
else return 0;
}
void SeqStack::Transform(int y) //执行将十进制转换为二进制
{
do
{
int n=y%2; //取余
y=y/2; //取整
Push(n); //入栈,利用栈“先进后出”的特点
}while(y!=0);
while(!Empty()) //栈不为空时,输出的栈中所有元素
{
cout<<Pop();
}
}
void main()
{
SeqStack S; //定义对象
int y;
cout<<"请输入需要转换为十进制的数字:";
cin>>y;
cout<<"转换为二进制的值为:";
S.Transform(y);
cout<<endl;
}
五、总结与心得
通过这次实验复习了栈和队列的大部分知识,但是链栈和链队列的插入删除操作不是很熟练。