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