Путь между двумя городами с использованием BFS в java

Я пытаюсь создать структуру данных для хранения содержимого карты, такой как информация о дорогах и городах. Я использую связанный список вершин/городов, и когда вершина/город создается, он создает еще один связанный список ребер в своем конструкторе. Мне нужен алгоритм для поиска кратчайшего пути между двумя случайными городами. Мой модифицированный алгоритм BFS не работает абсолютно нормально. Алгоритм:

public void getPath(String from, String to) {
        Queue<cVertex> Q = new LinkedList<cVertex>();
        List<cVertex> visited = new LinkedList<cVertex>();
        cVertex l = null;
        for (int i = 0; i < c.size(); i++) {
            if (c.get(i).cityName.equals(to)) {
                l = c.get(i);
                break;
            }
        }
        for (cVertex a : c) {
            if (a.cityName.equals(from)) {
                // System.out.println(a.cityName);
                Q.add(a);
                System.out.println("\nPath from " + a.cityName + " to "
                        + l.cityName);
                break;
            }
        }
        while (!Q.isEmpty()) {
            cVertex u = Q.remove();
            System.out.print(u.cityName);
            if (u.cityName.equals(l.cityName)) {
                return;
            }
            System.out.print("-->");
            visited.add(u);
            for (cVertex w : c) {
                for (Edge e : w.list) {
                    Edge n = e;
                    for (cVertex d : c) {
                        if (d.cityName.equals(e.to)) {
                            if (!visited.contains(d) && !Q.contains(d))
                                Q.add(d);
                        }
                    }
                }
            }
        }
    }

Мне нужна помощь! e.to означает, что это имя города, с которым связано это ребро.


person Faiqa Babar    schedule 22.12.2014    source источник
comment
Вы должны проверить, начинается ли ребро e в узле u, верно? Это кажется проблемой, я думаю, вы добавляете в Q гораздо больше узлов, чем хотите. Совет: Создайте список всех ребер на вашем графике. Ваш код будет намного читабельнее.   -  person vojta    schedule 22.12.2014
comment
Не работает - не полезное описание проблемы...   -  person Oliver Charlesworth    schedule 23.12.2014
comment
Вы можете погуглить BFS и найти псевдокод интересующего вас алгоритма, но вам придется перевести этот псевдокод на язык, который вы программируете, то есть на java в вашем случае. Кроме того, если ваши ребра имеют положительный вес, вы можете использовать алгоритм Дейкстры вместо BFS. Если некоторые из ваших ребер имеют положительный вес, а некоторые — отрицательный, вы можете использовать алгоритм Беллмана Форда или алгоритм Флойда Уоршалла вместо BFS.   -  person    schedule 27.08.2017


Ответы (1)


В своем вопросе вы должны указать, что "c" является статической переменной и представляет собой массив cVertex. Также покажите объявления и определения всех классов, которые вы используете в функции getPath, например: поля, свойства и методы классов cVertex и Edge также в вашем вопросе.

Я также хочу, чтобы вы объяснили мне, как в Java можно неявно преобразовать LinkedList в Queue и LinkedList в List. Я просто хочу знать. Мне интересно, потому что в C# все эти классы существуют в пространстве имен System.Collections.Generic, но вы не можете ни неявно, ни явно преобразовать LinkedList ни в Queue, ни в List.

Для С#:

Queue<cVertex> Q = new LinkedList<cVertex>();
List<cVertex> visited = new LinkedList<cVertex>();

Это написание запрещено и приводит к ошибкам, даже если класс cVertex был где-то объявлен и определен.

Но в любом случае ваш вопрос потрясающий и сложный.

К сожалению, я никогда не изучал Java, поэтому я не могу выложить вам свой код, который может быть неверен с точки зрения синтаксиса и грамматики, но все же я понял ваш умный вопрос и все же могу вам помочь, поэтому я просто расскажу вам свою идею и что вам нужно сделать, чтобы ответить на ваш вопрос.

Прежде всего удалите или, по крайней мере, исключите из своего проекта классы cVertex и Edge, которые вы используете внутри функции getPath, и добавьте новый класс. Назовите этот класс по имени «Город». Экземпляры этого класса должны хранить название города в виде строки и массива типа City. В этом массиве укажите все города, в которые можно добраться из this City по одной дороге, одному автобусу или даже пешком.

Следующим шагом будет создание экземпляров класса City до тех пор, пока вы не создадите все города страны на территории и сохраните их все в одном массиве. Если вам нравится называть этот массив "c", то вам разрешено. Конечно, я понимаю, что очень важно, чтобы этот массив был статическим, поэтому вы сможете использовать его во многих других функциях.

Теперь вам нужна функция, которая получает один параметр строки, который является названием города, и возвращает из статического массива ссылку на экземпляр города с этим названием.

