1. 程式人生 > >資料結構棧和佇列(五)棧的順序儲存結構和鏈式儲存結構的實現

資料結構棧和佇列(五)棧的順序儲存結構和鏈式儲存結構的實現

一、 實驗目的

1. 熟悉棧的特點(先進後出)及棧的抽象類定義;

2. 掌握棧的順序儲存結構和鏈式儲存結構的實現;

3. 熟悉佇列的特點(先進先出)及佇列的抽象類定義;

4. 掌握棧的順序儲存結構和鏈式儲存結構的實現;

二、實驗要求

1. 複習課本中有關棧和佇列的知識;

2. C++語言完成演算法和程式設計並傷及除錯通過;

三、實驗題目與要求

1. 停車場問題——棧和佇列的應用

【問題描述】設停車場是一個可停放 n 輛汽車的狹長通道,且只有一個大門可供汽車進出。汽車在停車場內按車輛到達時間的先後順序,依次由北向南排列(大門在最南端,最先到達的第一輛車停放在車場的最北端)。若停車場內已經停滿 n輛車,那麼後來的車只能在門外的便道上等候。一旦有車開走,則排在便道上的第一輛車即可開入。當停車場內某輛車要離開時,在它之後進入的車輛必須先退出車場為它讓路,待該輛車開出大門外,其他車輛再按原次序進入車場。每輛停放在車場的車在它離開停車場時必須按它停留的時間長短繳納費用。試為停車場編制按上述要求進行管理的模擬程式。

【基本要求】以棧模擬停車場,以佇列模擬車場外的便道,按照從終端讀入資料的序列進行模擬管理。每一組輸入資料包括三個資料項:汽車的“到達”(‘A’表示)或“離去”(‘D’表示)資訊、汽車標識(牌照號)以及到達或離去的時刻。對每一組輸入資料進行操作後的輸出資訊為:若是車輛到達,則輸出汽車在停車場內或者便道上的停車位置;若是車輛離去,則輸出汽車在停車場停留的時間和應繳納的費用(便道上停留的時間不收費)。棧以順序結構實現,佇列以連結串列結構實現。

【輸入輸出】

① 初始,顯示提示資訊:“請輸入停車場最大容量n=:”,提示使用者輸入停車場最大容量。

② 輸入車輛資訊:提示請輸入車輛資訊,提示使用者輸入車輛資訊(

“到達”或者“離開”,車牌編號,到達或者離開的時間),輸入值的格式為:第一個必須為字母,後兩個為數字,中間用逗號隔開。有以下幾種情況:

③ 若車輛資訊為“到達A”,車輛資訊開始進棧(模擬停車場),當棧滿,車輛會進佇列(模擬停車場旁便道)。

④ 若車輛資訊為“離開D”,會顯示該車進入停車場的時間以及相應的停車費用,若該車較部分車早進停車場,這部分車需先退出停車場,暫時進入一個新棧為其讓道,當待離開車離開停車場後,這部分車會重新進入停車場,同時便道上的第一輛車進入停車場。

⑤ 若輸入(‘P’,0,0),會輸出顯示停車場的車數。

⑥ 若輸入(‘W’,0,0),會輸出顯示便道上的車數。

⑦ 若輸入(‘E’,0,0),程式會跳出迴圈,同時程式結束。

⑧ 使用者每輸入一組資料,程式就會根據相應輸入給出輸出。

【測試用例】

 

【演算法分析】


【實現步驟】

(1) 步驟一:建立一標頭檔案park.h,定義資料的結構及函式的宣告

① 資料的表示

車輛資訊的表示——表示汽車的狀態(‘A’表示到達,‘D’表示離開)、車號、時間(到達或離開)

 

l 程式以順序棧模擬停車場以及臨時停放為給要離去的汽車讓路而從停車場退出來的汽車的場地,以連結串列佇列模擬車場外的便道,因此需要棧和隊列這兩個抽象資料型別。

 

上圖為棧的結構定義,其中data陣列儲存棧元素資訊,即車輛資訊,top表示棧頂,MaxSize表示棧的最大容量。

 

上圖為鏈佇列結點的結構定義,其中data為結點的資料域,next為結點的指標域,以及鏈佇列結構定義,包括隊頭指標front,及隊尾指標rear,及佇列元素總個數num。

② 資料的操作

宣告以下函式

 

(2) 步驟二:建立檔案park.cpp,實現park.h宣告的函式

l 初始化棧

 

l 判斷棧是否為空

 

