實驗2:棧和佇列的基本操作實現及其應用
一、實驗目的
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)出棧:首先判斷
(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;
}
五、總結與心得
通過這次實驗複習了棧和佇列的大部分知識,但是鏈棧和鏈佇列的插入刪除操作不是很熟練。