基于搜索算法解决罗马尼亚度假问题(利用pyside2实现图形化界面)

界面功能展示

主界面

图形化界面展示

贪心法:

图形化界面展示

A*算法

A*

代码构思

ui_widget.py 作为主界面,paint_widget.py作为绘图界面,paintFunctions.py作为绘图函数。
总体架构是:ui_widget.py 调用 paint_widget.py 调用 paintFunctions.py
paintFunctions.py 返回各种搜索算法的close表给 paint_widget.py 进行绘图再呈现到 ui_widget.py

主界面

ui_widget.py主界面利用 pyside2 自带的 designer程序自动生成主界面代码(自己用designer设计生成的代码):

添加了绑定事件按钮等等东西,代码中带有注释的都是添加的。

# -*- coding: utf-8 -*-
import random

################################################################################
## Form generated from reading UI file 'widgetlJxbcV.ui'
##
## Created by: Qt User Interface Compiler version 5.15.2
##
## WARNING! All changes made in this file will be lost when recompiling UI file!
################################################################################

from PySide2.QtCore import *
from PySide2.QtGui import *
from PySide2.QtWidgets import *
from paintFunctions import GraphSearch

from paint_widget import PaintWidget


class Ui_my_widget(QMainWindow):
    def __init__(self):
        super(Ui_my_widget, self).__init__()

        self.setupUi(self)
        self.retranslateUi(self)
        self.slot_init()

    def setupUi(self, my_widget):
        if not my_widget.objectName():
            my_widget.setObjectName(u"my_widget")
        my_widget.resize(685, 642)
        my_widget.setFixedSize(650, 650)
        self.paint_widget = PaintWidget()
        self.paint_widget.setParent(my_widget)
        self.paint_widget.setObjectName(u"paint_widget")
        self.checkBox_A = QCheckBox(self.paint_widget)
        self.checkBox_A.setObjectName(u"checkBox_17")
        self.checkBox_A.setGeometry(QRect(92, 488, 30, 20))
        self.checkBox_B = QCheckBox(self.paint_widget)
        self.checkBox_B.setObjectName(u"checkBox_3")
        self.checkBox_B.setGeometry(QRect(401, 323, 28, 20))
        self.checkBox_C = QCheckBox(self.paint_widget)
        self.checkBox_C.setObjectName(u"checkBox_4")
        self.checkBox_C.setGeometry(QRect(254, 284, 28, 20))
        self.checkBox_D = QCheckBox(self.paint_widget)
        self.checkBox_D.setObjectName(u"checkBox_9")
        self.checkBox_D.setGeometry(QRect(166, 295, 28, 20))
        self.checkBox_E = QCheckBox(self.paint_widget)
        self.checkBox_E.setObjectName(u"checkBox_2")
        self.checkBox_E.setGeometry(QRect(563, 289, 28, 20))
        self.checkBox_F = QCheckBox(self.paint_widget)
        self.checkBox_F.setObjectName(u"checkBox_13")
        self.checkBox_F.setGeometry(QRect(306, 445, 28, 20))
        self.checkBox_G = QCheckBox(self.paint_widget)
        self.checkBox_G.setObjectName(u"checkBox_10")
        self.checkBox_G.setGeometry(QRect(376, 266, 28, 20))
        self.checkBox_H = QCheckBox(self.paint_widget)
        self.checkBox_H.setObjectName(u"checkBox_8")
        self.checkBox_H.setGeometry(QRect(535, 346, 28, 20))
        self.checkBox_I = QCheckBox(self.paint_widget)
        self.checkBox_I.setObjectName(u"checkBox_6")
        self.checkBox_I.setGeometry(QRect(474, 502, 28, 20))

        self.checkBox_L = QCheckBox(self.paint_widget)
        self.checkBox_L.setObjectName(u"checkBox_12")
        self.checkBox_L.setGeometry(QRect(166, 375, 28, 20))
        self.checkBox_M = QCheckBox(self.paint_widget)
        self.checkBox_M.setObjectName(u"checkBox_7")
        self.checkBox_M.setGeometry(QRect(169, 335, 28, 20))
        self.checkBox_N = QCheckBox(self.paint_widget)
        self.checkBox_N.setObjectName(u"checkBox_16")
        self.checkBox_N.setGeometry(QRect(407, 533, 28, 20))
        self.checkBox_O = QCheckBox(self.paint_widget)
        self.checkBox_O.setObjectName(u"checkBox_15")
        self.checkBox_O.setGeometry(QRect(132, 567, 28, 20))
        self.checkBox_P = QCheckBox(self.paint_widget)
        self.checkBox_P.setObjectName(u"checkBox_5")
        self.checkBox_P.setGeometry(QRect(321, 364, 28, 20))
        self.checkBox_R = QCheckBox(self.paint_widget)
        self.checkBox_R.setObjectName(u"checkBox_14")
        self.checkBox_R.setGeometry(QRect(234, 406, 28, 20))
        self.checkBox_S = QCheckBox(self.paint_widget)
        self.checkBox_S.setObjectName(u"checkBox_11")
        self.checkBox_S.setGeometry(QRect(208, 453, 28, 20))
        self.checkBox_T = QCheckBox(self.paint_widget)
        self.checkBox_T.setObjectName(u"checkBox_20")
        self.checkBox_T.setGeometry(QRect(95, 406, 30, 20))
        self.checkBox_U = QCheckBox(self.paint_widget)
        self.checkBox_U.setObjectName(u"checkBox_19")
        self.checkBox_U.setGeometry(QRect(457, 346, 30, 20))
        self.checkBox_V = QCheckBox(self.paint_widget)
        self.checkBox_V.setObjectName(u"checkBox_18")
        self.checkBox_V.setGeometry(QRect(510, 440, 30, 20))
        self.checkBox_Z = QCheckBox(self.paint_widget)
        self.checkBox_Z.setObjectName(u"checkBox_21")
        self.checkBox_Z.setGeometry(QRect(109, 527, 30, 20))

        self.layoutWidget = QWidget(my_widget)
        self.layoutWidget.setObjectName(u"layoutWidget")
        self.layoutWidget.setGeometry(QRect(41, 6, 569, 231))
        self.verticalLayout = QVBoxLayout(self.layoutWidget)
        self.verticalLayout.setObjectName(u"verticalLayout")
        self.verticalLayout.setContentsMargins(0, 0, 0, 0)
        self.horizontalLayout = QHBoxLayout()
        self.horizontalLayout.setObjectName(u"horizontalLayout")
        self.btn_deepth = QPushButton(self.layoutWidget)
        self.btn_deepth.setObjectName(u"btn_deepth")
        self.btn_deepth.setMinimumSize(QSize(40, 40))
        self.btn_deepth.setMaximumSize(QSize(100, 40))

        self.horizontalLayout.addWidget(self.btn_deepth)

        self.btn_width = QPushButton(self.layoutWidget)
        self.btn_width.setObjectName(u"btn_width")
        self.btn_width.setMinimumSize(QSize(40, 40))
        self.btn_width.setMaximumSize(QSize(100, 40))

        self.horizontalLayout.addWidget(self.btn_width)

        self.btn_greedy = QPushButton(self.layoutWidget)
        self.btn_greedy.setObjectName(u"btn_greedy")
        self.btn_greedy.setMinimumSize(QSize(40, 40))
        self.btn_greedy.setMaximumSize(QSize(100, 40))

        self.horizontalLayout.addWidget(self.btn_greedy)

        self.btn_Astar = QPushButton(self.layoutWidget)
        self.btn_Astar.setObjectName(u"btn_Astar")
        self.btn_Astar.setMinimumSize(QSize(40, 40))
        self.btn_Astar.setMaximumSize(QSize(100, 40))

        self.horizontalLayout.addWidget(self.btn_Astar)

        self.btn_clear = QPushButton(self.layoutWidget)
        self.btn_clear.setObjectName(u"btn_clear")
        self.btn_clear.setMinimumSize(QSize(40, 40))
        self.btn_clear.setMaximumSize(QSize(100, 40))

        self.horizontalLayout.addWidget(self.btn_clear)

        self.verticalLayout.addLayout(self.horizontalLayout)

        self.verticalSpacer = QSpacerItem(20, 40, QSizePolicy.Minimum, QSizePolicy.Expanding)

        self.verticalLayout.addItem(self.verticalSpacer)

        self.horizontalLayout_2 = QHBoxLayout()
        self.horizontalLayout_2.setObjectName(u"horizontalLayout_2")
        self.listView_3 = QTextBrowser(self.layoutWidget)
        self.listView_3.setObjectName(u"listView_3")

        self.horizontalLayout_2.addWidget(self.listView_3)

        self.horizontalSpacer = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum)

        self.horizontalLayout_2.addItem(self.horizontalSpacer)

        self.listView_2 = QTextBrowser(self.layoutWidget)
        self.listView_2.setObjectName(u"listView_2")

        self.horizontalLayout_2.addWidget(self.listView_2)

        self.verticalLayout.addLayout(self.horizontalLayout_2)

        # 获取所有的checkbox
        self.list = self.paint_widget.findChildren(QCheckBox)
        # 计数器 保证每次只能两个
        self.count = 0

        self.retranslateUi(my_widget)

        QMetaObject.connectSlotsByName(my_widget)

    # setupUi

    def retranslateUi(self, my_widget):
        my_widget.setWindowTitle(QCoreApplication.translate("my_widget", u"罗马尼亚度假问题", None))
        self.checkBox_I.setText(QCoreApplication.translate("my_widget", u"I", None))
        self.checkBox_L.setText(QCoreApplication.translate("my_widget", u"L", None))
        self.checkBox_V.setText(QCoreApplication.translate("my_widget", u"V", None))
        self.checkBox_B.setText(QCoreApplication.translate("my_widget", u"B", None))
        self.checkBox_Z.setText(QCoreApplication.translate("my_widget", u"Z", None))
        self.checkBox_M.setText(QCoreApplication.translate("my_widget", u"M", None))
        self.checkBox_E.setText(QCoreApplication.translate("my_widget", u"E", None))
        self.checkBox_G.setText(QCoreApplication.translate("my_widget", u"G", None))
        self.checkBox_C.setText(QCoreApplication.translate("my_widget", u"C", None))
        self.checkBox_S.setText(QCoreApplication.translate("my_widget", u"S", None))
        self.checkBox_O.setText(QCoreApplication.translate("my_widget", u"O", None))
        self.checkBox_D.setText(QCoreApplication.translate("my_widget", u"D", None))
        self.checkBox_P.setText(QCoreApplication.translate("my_widget", u"P", None))
        self.btn_width.setText(QCoreApplication.translate("my_widget", u"\u5e7f\u5ea6\u4f18\u5148\u641c\u7d22", None))
        self.btn_deepth.setText(QCoreApplication.translate("my_widget", u"\u6df1\u5ea6\u4f18\u5148\u641c\u7d22", None))
        self.btn_greedy.setText(QCoreApplication.translate("my_widget", u"\u8d2a\u5a6a\u7b97\u6cd5", None))
        self.btn_Astar.setText(QCoreApplication.translate("my_widget", u"A*\u7b97\u6cd5", None))
        self.btn_clear.setText(QCoreApplication.translate("my_widget", u"\u6e05\u9664", None))
        self.checkBox_N.setText(QCoreApplication.translate("my_widget", u"N", None))
        self.checkBox_F.setText(QCoreApplication.translate("my_widget", u"F", None))
        self.checkBox_A.setText(QCoreApplication.translate("my_widget", u"A", None))
        self.checkBox_H.setText(QCoreApplication.translate("my_widget", u"H", None))
        self.checkBox_T.setText(QCoreApplication.translate("my_widget", u"T", None))
        self.checkBox_R.setText(QCoreApplication.translate("my_widget", u"R", None))
        self.checkBox_U.setText(QCoreApplication.translate("my_widget", u"U", None))

    def slot_init(self):
        # 绑定事件
        self.btn_width.clicked.connect(lambda: self.BFS())
        self.btn_deepth.clicked.connect(lambda: self.DFS())
        self.btn_greedy.clicked.connect(lambda: self.greedySearch())
        self.btn_Astar.clicked.connect(lambda: self.AstarSearch())
        self.btn_clear.clicked.connect(lambda: self.clearAll())
        for i in self.list:
            i.clicked.connect(lambda: self.checkBtn())

    def BFS(self):
        # self.findStartAndEnd()
        if self.paint_widget.start == "" or self.paint_widget.end == "":
            msg_box = QMessageBox(QMessageBox.Warning, 'Warning', '请先选择起点和终点!')
            msg_box.exec_()
            return
        self.paint_widget.gs = GraphSearch(self.paint_widget.start,
                                           self.paint_widget.end)  # 第一个参数是起点城市名的首字母 第二个是终点城市名的首字母
        self.paint_widget.gs.constructGraph()  # 构建城市图
        self.paint_widget.graph = self.paint_widget.gs.graph
        self.paint_widget.gs.widthFirstSearch()

        self.paint_widget.type = 0
        self.paint_widget.update()
        self.printPath(self.paint_widget.gs.close_width)
        pass

    def DFS(self):
        # self.findStartAndEnd()
        if self.paint_widget.start == "" or self.paint_widget.end == "":
            msg_box = QMessageBox(QMessageBox.Warning, 'Warning', '请先选择起点和终点!')
            msg_box.exec_()
            return
        self.paint_widget.gs = GraphSearch(self.paint_widget.start,
                                           self.paint_widget.end)  # 第一个参数是起点城市名的首字母 第二个是终点城市名的首字母
        self.paint_widget.gs.constructGraph()  # 构建城市图
        self.paint_widget.graph = self.paint_widget.gs.graph
        self.paint_widget.gs.deepFirstSearch()
        self.paint_widget.type = 1
        self.paint_widget.update()
        self.printPath(self.paint_widget.gs.close_deepth)
        pass

    def greedySearch(self):
        if self.paint_widget.start == "" or self.paint_widget.end == "":
            msg_box = QMessageBox(QMessageBox.Warning, 'Warning', '请先选择起点和终点!')
            msg_box.exec_()
            return
        # self.findStartAndEnd()
        self.paint_widget.gs = GraphSearch(self.paint_widget.start,
                                           self.paint_widget.end)  # 第一个参数是起点城市名的首字母 第二个是终点城市名的首字母
        self.paint_widget.gs.constructGraph()  # 构建城市图
        self.paint_widget.graph = self.paint_widget.gs.graph
        self.paint_widget.gs.greedSearch()
        self.paint_widget.type = 2
        self.paint_widget.update()
        self.printPath(self.paint_widget.gs.close_greed)
        pass

    def AstarSearch(self):
        if self.paint_widget.start == "" or self.paint_widget.end == "":
            msg_box = QMessageBox(QMessageBox.Warning, 'Warning', '请先选择起点和终点!')
            msg_box.exec_()
            return
        self.paint_widget.gs = GraphSearch(self.paint_widget.start,
                                           self.paint_widget.end)  # 第一个参数是起点城市名的首字母 第二个是终点城市名的首字母
        self.paint_widget.gs.constructGraph()  # 构建城市图
        self.paint_widget.graph = self.paint_widget.gs.graph
        self.paint_widget.gs.AstarAlgorithm()
        self.paint_widget.type = 3
        self.paint_widget.update()
        self.printPath(self.paint_widget.gs.close_Astar)

        pass

    def clearAll(self):
        self.paint_widget.type = -1
        self.paint_widget.update()
        self.listView_2.setText("")
        self.listView_3.setText("")
        self.count = 0
        for i in self.list:
            i.setChecked(False)
        pass

    def checkBtn(self):
        for i in self.list:
            if i.isChecked():
                if i.text() != self.paint_widget.start:
                    if self.count == 0:
                        self.paint_widget.start = i.text()
                    self.count += 1

                if i.text() != self.paint_widget.start and self.count == 2:
                    self.paint_widget.end = i.text()

        if self.count > 2:
            self.count = 0
            for i in self.list:
                i.setChecked(False)

            msg_box = QMessageBox(QMessageBox.Warning, 'Warning', '您选择的点太多!')
            msg_box.exec_()
            self.clearAll()
            return

    # def findStartAndEnd(self):
    #     count = 0
    #     for i in self.list:
    #         if i.isChecked():
    #             if count == 0:
    #                 self.paint_widget.start = i.text()
    #                 count = 1
    #
    #                 continue
    #             elif count == 1:
    #                 self.paint_widget.end = i.text()

    def printPath(self, close):
        endNode = close[-1]
        path = [endNode.name]
        # 从终点向前找
        while path[-1] != self.paint_widget.gs.start:
            if endNode.prev:
                path.append(endNode.prev.name)
                endNode = endNode.prev
        path.reverse()
        self.listView_2.setText("搜索路径:" + str(path))
        close1 = []
        for i in close:
            close1.append(i.name)
        self.listView_3.setText("close表:" + str(close1))