Реализовать эту функцию очень просто. Вы просто выполняете цикл (перебираете) все города и проверяете, соответствует ли название текущего города в цикле for строковому параметру. Если да, то вернуть ссылку на текущий город в цикле for и функция автоматически остановится. Если город с таким названием не найден, то функция естественно возвращает null.

Вы можете вызвать эту функцию по имени «findCityByName».

Когда вы закончите работу с этой функцией, следующим шагом будет переименование функции getPath с «getPath» на «getPaths» (просто добавьте в конце букву «s») и изменение типа возвращаемого значения этой функции с void на Boolean. Это изменение хорошо, потому что возвращаемое значение этой функции будет информировать вас об успешном выполнении функции или об ошибке. Функция завершается ошибкой и возвращает false, если входные параметры недействительны. В противном случае функция завершается успешно и возвращает true.

Теперь добавьте три дополнительных очень необходимых параметра для того, чтобы эта функция работала и работала успешно.

Тем не менее, первые два параметра этой функции - это два названия двух городов, из которых вы хотите перейти, в виде строк, как вы указали в своем вопросе, но третий параметр должен быть списком типа City. В этом списке хранятся ссылки на все города, которые были посещены. Вы можете назвать этот параметр по имени «посетил». Ваша ошибка заключалась в том, что вы определили «посещение» как локальную переменную, а не как параметр.

Четвертый параметр также должен быть списком, но массивом типа City. Каждый массив в этом списке хранит только ссылки на все города, которые вы должны посетить в первую очередь, прежде чем вы достигнете целевого города из города, из которого вы вышли. Каждый массив в этом списке на самом деле является путем. Этот список содержит только все возможные пути достижения целевого города из "из" города, имена которых указаны в первых двух параметрах этой функции в виде строк. Этот список на самом деле содержит то, что вы ожидаете получить от этой функции.

Вы можете вызвать этот параметр по имени «пути».

Пятый параметр последний также должен быть списком типа Город, как и тип третьего параметра - "посещенный", но в этом списке хранятся ссылки на города, которые были посещены, а нет на все не как в параметре "посещенный" список. Это разница между параметром списка "посещенных" и этим параметром. Каждый раз, когда завершается вызов функции getPaths, последний элемент этого списка удаляется, а не как в параметре "посещенный" список. Список "посещенных" только добавляет к себе ссылки городов. Он никогда не удаляет их, но удаляет список в последнем параметре.

Вы можете назвать этот параметр другим именем "currentPath".

Обратите внимание, что вы должны вызывать эту функцию более одного раза внутри этой функции, чтобы это работало!

Эта функция должна быть рекурсивной!

Это все параметры этой функции, но очень важно всегда передавать последние три дополнительных параметра (3-й, 4-й и 5-й) как ссылки, а не как копии!!!

Функция точно не будет работать, если передать эти параметры копиями.

Теперь, наконец, приступим к переписыванию кода реализации этой функции.

В начале дважды вызовите функцию «findCityByName», чтобы города использовались для поиска всех возможных путей между ними. Если один из них или оба равны null, то return false немедленно остановить функцию, потому что вы не можете рассчитать путь между двумя городами, если один из них не существует или оба! Также функция не работает, потому что один из первых двух параметров или оба недействительны!

Если оба не равны нулю, то есть оба города существуют, тогда вы можете запустить цикл for для всех городов в массиве города "из", в который вы можете отправиться.

Если текущий город в цикле for является целевым городом, в который вы хотите отправиться, то выделите новый массив размером списка «currentPath» и скопируйте все ссылки из списка «currentPath» в этот массив, а затем добавьте его в « paths» и вернуть true, чтобы немедленно остановить текущую функцию, потому что путь был найден, а также параметры допустимы для текущей функции getPath.

В противном случае (если текущий город в цикле for не является целевым городом), добавьте в оба списка «visited» и «currentPath» ссылку на текущий город в цикле for и функцию recall getPaths и введите в первый параметр название текущего города в цикле for. Во 2-м, 3-м, 4-м и 5-м параметрах введите 2-й, 3-й, 4-й и 5-й параметры вызываемой текущей функции getPaths соответственно (выберите из названия). еще не забудьте передать 3-й, 4-й и 5-й параметры как ссылки, а не как копии, иначе функция не будет работать!

Все, что я сказал вам сделать в операторе else, происходит только в том случае, если список «посещенных» еще не содержит ссылки на текущий город в цикле for! В противном случае цикл продолжается до следующего города. Важно проверить это в первую очередь.

Наконец (после цикла for), если "currentPath" не пуст, то удалите последнюю ссылку только из "currentPath" и не из "visited" тоже!!!

Затем return true. Первые два параметра первого вызова функции getPaths допустимы. Функция может завершиться успешно.

Это все объявление, определение и реализация функции getPaths.

