news 2026/1/10 14:48:16

(5-3-02)基于Flak和Floyd-Warshall的航班查询系统:计算最短路径+Flask Web前端

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
(5-3-02)基于Flak和Floyd-Warshall的航班查询系统:计算最短路径+Flask Web前端

5.4.3 计算最短路径

文件connecting_flights.py定义了类ConnectingFlights,用于计算连接航班的最短路径。该类通过调用 Floyd-Warshall 算法计算所有标准的最短路径,并将结果存储在数据库中。它提供了添加单个或多个航班信息的方法,并能够打印每对起始和目标机场之间的最短路径。通过与数据库的交互,该类能够实现航空运输领域中连接航班最短路径的计算与管理。

from shortest.Database import Database # import time class ConnectingFlights: """用于计算连接航班的类""" def __init__(self, database): """构造函数。接受数据库对象""" self.db = database def add_one_flight(self, orig, dest, **kwargs): """向数据库添加一条航班信息。接受多个权重作为关键字参数""" print(kwargs) self.db.add_flight(orig, dest, **kwargs) self.calc_all() # 计算最短路径 def add_many_flights(self, arr): """接受列表的列表,格式为:[orig, dest, **weight]""" for new_flight in arr: orig = new_flight[0] dest = new_flight[1] self.db.add_flight(orig, dest, **new_flight[2]) self.calc_all() # 计算最短路径 def calc_all(self): """计算所有标准的邻接表""" self.db.clear_adj() print("正在计算最短路径...") # 计算每个标准的最短路径 for cri in Database.Criterion: print("正在计算 {}...".format(cri.name)) # t1 = time.time() self.floyd_warshal(cri) # t2 = time.time() # print("用时 {}s.\n".format(t2-t1)) # 打印计算时间 def floyd_warshal(self, criterion): """使用 Floyd Warshal 算法在数据库中生成最短路径数据""" all_airports = self.db.all_airports # 初始化邻接表 self.make_adj(criterion) for thru in all_airports: # thru = 中间顶点 for orig in all_airports: if orig == thru: continue # 如果中间顶点与起点相同,则跳过中间顶点与起点相同的情况 for dest in all_airports: if dest == thru or dest == orig: # 如果目标或中间顶点与起点相同,则跳过目标点等于中间顶点或起点的情况 continue # 起点到中间顶点,中间顶点到目标 orig_to_thru = self.db.get_adj(criterion, orig, thru) thru_to_dest = self.db.get_adj(criterion, thru, dest) if orig_to_thru is None or thru_to_dest is None: # 如果无法通过中间顶点访问,不做任何操作 continue # 计算通过中间顶点 "thru" 的新权重 new_weight = float(orig_to_thru["weight"]) + float(thru_to_dest["weight"]) last_path = thru_to_dest["thru"] last_flight = thru_to_dest["last_flight"] # 获取现有的邻接表条目 orig_adj = self.db.get_adj(criterion, orig, dest) if orig_adj is None: # 如果之前没有发现路径,则添加。 self.db.add_to_adj(criterion, orig, dest, last_path, new_weight, last_flight) else: # 如果新权重总和较小,则使用中间顶点作为最短路径的一部分 old_weight = float(orig_adj["weight"]) if new_weight < old_weight: self.db.update_adj(criterion, orig, dest, last_path, new_weight, last_flight) def print_floyd_warshal(self, criterion): """打印每对起点和目标机场之间的最短路径""" # 获取所有机场的字符串列表 all_airports = self.db.all_airports for orig in all_airports: for dest in all_airports: print("\n{} 到 {}:".format(orig, dest)) # 获取最短路径信息 result = self.get_shortest_floyd_warshal(criterion, orig, dest) if len(result["path"]) != 0: for o, d, w, f in result["path"]: if f is None: f = "" weight = self.get_str_from_cri(criterion, w) print("{} {}->{}: {}".format(f, o, d, weight)) total = self.get_str_from_cri(criterion, result["total_weight"]) print("总 {}: {}".format(criterion.name, total)) else: print("没有数据") def get_shortest_floyd_warshal(self, criterion, orig, dest): """接受标准、起点和目标,并返回最短路径信息""" output = {} shortest = [] start = orig end = dest total = 0.0 while start != end: # 回溯以找到最短路径 # 获取起点到终点的最短路径 adj = self.db.get_adj(criterion, start, end) if adj is None: # 如果没有找到数据,则终止 break mid = adj["thru"] # 获取最短路径中终点之前的最后一个顶点 weight = float(self.db.get_weight(criterion, mid, end)) airline_id = adj["last_flight"] shortest.append((mid, end, weight, airline_id)) total += weight end = mid # 回溯,使用中间点作为终点 output["path"] = shortest[::-1] # 反转列表 output["total_weight"] = total return output @staticmethod def get_str_from_cri(criterion, target): """返回关联单位的格式化字符串""" if criterion == Database.Criterion.price: return "${}".format('%.2f' % target) elif criterion == Database.Criterion.duration: return "{} 小时 {} 分钟".format(int(target // 60), int(target % 60)) elif criterion == Database.Criterion.distance: return "{} 英里".format(target) def make_adj(self, criterion): """使用航班初始化邻接表""" weight_name = criterion.name for flight in self.db.all_flights: orig = flight["orig"] dest = flight["dest"] try: last_flight_info = (flight["airline"], flight["no"]) except KeyError: # 如果没有信息 last_flight_info = None try: weight = float(flight[weight_name]) find_existing = self.db.get_adj(criterion, orig, dest) if find_existing is None: # 如果不存在该起点目标对的邻接表,则添加 self.db.add_to_adj(criterion, orig, dest, orig, weight, last_flight_info) continue if weight < float(find_existing["weight"]): # 如果存在邻接表但新权重较小,则更新邻接表 self.db.update_adj(criterion, orig, dest, orig, weight, last_flight_info) except KeyError: pass # 如果指定的权重属性不存在,则忽略

