旅游管理系统(包含旅游最短路径规划算法等,包含系统分析的各种uml图和界面图)
1.1問題定義
本次課程的設(shè)計題目是設(shè)計一個大型景區(qū)的管理系統(tǒng),此系統(tǒng)是為了便于景區(qū)的管理以及提升游客的旅游體驗,更好的適應(yīng)現(xiàn)如今日益發(fā)展的旅游業(yè)。
?
1.2問題分析
現(xiàn)有某景區(qū)需要開發(fā)一個景區(qū)信息管理系統(tǒng),具體需求有:建立一個主程序應(yīng)用菜單選項,方便用戶操作和選擇需要使用的功能。為了能夠適應(yīng)不同景區(qū)的需求,系統(tǒng)需要能夠創(chuàng)建景點分布圖和輸出景點分布圖,對于創(chuàng)建,可以逐條添加景點信息和路徑信息;對于輸出,需要輸出景點分布圖的鄰接矩陣。而且,系統(tǒng)需要能夠制訂旅游景點導(dǎo)游線路策略,以便于游客游覽所有景點,即給出一個入口景點,生成一個導(dǎo)游線路圖;但是導(dǎo)游路線圖中可能含有回路,所以為了使導(dǎo)游線路圖能夠優(yōu)化,需要判斷圖中有無回路,若有回路,則打印輸出回路中的景點。同時,在導(dǎo)游線路圖中,還需要為一些不愿按線路走的游客提供信息服務(wù),比如從一個景點到另一個特定的景點。所以兩個景點之間的最短路徑和最短距離就變得很重要了,系統(tǒng)需要提供輸出兩個景點間的最短路徑和最短距離的功能。此外,在景區(qū)建設(shè)中,道路建設(shè)是其中一個重要內(nèi)容,道路建設(shè)首先要保證能連通所有景點,但又要花最小的代價,修建道路的代價與它的里程相關(guān)。系統(tǒng)同樣需要提供景點搜索和景點排序的功能,游客通過輸入關(guān)鍵字來了解相關(guān)景點以便計劃好游覽地點和順序;而景點排序可以通過景點的歡迎度或景點的人流量來排序,讓游客對各個景點有個直觀的感受。最后,景區(qū)中還有固定容量的停車場,系統(tǒng)需要對到達(dá)車輛和離開車輛進行管理;當(dāng)車輛到達(dá)時,如果停車場中還有剩余位置,則直接進入并開始計時,否則在候車道等待;當(dāng)車輛離開時,在它之后進入的車輛必須先退出車場為它讓路,待該輛車開出大門外,其它車輛再按原次序進入車場,每輛停放在車場的車在它離開停車場時必須按它停留的時間長短交納費用。本系統(tǒng)面向管理員和游客,所以可以分為管理員功能和游客功能。
?
1.3研究意義
隨著科學(xué)技術(shù)的發(fā)展,人民生活水平的提高以及我國休假制度的完善,外出旅游成為了越來越多的人民度假的選擇。在這般背景前提下,造成了旅游景區(qū)在高峰期部分景點人流過大導(dǎo)致?lián)矶?#xff0c;從而影響景區(qū)形象和游客旅游體驗。從根本上說,這往往是因為景區(qū)服務(wù)不夠細(xì)致,管理不夠科學(xué),效率不高所造成的。另一方面,游客人數(shù)的急劇增長所帶來的安全問題也日益凸顯起來。系統(tǒng)化、電子化、網(wǎng)絡(luò)化、智能化的景區(qū)管理系統(tǒng)也成為了日益迫切的需求,本項目就是在這樣的背景下提出的。針對此問題,所以研究和開發(fā)高質(zhì)量、高性能的景區(qū)信息管理系統(tǒng)變得尤為重要,可以提高景區(qū)的管理水平,解放了繁重的體力勞動和腦力勞動,極大的改善了用戶服務(wù)的質(zhì)量,提高了景區(qū)的信譽。
?
第二章?系統(tǒng)設(shè)計
2.1總體設(shè)計
2.1.1設(shè)計思想
系統(tǒng)采用前后端結(jié)合的方式,后端采用servlet運行核心算法,前端使用網(wǎng)頁進行結(jié)果動態(tài)展示,通信格式為JSON。整個系統(tǒng)的架構(gòu)圖如下圖所示:
轉(zhuǎn)存失敗重新上傳取消
<1> 管理員功能:
(1)登錄:管理員使用賬戶密碼登錄。?????????
(2)公告發(fā)布:設(shè)計發(fā)布公告時將內(nèi)容寫入一個文本文件,在游客登錄或管理員登錄時讀取這個文本文件并顯示在界面上。
(3)管理景點:景點信息的增加,刪除和修改的功能。
(4)管理道路:實現(xiàn)道路的插入和刪除的功能。
(5)輸出停車場信息:顯示停車場信息,包括等待車輛,停車場空位。
?
<2> 游客功能:
(1)景點查詢和排序:可以根據(jù)自己的需求對景區(qū)進行各種信息查詢并對景點進行一定順序上的排序。
(2)景點最短路徑:系統(tǒng)還應(yīng)能夠根據(jù)游客輸入的兩個景點的名稱來輸出兩地之間的最短路徑和最短距離。
(3)輸出導(dǎo)游路線圖:系統(tǒng)還能為游客輸出導(dǎo)游路線圖。
?
2.1.2數(shù)據(jù)結(jié)構(gòu)
- 圖的存儲結(jié)構(gòu)
public class ALGraph {
private int arcNum; //景點數(shù)量
???? private int vetNum; //路的數(shù)量
???? private List<ArcNode> nodes; //存儲景點的list
···
??? }
- 景點結(jié)點
public class ArcNode {
private String name; //景點名稱
private String des; //景點描述
private int pop; //景點歡迎度
private boolean hasRest; //有無休息區(qū)
private boolean hasToilet; //有無公廁
private VNode first; //景點的第一條邊
···
}
- 邊結(jié)點
?
public class VNode {
private int index; //另一個景點在景點數(shù)組中的位置
private int dist; //兩個景點的距離
private int time; //所需時間
private VNode next; //與頭結(jié)點相連的下一條邊
···
}
- 汽車
public class Car {
private String number; //汽車車號
private Date ar_time; //汽車到達(dá)時間
···
}
- 棧
public class Stack<T> {
private T[] mArray;
private int size;
?
public void push(T val){
···
}
?
public void pop(){
···
}
?
public T peek(){
···
}
}
- 隊列
public class Queue {
private CarNode phead; //汽車結(jié)點的鏈表頭
private int size; //隊列的元素個數(shù)
?
public void add(Car car){
···
}
?
public void pop(){
···
}
?
public CarNode front(){
···
}
}
2.1.3用戶界面
轉(zhuǎn)存失敗重新上傳取消
轉(zhuǎn)存失敗重新上傳取消
轉(zhuǎn)存失敗重新上傳取消
轉(zhuǎn)存失敗重新上傳取消
?
轉(zhuǎn)存失敗重新上傳取消
?
轉(zhuǎn)存失敗重新上傳取消
?
?
2.2程序設(shè)計
2.2.1類圖
轉(zhuǎn)存失敗重新上傳取消
2.2.2流程圖
轉(zhuǎn)存失敗重新上傳取消
第三章?系統(tǒng)實現(xiàn)與調(diào)試
3.1 導(dǎo)游線路圖的創(chuàng)建
建立無向帶權(quán)圖,輸入頂點和邊的信息,輸出鄰接鏈表G。因無向邊的原因,輸入一條邊時構(gòu)建兩條點。對于導(dǎo)游線路圖的創(chuàng)建,該系統(tǒng)直接從服務(wù)器端存儲的Arc.txt中讀取所有景點信息和路徑信息。如果需要更換旅游圖,則直接在txt文件中修改即可。
讀入數(shù)據(jù)的代碼段為:
for(int?i=1; i<=graph.getArcNum(); i++){
tmp = reader.nextLine();
infos?= tmp.split(" ");
name = infos[0];
des = infos[1];
pop = Integer.parseInt(infos[2]);
hasRest = ((infos[3].equals("1"))?true:false);
hasToilet = ((infos[4].equals("1"))?true:false);
graph.getNodes().add(new?ArcNode(name, des, pop, hasRest, hasToilet));
}
對于導(dǎo)游路線圖的輸出,從鄰接鏈表轉(zhuǎn)換成鄰接矩陣,并輸出鄰接矩陣。
輸出數(shù)據(jù)的代碼段為:
for(int?i=0; i<graph.getArcNum(); i++){
System.out.print(graph.getNodes().get(i).getName()+" ?");
for(int?j=0; j<graph.getArcNum(); j++){
if(i == j){
System.out.print("0 ?");
continue;
}
int?dis = isContact(i, j);
System.out.print(dis + " ?");
}
System.out.println();
}
將鄰接鏈表中的景點和路徑信息顯示在前端html頁面的中,即在前端顯示模擬的導(dǎo)游路線圖和景點信息。
HTML5中有canvas這個網(wǎng)頁元素,可以用JavaScript代碼在canvas中直接繪制圓、直線等幾何圖形。所以選用了該元素,并使用JavaScript繪制了景點、路徑、路徑長度以及各個景點的信息。
context.beginPath();
//畫圓
context.arc(xPosition,yPosition,30,0,Math.PI*2,true);
//畫線
context.moveTo(x1Position,y1Position);
context.lineTo(x2Position,y2Position);
//添加文字
context.fillText(dist, xPosition, yPosition);
context.closePath();
context.stroke();
3.2輸出導(dǎo)游路線圖及其圖中的回路部分
TourSystem中可輸出旅游景點導(dǎo)游線路,輸入一個景點作為入口,建立一個導(dǎo)游線路圖,導(dǎo)游線路圖用有向圖表示。遍歷采用深度優(yōu)先遍歷,這也比較符合游客心理。
深度優(yōu)先遍歷的關(guān)鍵代碼為:
public?List<Integer> DFSTraverse(String start){
Stack<Integer> traverseNodes = new?Stack<Integer>(Integer.class, graph.getArcNum());
startIndex?= getPos(start);
traverseNodes.push(startIndex);
while(!traverseNodes.isEmpty() && !hasAllVisited()){
int?arcNodeIndex = traverseNodes.peek();
visited[arcNodeIndex] = true;
VNode vNode =?graph.getNodes().get(arcNodeIndex).getFirst();
while(vNode != null){
if(!visited[vNode.getIndex()]){
traverseNodes.push(vNode.getIndex());
break;
}
vNode = vNode.getNext();
}
if(vNode == null){
traverseNodes.pop();
}
tourIndexList.add(arcNodeIndex);
}
?
return?tourIndexList;
}
為優(yōu)化導(dǎo)游線路圖功能,可通過拓?fù)渑判虻姆椒ㄅ袛鄨D中有無回路,若有回路,則打印輸出回路中的景點。拓?fù)渑判蛑辉谟邢驘o環(huán)圖中有效,這里針對輸出的有向路線圖。過程如下:(1)計算有向圖中每個頂點的入度,存儲在數(shù)組中,將所有入度為0的頂點入隊;(2)遍歷隊列,在遍歷的過程中,將出隊的頂點所有相鄰的頂點的入度減1,如果有入度減為0的頂點則繼續(xù)入隊,否則繼續(xù)遍歷隊列剩余元素;(3)遍歷入度數(shù)組,若入度不為0,則該頂點在回路中。
拓?fù)渑判虻年P(guān)鍵代碼為:
while(!queue.isEmpty()){
int?ind = queue.poll();
VNode node = directGraph.getNodes().get(ind).getFirst();
while(node != null){
indegree[node.getIndex()]--;
if(indegree[node.getIndex()] == 0){
queue.offer(node.getIndex());
}
node = node.getNext();
}
}
拓?fù)渑判蜻^程中,首先會將所有入度為0的頂點入隊,然后才可以繼續(xù)進行隊列元素的提取和添加。在本功能實現(xiàn)過程中,通過路線圖輸出過程中建立起來的有向圖里,沒有入度為0的頂點,所以拓?fù)渑判驘o法正常進行。
將入口頂點的入度置為0,然后將其放入隊列中,這樣就能正常進行拓?fù)渑判蛄?。比如北門是起始點,則將北門這個頂點的入度置為0,然后放入隊列。
3.3輸出兩個景點之間最短路徑和最短距離
Dijkstra算法是典型的最短路徑算法,用于計算一個節(jié)點到其他所有節(jié)點的最短路徑。設(shè)G=(V,E)是一個帶權(quán)有向圖,把圖中頂點集合V分成兩組,第一組為已求出最短路徑的頂點集合(用S表示,初始時S中只有一個源點,以后每求得一條最短路徑 , 就將加入到集合S中,直到全部頂點都加入到S中,算法就結(jié)束了),第二組為其余未確定最短路徑的頂點集合(用U表示),按最短路徑長度的遞增次序依次把第二組的頂點加入S中。在加入的過程中,總保持從源點v到S中各頂點的最短路徑長度不大于從源點v到U中任何頂點的最短路徑長度。此外,每個頂點對應(yīng)一個距離,S中的頂點的距離就是從v到此頂點的最短路徑長度,U中的頂點的距離,是從v到此頂點只包括S中的頂點為中間頂點的當(dāng)前最短路徑長度。
Dijkstra的關(guān)鍵代碼為:
public?void?dijkstra(String source, String des){
sourceIndex?= getPos(source);
desIndex?= getPos(des);
//初始化最短距離數(shù)組為INF
for(int?i=0; i<graph.getArcNum(); i++){
dis[i] = (i==sourceIndex?? 0 : Constants.INF);
}
?
for(int?i=0; i<graph.getArcNum(); i++){
int?minPos = -1, m = Constants.INF;
for(int?j=0; j<graph.getArcNum(); j++){
if(vis[j]==0 && dis[j]<m){
m = dis[minPos=j];
}
}
vis[minPos] = 1;
for(int?j=0; j<graph.getArcNum(); j++){
if(vis[j]==0 && dis[minPos]+getLength(minPos, j)<dis[j]){
dis[j] = dis[minPos]+getLength(minPos, j);
fath[j] = minPos;
}
}
}
}
通過Dijkstra算法直接計算兩點間的最短距離比較容易,如何保存最短路徑中的頂點信息則相對困難一些。
通過設(shè)定一個fath數(shù)組,用來記錄最短路徑尋找過程中,尋找路線確定的頂點信息。當(dāng)?shù)竭_(dá)某點的距離需要更新時,fath數(shù)組則用來記錄邊的起點信息。最后通過目標(biāo)點的坐標(biāo)依次遍歷fath數(shù)組,則可得到逆序的路線圖。
3.4輸出道路修建規(guī)劃圖
在景區(qū)建設(shè)中,道路建設(shè)是其中一個重要內(nèi)容。道路建設(shè)首先要保證能連通所有景點,但又要花最小的代價,可以通過求最小生成樹來解決這個問題。首先將圖中所有的邊按其權(quán)值大小排序,然后按由小到大的次序順序選取這些邊,若選邊后不形成回路,則保留作為一條邊,若形成回路則除去.依次選夠(n-1)條邊,即得最小生成樹。(n為頂點數(shù))
最小生成樹的關(guān)鍵代碼為:
//用于保存已有最小生成樹中每個頂點在該最小生成樹中的終點
int[] ends = new?int[graph.getVetNum()];
//用于保存結(jié)果最小生成樹的邊
VData[] results = new?VData[graph.getVetNum()];
?
//獲取圖中所有的邊
VData[] edges = getEdges();
//將邊按照權(quán)的大小進行排序(從小到大)
sortEdges(edges);
//對所有邊進行遍歷
for(int?i=0; i<graph.getVetNum(); i++){
int?m = getEnd(ends, edges[i].getStart());?//獲取起點在已有最小生成樹中的終點
int?n = getEnd(ends, edges[i].getEnd()); //獲取該邊終點在已有最小生成樹中的終點
//如果m!=n,說明在已有最小生成樹中添加該邊不會形成回路
if(m != n){
ends[m] = n;
results[index++] = edges[i];
}
}
最小生成樹算法在選邊的過程中要判斷添加該邊后是否會形成回路,若形成回路則出去,不形成則屬于最小生成樹里的一條邊。那么如何判斷是否會形成回路。
使用并查集的思想。首先將頂點劃分到不同集合中,每個集合中的頂點表示一個無回路的連通分量;然后當(dāng)選取一條邊時,若它的兩個頂點屬于不同的集合,則這條邊保留,并把兩個頂點所在的集合合并成一個集合。如果選取的一條邊的兩個頂點屬于同一個集合,則該邊舍棄。
3.5查找及排序
對于景點查找功能,本系統(tǒng)采用KMP匹配算法。KMP字符串模式匹配通俗點說就是一種在一個字符串中定位另一個串的高效算法。首先求得模式串中每個字符的next[j]值;然后進行模式匹配。此算法的思想是直截了當(dāng)?shù)?#xff1a;將主串S中某個位置i起始的子串和模式串T相比較。即從j=0起比較S[i+j]與T[j],若相等,則在主串S中存在以i為起始位置匹配成功的可能性,繼續(xù)往后比較(j逐步增1),直至與T串中最后一個字符相等為止,否則改從S串的下一個字符起重新開始進行下一輪的"匹配",即將串T向后滑動一位,即i增1,而j退回至0,重新開始新一輪的匹配,此時j=0。
KMP匹配算法的關(guān)鍵代碼為:
int[] K = calculateK(keyword);
?
int?i = 1, j = 1;
while(i<=doc.length() && j<=keyword.length()){
if(j==0 || newDoc.charAt(i)==newKeyword.charAt(j)){
i++;
j++;
}else{
j = K[j];
}
}
對于排序功能,由于快速排序算法時間復(fù)雜度為O(N*logN),所以本系統(tǒng)采用快速排序算法。算法描述如下:從數(shù)列中取出一個數(shù)作為基準(zhǔn)數(shù),然后開始分區(qū)過程,將比基準(zhǔn)數(shù)大的數(shù)全放在它的右邊,小于或等于它的數(shù)全放在它的左邊。再對左右區(qū)間分別重復(fù)第二步,直到各區(qū)間只有一個數(shù),算法結(jié)束。
快速排序算法的關(guān)鍵代碼為:
public?void?quickSort(int[] a, int?l, int?r) {
if?(l < r) {
int?i,j,x;
?
i = l;
j = r;
x = a[i];
while?(i < j) {
while(i < j && a[j] > x) j--; // 從右向左找第一個小于x的數(shù)
if(i < j) a[i++] = a[j];
while(i < j && a[i] < x) i++; // 從左向右找第一個大于x的數(shù)
if(i < j) a[j--] = a[i];
}
????????? a[i] = x;
????????? quickSort(a, l, i-1); /* 遞歸調(diào)用 */
????????? quickSort(a, i+1, r); /* 遞歸調(diào)用 */
???? }
}
???最初字符串匹配算法采用的方法是樸素字符串匹配算法,即目標(biāo)字符串與原字符串進行比較,如果出現(xiàn)不相同的字符,就將目標(biāo)字符串前移到出現(xiàn)不同字符的位置然后重新進行比較,可是這樣的算法效率太低。
考慮到KMP匹配算法的時間復(fù)雜度為O(n+m),所以其效率要比樸素字符串匹配算法高效很多,最終選擇使用KMP匹配算法用來搜索功能。
3.6輸出車輛的進出信息
為實現(xiàn)任務(wù)要求的車輛停放要求,本項目自定義實現(xiàn)了棧和隊列兩種數(shù)據(jù)結(jié)構(gòu)。有車輛進入停車場時,首先判斷停車場是否為已滿;如果已經(jīng)滿了,就將車加入候車道中,否則就添加車到停車場中。
車輛添加關(guān)鍵代碼為:
//判斷停車場是否滿了,若滿了則添加車到候車道,否則直接添加
if(parking.isFull()){
shortcut.add(new?Car(number, null));
}else{
parking.push(new?Car(number, new?Date()));
}
汽車駛出停車場時,先將目標(biāo)車輛后面的車壓入暫時停車場中;然后目標(biāo)車輛離開,算出停留時間和應(yīng)繳費用;接著將暫時停車場棧中的車重新壓入到停車場棧中;最后,如果候車道中停放有車輛,就將候車道隊列最前面的車移出來壓到內(nèi)部棧中并修改駛?cè)霑r間。
車輛離開關(guān)鍵代碼為:
//查找指定車牌號的car,將其后的car停入暫時的停車場
while(parking.peek().getNumber() == number){
tempParking.push(parking.peek());
parking.pop();
}
parking.pop();
//將暫時停車場中的車重新壓入停車場棧
while(!tempParking.isEmpty()){
parking.push(tempParking.peek());
tempParking.pop();
}
//若候車道中停放有車輛,就移出最前面的車
if(!shortcut.isEmpty()){
parking.push(shortcut.front().getCar());
shortcut.pop();
}
當(dāng)汽車離開停車場時,動態(tài)顯示汽車進入暫時停車場、汽車離開和候車道中的汽車進入停車場的過程。
在后臺算法運行的過程中,棧和隊列每更新一次,就將兩個棧和一個隊列中的車輛信息記錄一次。然后生成JSON字符串返回給網(wǎng)頁端,網(wǎng)頁端通過JavaScript有間隔的更新每次記錄的信息,即可生成動態(tài)過程。
第四章?系統(tǒng)測試
4.1測試用例
- 輸入為空
- 輸入景點分布圖中不存在的景點名稱
- 輸入為空
- 輸入景點分布圖中不存在的名稱
- 輸入相同的起始點
- 車場初始化前進行車輛到達(dá)或車輛離開操作
- 初始化輸入為負(fù)數(shù)或小數(shù)
- 車輛到達(dá)和車輛離開輸入為空
- 車輛到達(dá)輸入已存在車牌號
- 車輛離開輸入不存在車牌號
4.2測試結(jié)果
- 彈出提示框,顯示起點輸入不能為空
- 彈出提示框,顯示景點不存在,請重新輸入
- 彈出提示框,顯示起始點輸入不能為空
- 彈出提示框,提示起點或終點名稱輸入錯誤,請重新輸入
- 后臺報錯,拋出異常
?
?
5.3 主要創(chuàng)新點
?
?
5.4 遇到的困難
- 彈出提示框,提示請先進行車場初始化
- 彈出提示框,提示請輸入正確格式的數(shù)
- 彈出提示框,提示信息不能為空
- 彈出提示框,提示車牌號已存在,請輸入其它車牌
- 彈出提示框,提示車場中無此車牌號,請重新輸入
-
第五章?結(jié)論
5.1實現(xiàn)結(jié)果
?
?
正在上傳…重新上傳取消
?
轉(zhuǎn)存失敗重新上傳取消轉(zhuǎn)存失敗重新上傳取消轉(zhuǎn)存失敗重新上傳取消轉(zhuǎn)存失敗重新上傳取消轉(zhuǎn)存失敗重新上傳取消轉(zhuǎn)存失敗重新上傳取消轉(zhuǎn)存失敗重新上傳取消轉(zhuǎn)存失敗重新上傳取消轉(zhuǎn)存失敗重新上傳取消
?
5.2系統(tǒng)功能
- 創(chuàng)建和輸出分布圖
- 輸出線路圖和回路
- 景點間最短路徑
- 道路修建圖
- 景點查找和排名
- 停車場管理
- 發(fā)布公告
- 將算法部署在Tomcat中,算法運行結(jié)果在網(wǎng)頁端顯示
- 網(wǎng)頁端顯示過程均為動態(tài)展示過程
- 搜索算法使用KMP匹配算法對景點名稱和景點描述進行搜索
- 輸出回路時,若有多條回路,則可以全部輸出
- 某些算法的實現(xiàn)過程,比如KMP匹配算法的計算next數(shù)組
- 路線圖的繪制方法
?
總結(jié)
以上是生活随笔為你收集整理的旅游管理系统(包含旅游最短路径规划算法等,包含系统分析的各种uml图和界面图)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: mysql无法找到事件id100描述_解
- 下一篇: NLPIR ICTCLAS2015分词系