1. 程式人生 > >實驗2:棧和佇列的基本操作實現及其應用

實驗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)出棧:首先判斷

棧是否為空,若為空,則不能執行出棧操作,否則,先把出棧的元素的值賦給新的變數並返回其值,頭指標頭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;

}

五、總結與心得

    通過這次實驗複習了棧和佇列的大部分知識,但是鏈棧和鏈佇列的插入刪除操作不是很熟練。