l 其它函式請自行完成(可參考書P81P100

(3) 步驟三:建立檔案main.cpp,完成主函式(在橫線上補充合適的程式碼)

 

   

 

2. 表示式計算問題——棧的應用

利用棧將中綴表示式轉成字尾表示式,並棧利用對轉化後的字尾表示式求值(設計性實驗),假設運算元均為整數。

中綴表示式                      字尾表示式

            a*b+c                            ab*c+

            a+b*c                            abc*+

            a+(b*c+d)/e                  abc*d+e/+

例如計算字尾表示式435*+   即計算4+3*5

應輸出結果 19

實現提示

中綴表示式轉成字尾表示式演算法思想:

1. 首先需要分配2個棧,一個作為臨時儲存運算子的棧S1(含一個結束符號),一個作為輸入字尾表示式的棧S2(空棧),S1棧可先放入優先順序最低的運算子#。

2. 從中綴表示式的左端開始取字元,逐序進行如下步驟:     

Ø 若取出的字元是數字,則分析出完整的運算元,將該運算元直接送入S2棧  

Ø 若取出的字元是運算子,則將該運算子與S1棧棧頂元素比較,如果該運算子優先順序大於S1棧棧頂運算子優先順序,則將該運算子進S1棧,否則,將S1棧的棧頂運算子彈出,送入S2棧中,直至S1棧棧頂運算子低於(不包括等於)該運算子優先順序,則將該運算子送入S1棧。

Ø 若取出的字元是“(”,則直接送入S1棧棧頂。

Ø 若取出的字元是“)”,則將距離S1棧棧頂最近的“(”之間的運算子,逐個出棧,依次送入S2棧,此時拋棄“(”。

Ø 重複上面的1~4步,直至處理完所有的輸入字元。

Ø 若取出的字元是'\0',則將S1棧內所有運算子(不包括“#”),逐個出棧,依次送入S2棧。     

完成以上步驟,S2棧便為字尾表示式輸出結果,但需要對S2的內容進行逆序。因此,S2也可考慮用佇列。

對已轉化的字尾表示式求值步驟:

1. 若表示式輸入未完畢,讀入一個字元

2. 若是運算元,壓入棧,轉1

3. 若是運算子,從棧中彈出2個數,將運算結果再壓入棧

4. 若表示式輸入完畢,棧頂即所求表示式的值;

數與數之間採用空格進行分隔,若讀到數字字元直到遇到空格,則將所有數字字元湊好形成運算元。

 下面附上完整程式碼:

標頭檔案park.h


#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<iostream>
#pragma warning(disable:4996)
using namespace std;
#define price 0.1 //車費 0.1元/分
typedef struct time{
	int hour;
	int min;
}Time;//時間結點

typedef struct node{
	char num[10];//車牌
	Time reach;//到達
	Time leave;//離去
}CarNode;//車輛資訊結點

typedef struct{
	CarNode *elem;
	int top;  //top=-1為空棧
}SeqStackCar;   //順序棧 模擬停車場

typedef struct car{
	CarNode data;   //資料域
	struct car *next;//指標域
}QueueNode;   //鏈佇列結點

typedef struct{
	QueueNode *front;//頭指標
	QueueNode *rear;//尾指標
}LinkQueue; //鏈佇列  模擬停車便道

void InitStack(SeqStackCar *s,int n);//初始化停車場棧道
void In(SeqStackCar *s, CarNode x,int n);//進棧
void Out(SeqStackCar *s, CarNode *x);//出棧
void GetTop(SeqStackCar *s, CarNode *x);//獲取棧頂

void InitQueue(LinkQueue *q);//初始化停車便道
void Enter(LinkQueue *q, CarNode x);//入隊
void Delte(LinkQueue *q, CarNode *x);//出隊

void Arrive(SeqStackCar *s, LinkQueue *q,int n);//車輛到達
void Leave(SeqStackCar *s, LinkQueue *p,int n);//車輛離開

void PrintfSeq(SeqStackCar *s);//列印停車場內車輛的資訊
void PrintfQue(LinkQueue *q);//列印便道內車輛的資訊

2. park.cpp(實現park.h宣告的函式)

#include"park.h"
void InitStack(SeqStackCar *s,int n)
{
	s->elem = new CarNode[n];
	s->top = -1;
}
void In(SeqStackCar *s, CarNode x,int n)
{
	if (s->top == n- 1) return;
	s->top++;
	s->elem[s->top] = x;
}
void Out(SeqStackCar *s, CarNode *x)
{
	if (s->top == -1) return;
	else
	{
		*x = s->elem[s->top]; 
		s->top--;
	}
}
void GetTop(SeqStackCar *s, CarNode *x)
{
	if (s->top == -1) return;
	else *x = s->elem[s->top];
}
void InitQueue(LinkQueue *q)
{
	q->front = q->rear = (QueueNode *)malloc(sizeof(QueueNode));
	q->front->next = NULL;
}
void Enter(LinkQueue *q, CarNode x)
{
	QueueNode *newnode;
	newnode = (QueueNode *)malloc(sizeof(QueueNode));
	if (newnode != NULL)
	{
		newnode->data = x;
		newnode->next = NULL;
		q->rear->next = newnode;
		q->rear = newnode;
	}
	else return;
}
void Delte(LinkQueue *q, CarNode *x)
{
	QueueNode *s;
	if (q->front == q->rear) return;
	s = q->front->next;
	q->front->next = s->next;
	if (q->rear == s)
		q->rear = q->front;
	*x = s->data;
	free(s);
}
void Arrive(SeqStackCar *s, LinkQueue *q,int n)//s停車場,q便道
{
	CarNode p;
	if (s->top == n - 1)
	{ 
		printf("停車場已滿,請將車停往免費便道等候!\n");
		printf("車正在進入免費便道,請錄入車輛資訊!\n");
		printf("車輛車牌:");
		scanf("%s", &p.num);
		Enter(q, p);
		printf("車輛已停入便道!\n");
	}
	else
	{
		printf("車正在進入停車場,請錄入車輛資訊!\n");
		printf("車輛車牌:");
		scanf("%s", &p.num);
		printf("進入的時間(x時x分):");
		scanf_s("%d %d", &p.reach.hour,&p.reach.min);
		printf("該車進入的時間為:%d:%d\n",p.reach.hour,p.reach.min);
		In(s, p,n);
		printf("該車輛停在車場的第%d位置上.\n", s->top+1);
	}
}
void Leave(SeqStackCar *s, LinkQueue *q,int n)
{
	CarNode p;
	SeqStackCar w;//臨時棧,存放讓路的車輛
	InitStack(&w,n);//初始化臨時棧
	int i;
	double free;
	printf("離開車輛的車牌為:");
	scanf("%s", &p.num);
	for (i = 0; i <= s->top; i++)
	{
		if (strcmp(p.num,s->elem[i].num)==0)  //查詢離開的車輛在停車場中的位置,返回i
		{
			printf("該車輛在停車場的第%d位置上.\n", i+1);
			printf("輸入該車輛離開的時刻(x時x分):");
			scanf_s("%d %d", &s->elem[i].leave.hour, &s->elem[i].leave.min);
			printf("該車離開的時間為:%d:%d\n",s->elem[i].leave.hour, s->elem[i].leave.min);
			free = ((s->elem[i].leave.hour * 60 + s->elem[i].leave.min) - (s->elem[i].reach.hour * 60 + s->elem[i].reach.min))*price;
			printf("該車輛改收取的停車費用為%.2f元:\n", free);
			while (s->top != i)  //有車輛離開時,後面的車依次出棧,進入臨時棧w,
			{
				CarNode x;
				Out(s, &x); //從模擬停車場出棧
				In(&w, x,n);//進入臨時棧
			}
			s->top--;
			while (w.top > -1) //該車離開後,再讓臨時棧w停放的車進停車場棧s
			{
				CarNode y;   
				Out(&w, &y);
				In(s, y,n);
			}
			//有空位了,便道的第一輛車可進停車場
			if (q->front == q->rear)
				printf("便道內無停車,沒車需進停車場!\n");
			else
			{
				CarNode c;
				Delte(q, &c);
				printf("車正在進入停車場,請錄入車輛資訊!\n");
				printf("車輛車牌:%s\n", c.num);
				printf("進入的時間(x時x分):");
				scanf_s("%d %d", &c.reach.hour, &c.reach.min);
				printf("該車進入的時間為:%d:%d\n",c.reach.hour, c.reach.min);
				In(s, c,n);
				printf("該車輛停在車場的第%d位置上.\n", s->top+1);
			}
		}
	}
}

void PrintfSeq(SeqStackCar *s)//列印停車場內車輛的資訊
{
	if (s->top == -1)
		printf("停車場內為無停車!\n");
	else
	{
		printf("===============停車場內車輛資訊如下===============\n");
		for (int i = 0; i <= s->top; i++)
		{
			printf("%d號車輛:車牌:%s\t進入時間:%d:%d\n", i+1, s->elem[i].num,s->elem[i].reach.hour,s->elem[i].reach.min);
		}
		printf("==================================================\n");
	}
}
void PrintfQue(LinkQueue *q)//列印免費便道內車輛的資訊
{
	int i = 1;
	QueueNode *temp = q->front->next;
	if (q->front == q->rear)
		printf("免費便道內無停車!\n");
	else
	{
		printf("====免費便道內車輛資訊如下====\n");
		while (temp!=NULL)
		{
			printf("\t%d號車輛:車牌:%s\n", i,temp->data.num);
			i++;
			temp = temp->next;
		}
		printf("==============================\n");
	}
}

3. main.cpp(程式入口)

#include"park.h"
void menu();//選單
int main()
{
	int n;
	printf("請輸入停車場的容量:");
	scanf("%d",&n);
	printf("停車能容納%d輛車\n",n);
	system("pause");
	SeqStackCar S;
	LinkQueue Q;
	InitStack(&S,n);
	InitQueue(&Q);
	int userchoice;
	do{
		menu();
		scanf_s("%d", &userchoice);
		switch (userchoice)
		{
		case 0:break;
		case 1:Arrive(&S, &Q, n); break;
		case 2:Leave(&S, &Q, n); break;
		case 3:PrintfSeq(&S); break;
		case 4:PrintfQue(&Q); break;
		}
		system("pause");
	} while (userchoice != 0);
	return 0;
}
void menu()
{
	system("cls");
	printf("\t停車場系統\n\n\t0.退出\n\t1.車輛到達\n\t2.車輛離開\n\t3.列印停車場車輛資訊\n\t4.列印便道車輛資訊\n\n請輸入你的選擇:");
}

二、表示式計算問題
#include <iostream>
#include <string>
#include <cstdio>
#include <stack>
#include <queue>
#include <map>
using namespace std;

const char oper[] = { ' ', '(', '+', '-', '*', '/', ')'};

bool isNum(char &ch) {
	if (ch >= '0' && ch <= '9') return true;
	return false;
}

int main(void) {
	map<char, int> mp; /*運算子與優先順序的對映*/
	for (int i = 0; oper[i]; i++) {
		mp[oper[i]] = i;
	}
	char str[110];
	stack<char> ope; /*中綴轉字尾暫存運算元棧*/
	queue<char> que; /*中綴轉字尾暫存字尾表示式佇列*/
	string PostFixExp;  /*儲存字尾表示式*/
	stack<int> ss;   /*字尾表示式計算暫存運算元棧*/
	std::cout<<"輸入算術表示式"<<endl;
	while (gets_s(str)) {
		for (int i = 0; str[i]; i++) {
			if (isNum(str[i])) {
				que.push(str[i]);/*如果是運算元是進隊*/
			}
			else {
				que.push(' ');/*如果是操作符則先將空格進隊,目的是用空格隔開運算元*/
				if (str[i] == '(') {
					ope.push(str[i]);/*左括號直接進隊*/
					continue;
				}
				if (str[i] == ')') {
					while (ope.top() != '(') {
						que.push(ope.top());
						ope.pop();
					}
					ope.pop();
					continue;
				}
				while (!ope.empty() && ope.top() != '(' && mp[str[i]] <= mp[ope.top()]) {
					que.push(ope.top());
					ope.pop();
					
				}
				ope.push(str[i]);
			}
		}
		while (!ope.empty()) {
			que.push(ope.top());
			ope.pop();
		}
		while (!que.empty()) {
			if (isNum(que.front())) {
				int num = 0;
				while (isNum(que.front())) {
					num = num * 10 + que.front() - '0';
					PostFixExp+=que.front();
					que.pop();
				}
				ss.push(num);
 			}
			else {
				PostFixExp+=que.front();
				if (que.front() == '+') {
					int num2 = ss.top(); ss.pop();
					int num1 = ss.top(); ss.pop();
					ss.push(num1 + num2);
				}
				else if (que.front() == '-') {
					int num2 = ss.top(); ss.pop();
					int num1 = ss.top(); ss.pop();
					ss.push(num1 - num2);
				}
				else if (que.front() == '*') {
					int num2 = ss.top(); ss.pop();
					int num1 = ss.top(); ss.pop();
					ss.push(num1 * num2);
				}
				else if (que.front() == '/') {
					int num2 = ss.top(); ss.pop();
					int num1 = ss.top(); ss.pop();
					ss.push(num1 / num2);
				}
				que.pop();
			}
		}
		cout<<"字尾表示式為"<<PostFixExp<<"=";
		cout  << ss.top() << endl;
	}
}

舉例:

(12+133)*10-340

讀完字串,佇列的情況

空格

1

2

空格

1

3

3

空格

+

空格

1

0

空格

*

3

4

0

-