对上述代码的具体说明如下所示。

  1. __init__(self, database):初始化方法,接受一个数据库对象作为参数,并将其存储在类的属性中。
  2. add_one_flight(self, orig, dest, **kwargs):添加单个航班信息到数据库中的方法。接受起始机场、目的地机场和多个权重作为关键字参数,并将航班信息添加到数据库中,并计算最短路径。
  3. add_many_flights(self, arr):添加多个航班信息到数据库中的方法。接受一个包含多个航班信息的列表,并将其添加到数据库中,并计算最短路径。
  4. calc_all(self):计算所有标准的最短路径的方法。清除现有的邻接表数据,然后使用 Floyd-Warshall 算法计算所有标准的最短路径,并将结果存储在数据库中。
  5. floyd_warshal(self, criterion):使用 Floyd-Warshall 算法计算指定标准的最短路径的方法。根据指定的标准,计算所有顶点对之间的最短路径,并更新数据库中的邻接表。
  6. print_floyd_warshal(self, criterion):打印指定标准的所有起始和目标机场之间的最短路径的方法。对于每对起始和目标机场,打印其最短路径以及总权重。
  7. get_shortest_floyd_warshal(self, criterion, orig, dest):获取指定标准下起始和目标机场之间的最短路径的方法。根据指定的标准、起始机场和目标机场,返回最短路径的相关信息。
  8. get_str_from_cri(criterion, target):根据指定的标准和目标值返回格式化的字符串的静态方法。根据不同的标准,返回对应的格式化字符串。
  9. make_adj(self, criterion):使用航班信息初始化指定标准的邻接表的方法。根据指定的标准,遍历数据库中的航班信息,初始化邻接表,并更新数据库中的邻接表。

5.4.4 Flask Web前端

(1)文件server.py是一个基于Flask的Web应用程序的入口文件,实现了一个基于Flask的Web应用程序。用户可以通过网页界面查询航班的最短路径信息,并将结果展示在网页上。