Обратите внимание, что каждый раз перед вызовом этой функции вы должны выделить память для трех списков, которые эта функция получает в последних трех параметрах. Прежде чем передавать эти списки по ссылке в эту функцию, назначьте их пустым конструктором, или конструктором без параметров, или конструктором, который сначала инициализирует новый список пустым. Это очень важно при первом вызове функции getPaths, чтобы все списки последних трех параметров были пустыми.

Когда первый вызов функции getPaths завершится и функция завершится успешно, только "currentPath" останется пустым. В противном случае только "посещенные" не останутся пустыми. В случае, если функция выполнена успешно и хотя бы один путь найден, то «пути» будут заполнены данными. Интересны только «пути», а не два других, потому что это ответ, который дает функция getPaths.

После того, как этот список путей готов, теперь вам нужно преобразовать его в jugged массив City, выделив новый массив размером с этот список и просто скопировав из этого списка все ссылки на этот массив.

Jugged array — это массив, тип которого также является массивом.

Теперь давайте определим новую функцию: getShortestPath

Эта функция получает только один параметр объединенного массива City и возвращает массив City.

На самом деле он получает пути и возвращает кратчайший путь. Алгоритм реализации этой функции очень прост.

Сначала определите новую локальную переменную: назовите «minSize», введите int и назначьте ей размер первого массива в массиве Jugged.

Затем определите еще одну новую локальную переменную: назовите «shortestPath», введите «массив городов» и установите ее как ссылку на первый массив в массиве Jugged.

Затем для цикла остальные массивы (начиная со второго массива до последнего) и если размер текущего массива меньше значения «minSize», то установить оба «minSize» на размер текущего массива в for loop и "shortPath" к ссылке текущего массива в цикле for.

Наконец, после цикла for верните «shortPath».

Если у вас много городов, и getPaths работает слишком много времени, то вы можете добавить размещение в класс City (координаты X и Y как дубли).

Затем удалите или прокомментируйте функцию getShortestPath и переименуйте функцию getPaths с "getPaths" на "getShortestPath", а также удалите или прокомментируйте параметр "paths" и весь код, относящийся к этому параметру. Также измените имя параметра «currentPath» на «path» и примените это изменение ко всем вхождениям в функции.

Также добавьте еще одно условие И в оператор if, который является оператором else в цикле for. В дополнительном условии проверьте, что текущий город в цикле for находится ближе всего к целевому городу. Чтобы знать, что вам нужно сначала перед циклом for добавить еще один цикл for ко всем городам в массиве from, рассчитайте и добавьте расстояния всех этих городов до целевого города в новый local список удваивается, что вы выделяете ранее. Затем объявите, определите и реализуйте функцию, которая возвращает наименьший двойник в списке двойников. Затем вызовите эту функцию, чтобы получить расстояние от ближайшего города до целевого города и сохранить его в новой локальной переменной: назовите «min» и введите double

В дополнительном условии, если рассчитанное расстояние от текущего города до целевого города приблизительно равно значению «min», то текущий город является ближайшим к целевому городу.

Также определите, объявите и реализуйте новую функцию, которая получает два параметра City и возвращает расстояние между ними как двойное, и используйте эту функцию, чтобы помочь вам.

Математическое выражение, которое вычисляет расстояние между двумя точками:

sqrt(pow(x2 - x1, 2) + pow(y2 - y1, 2)) или sqrt((x2 - x1)^2 + (y2 - y1)^2)

pow(a, b) = a^b, pow(n, 2) = n^2

Также в функции getShortestPath (которая когда-то вызывалась по имени "getPaths") удалите или прокомментируйте все строки, которые удаляют элементы из "пути" (которая когда-то называлась по имени "currentPath")

Путь к новому выделенному списку пуст перед вызовом функции getShortestPath, но после вызова и после того, как вы передаете его по ссылке, «путь» заполняется данными. Это должен быть кратчайший путь между городом «из» и городом «в».

Напишите комментарий и скажите, знаете ли вы C#, а не только Java.

Я знаю C#, поэтому, если вы скажете мне «да», я опубликую код на C#.

Вот и все. Надеюсь, это ответит на ваш вопрос. Попробуйте эту идею и оставьте комментарий, чтобы сказать мне, было ли это полезно.

УДАЧИ от меня! :D

person Community    schedule 23.12.2014
comment
Я опубликовал этот ужасный ДЛИННЫЙ ответ 2 года назад, когда я был новичком, который только что закончил среднюю школу по программированию. Это было до того, как я начал изучать информатику в колледже. Я не знаю, стоит ли удалять этот ответ, но я был удивлен, что он еще не получил отрицательных голосов. В любом случае, я скоро добавлю другой КОРОТКИЙ и СООТВЕТСТВУЮЩИЙ ответ на этот вопрос с моим новым опытом и моими знаниями в области компьютерных наук и в колледже. - person ; 27.08.2017
comment
@FarewellStackExchange Каждый в какой-то момент новичок.. Не будьте слишком строги к себе :D - person phil; 09.12.2017