if __name__ == "__main__":
    import sys

    app = QApplication(sys.argv)
    ui = Ui_my_widget()
    ui.show()
    sys.exit(app.exec_())

绘图界面 paint_widget.py

import functools
import math
import matplotlib.pyplot as plt
from collections import deque

from Graph import Graph, Node
from test import Test


class GraphSearch():
    def __init__(self, start='A', end='B'):
        self.start = start
        self.end = end
        # cities是一个字典 第一个表示城市坐标,第二个list表示相邻城市及其路径权重
        self.cities = {'A': [(91, 492), [['Z', 75], ['T', 118], ['S', 140]]],
                       'B': [(400, 327), [['U', 85], ['G', 90], ['P', 101], ['F', 211]]],
                       'C': [(253, 288), [['D', 120], ['P', 138], ['R', 146]]],
                       'D': [(165, 299), [['M', 75], ['C', 120]]],
                       'E': [(562, 293), [['H', 86]]],
                       'F': [(305, 449), [['S', 99], ['B', 211]]],
                       'G': [(375, 270), [['B', 90]]],
                       'H': [(534, 350), [['E', 86], ['U', 98]]],
                       'I': [(473, 506), [['N', 87], ['V', 92]]],
                       'L': [(165, 379), [['M', 70], ['T', 111]]],
                       'M': [(168, 339), [['L', 70], ['D', 75]]],
                       'N': [(406, 537), [['I', 87]]],
                       'O': [(131, 571), [['Z', 71], ['S', 151]]],
                       'P': [(320, 368), [['R', 97], ['B', 101], ['C', 138]]],
                       'R': [(233, 410), [['S', 80], ['P', 97], ['C', 146]]],
                       'S': [(207, 457), [['R', 80], ['F', 99], ['A', 140], ['O', 151]]],
                       'T': [(94, 410), [['L', 111], ['A', 118]]],
                       'U': [(456, 350), [['B', 85], ['H', 98], ['V', 142]]],
                       'V': [(509, 444), [['I', 92], ['U', 142]]],
                       'Z': [(108, 531), [['O', 71], ['A', 75]]]}
        self.graph = Graph()
        self.close_width = []
        self.open_width = []
        self.close_deepth = 0
        self.close_greed = 0
        self.close_Astar = 0

    def constructGraph(self):
        for i in self.cities:
            # print(i, self.cities[i])
            node = Node()
            node.name = i
            node.point = self.cities[i][0]
            node.next = self.cities[i][1]  # 包含了 邻居城市与它们之间的距离
            self.graph.nodes.append(node)

    def printPath(self, close):
        endNode = close[-1]
        path = [endNode.name]
        # 从终点向前找
        while path[-1] != self.start:
            if endNode.prev:
                path.append(endNode.prev.name)
                endNode = endNode.prev
        path.reverse()

        print("搜索路径为:", path)

        print("close表为:", end=" ")
        for i in close:
            print(i.name, end=" ")
        print()

    def widthFirstSearch(self):
        startNode = Node()
        endNode = Node()
        for i in self.graph.nodes:
            if i.name == self.start:
                startNode = i
            if i.name == self.end:
                endNode = i
        close = []
        open = deque()
        open.append(startNode)
        while open:
            city = open.popleft()
            if city not in close:
                close.append(city)
                if city == endNode:
                    self.close_width = close
                    self.open_width = open
                    return
                for i in city.next:
                    for j in self.graph.nodes:
                        if i[0] == j.name:
                            i = j
                            if i not in open and i not in close:  # 结点i既不在open表 又不在close表,代表它没有被访问过,
                                i.prev = city
                                open.append(i)  # 只有没有被访问过的邻居,我们才将它加入open表中进行下一步操作
                            break

    def deepFirstSearch(self):
        startNode = Node()
        endNode = Node()
        for i in self.graph.nodes:
            if i.name == self.start:
                startNode = i
            if i.name == self.end:
                endNode = i

        close = []
        open = []  # 模拟堆栈
        open.append(startNode)
        while open:
            city = open.pop()
            if city not in close:
                close.append(city)
                if city == endNode:
                    self.close_deepth = close
                    return
                for i in reversed(city.next):
                    for j in self.graph.nodes:
                        if i[0] == j.name:
                            i = j
                            if i not in close:
                                i.prev = city
                                open.append(i)
                            break

    def greedSearch(self):
        startNode = Node()
        endNode = Node()
        for i in self.graph.nodes:
            if i.name == self.start:
                startNode = i
            if i.name == self.end:
                endNode = i
        close = []
        open = deque()
        open.append(startNode)
        while open:
            open = sorted(open, key=functools.cmp_to_key(self.compareValue))
            # open.sort(key=functools.cmp_to_key(self.compareValue))
            city = open.pop()
            if city not in close:
                close.append(city)
                if city == endNode:
                    # print("\n贪婪搜索路径为:")
                    # self.printPath(close)
                    # print("贪婪搜索close表为:")
                    # for i in close:
                    #     print(i.name, end=" ")
                    # print("\n搜索总代价为:", close[-1].gn)
                    self.close_greed = close
                    return
                for i in city.next:
                    for j in self.graph.nodes:
                        if i[0] == j.name:
                            cost = i[1]
                            i = j
                            if i not in open and i not in close:
                                i.prev = city  # 更新前置结点之前判断是否路径更佳,而不是简单的判断是否被访问
                                open.append(i)  # append是浅拷贝
                                while i.prev:
                                    for j in i.next:
                                        if j[0] == i.prev.name:
                                            i.gn = j[1] + i.prev.gn
                                            i = i.prev
                                            break
                            elif i.gn > (city.gn + cost):
                                i.prev = city  # 更新前置结点之前判断是否路径更佳,而不是简单的判断是否被访问
                                open.append(i)  # append是浅拷贝
                                while i.prev:
                                    for j in i.next:
                                        if j[0] == i.prev.name:
                                            i.gn = j[1] + i.prev.gn
                                            i = i.prev
                                            break
                            break

    def AstarAlgorithm(self):
        # 计算城市图每个点到终点城市的距离,获取h(n):节点n距离终点的预计代价,也就是A*算法的启发函数
        distance = {}
        startNode = Node()
        endNode = Node()
        for i in self.graph.nodes:
            distance[i.name] = math.sqrt(pow(i.point[0] - self.cities[self.end][0][0], 2) + \
                                         pow(i.point[1] - self.cities[self.end][0][1], 2))
            if i.name == self.start:
                startNode = i
            if i.name == self.end:
                endNode = i
        close = []
        open = deque()
        open.append(startNode)
        while open:
            # 对open表排序
            open = deque(sorted(open, key=functools.cmp_to_key(self.compareNode)))
            city = open.popleft()
            # city结点不在close里面则扩展
            if city not in close:
                close.append(city)
                if city == endNode:
                    self.close_Astar = close
                    return
                for i in city.next:
                    for j in self.graph.nodes:
                        if i[0] == j.name:
                            cost = i[1]
                            i = j
                            if i not in open and i not in close:
                                # 判断是否被访问
                                i.prev = city
                                # print(f"{i.name}<-{city.name}")
                                open.append(i)
                                # 计算当前结点到起点已经走过的代价并且加上欧式距离 获取f(n)=g(n)+h(n)
                                # g(n):节点n距离起点的代价 这个代价是已知的,只需要把走过的路花费的代价加起来

                                while i.prev:
                                    for j in i.next:
                                        if j[0] == i.prev.name:
                                            i.gn = j[1] + i.prev.gn
                                            i.fn = i.gn + distance[i.name]
                                            i = i.prev
                                            break
                            elif i.gn > (city.gn + cost):
                                i.prev = city  # 更新前置结点之前判断是否路径更佳,而不是简单的判断是否被访问
                                # print(f"{i.name}<-{city.name}")
                                open.append(i)  # append是浅拷贝
                                while i.prev:
                                    for j in i.next:
                                        if j[0] == i.prev.name:
                                            i.gn = j[1] + i.prev.gn
                                            i.fn = i.gn + distance[i.name]
                                            i = i.prev
                                            break
                            break

    def compareValue(self, node1, node2):
        # 小的排右边
        if node1.gn < node2.gn:
            return 1
        elif node1.gn > node2.gn:
            return -1
        else:
            return 0

    def compareNode(self, node1, node2):
        if node1.fn > node2.fn:
            return 1
        elif node1.fn < node2.fn:
            return -1
        else:
            # fn相等按gn算
            if node1.gn > node2.gn:
                return 1
            else:
                return -1