from flask import Flask, render_template from shortest.webapp.forms import find_form from shortest.Database import Database from shortest.connecting_flights import ConnectingFlights import os def setup_app(): # 初始化Flask应用 app = Flask(__name__) # 生成随机密钥 SECRET_KEY = os.urandom(32) # 设置应用的密钥 app.config['SECRET_KEY'] = SECRET_KEY # 初始化数据库对象 db = Database('localhost', 27017, "connecting_flight") # 初始化连接航班对象 cf = ConnectingFlights(db) @app.route("/", methods=["GET", "POST"]) def index(): # 创建表单实例 form = find_form() dbresult = [] result = [] total = "" if form.orig.data != "None" and form.dest.data != "None" and form.cri.data != "None": print(form.orig.data, form.dest.data, form.cri.data) # 获取选择的路径优先标准 cri = Database.Criterion[form.cri.data] # 获取起点和终点 search_orig = form.orig.data.split()[0].upper() search_dest = form.dest.data.split()[0].upper() # 查询最短路径的航班信息 dbresult = cf.get_shortest_floyd_warshal(cri, search_orig, search_dest) # 获取最短路径上的航班 for flight in dbresult["path"]: orig = flight[0] dest = flight[1] weight = cf.get_str_from_cri(cri, flight[2]) airline, no = flight[3] result.append({"orig": orig, "dest": dest, "weight": weight, "airline": airline, "no": no}) # 获取总权重 total = cf.get_str_from_cri(cri, dbresult["total_weight"]) print(result) # 渲染模板 return render_template("index.html", form=form, result=result, total=total) return app if __name__ == "__main__": # 运行Flask应用 setup_app.run()

上述代码的实现流程如下所示。

  1. 首先,导入了必要的模块和类,包括Flask、render_template函数、表单类find_form、Database类和ConnectingFlights类。
  2. 然后,定义了一个名为setup_app的函数,该函数用于配置和初始化Flask应用,并设置了应用的密钥。接下来,它创建了Database和ConnectingFlights的实例,并将它们传递给Flask应用。
  3. 然后,定义了路由"/",它接受GET和POST请求。在这个路由中,首先创建一个表单实例,然后根据用户的输入查询数据库,获取最短路径的航班信息,并将结果渲染到模板index.html中。最后,它使用run()方法运行Flask应用。

(2)文件forms.py定义了一个名为find_form的Flask表单类,用于在Web应用中接收用户输入的起点、目的地和路径优先标准。在这个表单中包含了三个字段:起点(orig)、目的地(dest)和路径优先标准(cri),以及一个提交按钮(submit)。其中,起点和目的地字段是字符串字段,要求用户输入并且不能为空且长度为3个字符以上;路径优先标准字段是下拉选择字段,提供了从数据库中获取的路径优先标准选项。

from flask_wtf import FlaskForm from wtforms import StringField, SubmitField, SelectField from wtforms.validators import DataRequired, Length from shortest.Database import Database class find_form(FlaskForm): orig = StringField(u'Origin', validators=[DataRequired(), Length(3)]) dest = StringField(u'Destination', validators=[DataRequired(), Length(3)]) options = [] for option in Database.Criterion: name = option.name options.append((name, name.capitalize() )) cri = SelectField(u'By',validators=[DataRequired()], choices=options) submit = SubmitField('Search')

(3)文件index.html是一个HTML模板文件,用于渲染Web应用的主页。

<!doctype html> <html lang="en"> <head> <!-- Required meta tags --> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> <!-- Bootstrap CSS --> <link rel="stylesheet" href={{ url_for('static', filename='css/bootstrap.min.css') }}> <link rel="stylesheet" href={{ url_for('static', filename='css/index.css') }}> <body> <div class="jumbotron jumbotron-fluid" style="background-image: url({{ url_for('static', filename='conn.jpg') }}); background-size: cover;"> <div class="container"> <div class="back" style="background-image: url({{ url_for('static', filename='back.png') }})"> <h1 class="display-4">Connecting Flights</h1> <p class="lead">Shortest path through Floyd-Warshall algorithm</p> </div> </div> </div> <div class="row"> <div class="container"> <form method="POST" action=""> <div class="form-row"> {{ form.hidden_tag() }} <div class="col"> {{ form.orig.label(class="form-control-lable") }} {{ form.orig(class="form-control") }} </div> <div class="col"> {{ form.dest.label(class="form-control-lable")}} {{ form.dest(class="form-control") }} </div> <div class="col"> {{ form.cri.label(class="form-control-lable")}} {{ form.cri(class="form-control") }} </select> </div> </div> <br> <div class="form-row" style="top-margin: 1rm"> <div class="col"> {{ form.submit(class="btn btn-outline-info") }} </div> </div> </form> </div> </div> <br> <div class="row"> <div class="container"> <ul class="list-group"> {%if total != ""%} <li class="list-group-item"> {%if result|length != 0%} <h3>Lowest {{total}}</h3> {%else%} <p>No data for this query</p> {%endif%} </li> {%endif%} {%for path in result%} <li class="list-group-item"> <div class="container"> <h2 style="color:darkblue">{{path["orig"]}}-{{path["dest"]}}</h2> <h4 style="color:grey">{{path["airline"]}} {{path["no"]}} </h4> <p>{{path["weight"]}}</p> </div> </li> {%endfor%} </ul> </div> </div> </body> </html>

在页面中包含了一个带有背景图的jumbotron,展示了"Connecting Flights"的标题和一个简短的描述。页面下方是一个表单,用于用户输入起点、目的地和路径优先标准,并有一个提交按钮。在表单下方,会展示用户查询结果,包括最短路径的航班信息。如果有结果,会显示最短路径的航班信息,包括起点、目的地、航空公司、航班号和权重信息(例如总飞行距离或耗时)。如图5-5所示。

图5-5 主页查询效果

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/1/5 10:55:30

Wallpaper Engine创意工坊下载工具:高效获取动态壁纸的完整指南

Wallpaper Engine创意工坊下载工具&#xff1a;高效获取动态壁纸的完整指南 【免费下载链接】Wallpaper_Engine 一个便捷的创意工坊下载器 项目地址: https://gitcode.com/gh_mirrors/wa/Wallpaper_Engine 在追求个性化桌面体验的时代&#xff0c;Wallpaper Engine以其丰…

作者头像 李华
网站建设 2026/1/5 10:55:09

免费光学材料数据库完整指南:从零基础到专业应用

免费光学材料数据库完整指南&#xff1a;从零基础到专业应用 【免费下载链接】refractiveindex.info-database Database of optical constants 项目地址: https://gitcode.com/gh_mirrors/re/refractiveindex.info-database 在光学设计和材料研究领域&#xff0c;准确的…

作者头像 李华
网站建设 2026/1/5 10:54:11

终极QQ空间备份指南:3步搞定所有回忆数据

终极QQ空间备份指南&#xff1a;3步搞定所有回忆数据 【免费下载链接】GetQzonehistory 获取QQ空间发布的历史说说 项目地址: https://gitcode.com/GitHub_Trending/ge/GetQzonehistory 还在为QQ空间里那些珍贵的青春回忆可能丢失而担忧吗&#xff1f;那些记录着青春足迹…

作者头像 李华
网站建设 2026/1/5 10:53:55

3分钟上手ipget:无需配置的分布式下载神器

3分钟上手ipget&#xff1a;无需配置的分布式下载神器 【免费下载链接】ipget Retrieve files over IPFS and save them locally. 项目地址: https://gitcode.com/gh_mirrors/ip/ipget 在当今数字化时代&#xff0c;文件获取方式正在经历革命性变革。ipget作为一款专为I…

作者头像 李华
网站建设 2026/1/5 10:53:53

番茄小说下载器从零上手:3分钟快速入门指南

番茄小说下载器从零上手&#xff1a;3分钟快速入门指南 【免费下载链接】Tomato-Novel-Downloader 番茄小说下载器不精简版 项目地址: https://gitcode.com/gh_mirrors/to/Tomato-Novel-Downloader 番茄小说下载器是一款功能强大的电子书获取工具&#xff0c;能够将网络…

作者头像 李华