功能界面 paintFunction.py

import functools
import math
import matplotlib.pyplot as plt
from collections import deque

from Graph import Graph, Node
from test import Test


class GraphSearch():
    def __init__(self, start='A', end='B'):
        self.start = start
        self.end = end
        # cities是一个字典 第一个表示城市坐标,第二个list表示相邻城市及其路径权重
        self.cities = {'A': [(91, 492), [['Z', 75], ['T', 118], ['S', 140]]],
                       'B': [(400, 327), [['U', 85], ['G', 90], ['P', 101], ['F', 211]]],
                       'C': [(253, 288), [['D', 120], ['P', 138], ['R', 146]]],
                       'D': [(165, 299), [['M', 75], ['C', 120]]],
                       'E': [(562, 293), [['H', 86]]],
                       'F': [(305, 449), [['S', 99], ['B', 211]]],
                       'G': [(375, 270), [['B', 90]]],
                       'H': [(534, 350), [['E', 86], ['U', 98]]],
                       'I': [(473, 506), [['N', 87], ['V', 92]]],
                       'L': [(165, 379), [['M', 70], ['T', 111]]],
                       'M': [(168, 339), [['L', 70], ['D', 75]]],
                       'N': [(406, 537), [['I', 87]]],
                       'O': [(131, 571), [['Z', 71], ['S', 151]]],
                       'P': [(320, 368), [['R', 97], ['B', 101], ['C', 138]]],
                       'R': [(233, 410), [['S', 80], ['P', 97], ['C', 146]]],
                       'S': [(207, 457), [['R', 80], ['F', 99], ['A', 140], ['O', 151]]],
                       'T': [(94, 410), [['L', 111], ['A', 118]]],
                       'U': [(456, 350), [['B', 85], ['H', 98], ['V', 142]]],
                       'V': [(509, 444), [['I', 92], ['U', 142]]],
                       'Z': [(108, 531), [['O', 71], ['A', 75]]]}
        self.graph = Graph()
        self.close_width = []
        self.open_width = []
        self.close_deepth = 0
        self.close_greed = 0
        self.close_Astar = 0

    def constructGraph(self):
        for i in self.cities:
            # print(i, self.cities[i])
            node = Node()
            node.name = i
            node.point = self.cities[i][0]
            node.next = self.cities[i][1]  # 包含了 邻居城市与它们之间的距离
            self.graph.nodes.append(node)

    def printPath(self, close):
        endNode = close[-1]
        path = [endNode.name]
        # 从终点向前找
        while path[-1] != self.start:
            if endNode.prev:
                path.append(endNode.prev.name)
                endNode = endNode.prev
        path.reverse()

        print("搜索路径为:", path)

        print("close表为:", end=" ")
        for i in close:
            print(i.name, end=" ")
        print()

    def widthFirstSearch(self):
        startNode = Node()
        endNode = Node()
        for i in self.graph.nodes:
            if i.name == self.start:
                startNode = i
            if i.name == self.end:
                endNode = i
        close = []
        open = deque()
        open.append(startNode)
        while open:
            city = open.popleft()
            if city not in close:
                close.append(city)
                if city == endNode:
                    self.close_width = close
                    self.open_width = open
                    return
                for i in city.next:
                    for j in self.graph.nodes:
                        if i[0] == j.name:
                            i = j
                            if i not in open and i not in close:  # 结点i既不在open表 又不在close表,代表它没有被访问过,
                                i.prev = city
                                open.append(i)  # 只有没有被访问过的邻居,我们才将它加入open表中进行下一步操作
                            break

    def deepFirstSearch(self):
        startNode = Node()
        endNode = Node()
        for i in self.graph.nodes:
            if i.name == self.start:
                startNode = i
            if i.name == self.end:
                endNode = i

        close = []
        open = []  # 模拟堆栈
        open.append(startNode)
        while open:
            city = open.pop()
            if city not in close:
                close.append(city)
                if city == endNode:
                    self.close_deepth = close
                    return
                for i in reversed(city.next):
                    for j in self.graph.nodes:
                        if i[0] == j.name:
                            i = j
                            if i not in close:
                                i.prev = city
                                open.append(i)
                            break

    def greedSearch(self):
        startNode = Node()
        endNode = Node()
        for i in self.graph.nodes:
            if i.name == self.start:
                startNode = i
            if i.name == self.end:
                endNode = i
        close = []
        open = deque()
        open.append(startNode)
        while open:
            open = sorted(open, key=functools.cmp_to_key(self.compareValue))
            # open.sort(key=functools.cmp_to_key(self.compareValue))
            city = open.pop()
            if city not in close:
                close.append(city)
                if city == endNode:
                    # print("\n贪婪搜索路径为:")
                    # self.printPath(close)
                    # print("贪婪搜索close表为:")
                    # for i in close:
                    #     print(i.name, end=" ")
                    # print("\n搜索总代价为:", close[-1].gn)
                    self.close_greed = close
                    return
                for i in city.next:
                    for j in self.graph.nodes:
                        if i[0] == j.name:
                            cost = i[1]
                            i = j
                            if i not in open and i not in close:
                                i.prev = city  # 更新前置结点之前判断是否路径更佳,而不是简单的判断是否被访问
                                open.append(i)  # append是浅拷贝
                                while i.prev:
                                    for j in i.next:
                                        if j[0] == i.prev.name:
                                            i.gn = j[1] + i.prev.gn
                                            i = i.prev
                                            break
                            elif i.gn > (city.gn + cost):
                                i.prev = city  # 更新前置结点之前判断是否路径更佳,而不是简单的判断是否被访问
                                open.append(i)  # append是浅拷贝
                                while i.prev:
                                    for j in i.next:
                                        if j[0] == i.prev.name:
                                            i.gn = j[1] + i.prev.gn
                                            i = i.prev
                                            break
                            break

    def AstarAlgorithm(self):
        # 计算城市图每个点到终点城市的距离,获取h(n):节点n距离终点的预计代价,也就是A*算法的启发函数
        distance = {}
        startNode = Node()
        endNode = Node()
        for i in self.graph.nodes:
            distance[i.name] = math.sqrt(pow(i.point[0] - self.cities[self.end][0][0], 2) + \
                                         pow(i.point[1] - self.cities[self.end][0][1], 2))
            if i.name == self.start:
                startNode = i
            if i.name == self.end:
                endNode = i
        close = []
        open = deque()
        open.append(startNode)
        while open:
            # 对open表排序
            open = deque(sorted(open, key=functools.cmp_to_key(self.compareNode)))
            city = open.popleft()
            # city结点不在close里面则扩展
            if city not in close:
                close.append(city)
                if city == endNode:
                    self.close_Astar = close
                    return
                for i in city.next:
                    for j in self.graph.nodes:
                        if i[0] == j.name:
                            cost = i[1]
                            i = j
                            if i not in open and i not in close:
                                # 判断是否被访问
                                i.prev = city
                                # print(f"{i.name}<-{city.name}")
                                open.append(i)
                                # 计算当前结点到起点已经走过的代价并且加上欧式距离 获取f(n)=g(n)+h(n)
                                # g(n):节点n距离起点的代价 这个代价是已知的,只需要把走过的路花费的代价加起来

                                while i.prev:
                                    for j in i.next:
                                        if j[0] == i.prev.name:
                                            i.gn = j[1] + i.prev.gn
                                            i.fn = i.gn + distance[i.name]
                                            i = i.prev
                                            break
                            elif i.gn > (city.gn + cost):
                                i.prev = city  # 更新前置结点之前判断是否路径更佳,而不是简单的判断是否被访问
                                # print(f"{i.name}<-{city.name}")
                                open.append(i)  # append是浅拷贝
                                while i.prev:
                                    for j in i.next:
                                        if j[0] == i.prev.name:
                                            i.gn = j[1] + i.prev.gn
                                            i.fn = i.gn + distance[i.name]
                                            i = i.prev
                                            break
                            break

    def compareValue(self, node1, node2):
        # 小的排右边
        if node1.gn < node2.gn:
            return 1
        elif node1.gn > node2.gn:
            return -1
        else:
            return 0

    def compareNode(self, node1, node2):
        if node1.fn > node2.fn:
            return 1
        elif node1.fn < node2.fn:
            return -1
        else:
            # fn相等按gn算
            if node1.gn > node2.gn:
                return 1
            else:
                return -1

代码开源

以上全部代码都开源在了github上:https://github.com/Infinityay/SearchAlgorithm. 如果对你有帮助,希望能给我点个Star, 蟹蟹!
当然你也可以去我的个人博客看看 生有涯知无涯

  • 1
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值