Breadth-First Search statt einem Pfad, alle Pfade herausfinden

lennero

Bekanntes Mitglied
Hallo
Habe folgenden Code, der den kürzesten Pfad zwischen Zwei Knoten in einem 2D-Array herausfindet. Allerdings kann es vorkommen, dass es mehrere kürzeste Pfade zwischen den Knoten gibt, und ich brauche immer einen bestimmten, von der Aufgabenstellung verlangten Pfad.

Java:
        Node optimalNode = null;
        Queue<Node> nodes = new LinkedList<Node>();
        nodes.offer(new Node(startingPos, null));

        Set<Point> examinedPoints = new HashSet<Point>();
        

        while(!nodes.isEmpty())
        {
            Node current = nodes.poll();
            examinedPoints.add(current.point);
           
            //check if we found a target
            for(int i = 0; i < possibleTargets.size(); i++)
            {
                if(current.point.equals(possibleTargets.get(i)))
                {
                    optimalNode = current;
                    break;
                }
            }

            //add all relevant nodes in the vicinity (up, left, right, down) to the queue (the way the grid is set-up makes it impossible to be out of bounds here)
            if(grid[current.point.y - 1][current.point.x]== '.' && !examinedPoints.contains(new Point(current.point.x, current.point.y - 1)))
            {
                nodes.offer(new Node(new Point(current.point.x, current.point.y - 1), current));
            }
            if(grid[current.point.y][current.point.x - 1] == '.' && !examinedPoints.contains(new Point(current.point.x - 1, current.point.y)))
            {
                nodes.offer(new Node(new Point(current.point.x - 1, current.point.y), current));
            }
            if(grid[current.point.y][current.point.x + 1] == '.' && !examinedPoints.contains(new Point(current.point.x + 1, current.point.y)))
            {
                nodes.offer(new Node(new Point(current.point.x + 1, current.point.y), current));
            }
            if(grid[current.point.y + 1][current.point.x] == '.' && !examinedPoints.contains(new Point(current.point.x, current.point.y + 1)))
            {
                nodes.offer(new Node(new Point(current.point.x, current.point.y + 1), current));
            }
        }


und so sieht das Feld aus

Code:
#######
#.E...#
#...!.#
#..!G!#
#######

Hierbei muss ich das 'E' auf das nächstgelegene Ausrufezeichen transportieren. (ist nur ein kleines Beispiel, in der Aufgabe gibt es mehrere 'E' und 'G' Zeichen auf dem Feld). Falls zwei Ausrufezeichen dieselbe Distanz vom 'E' haben, muss das Ausrufezeichen gewählt werden, welches von links nach rechts gelesen als erstes vorkommt.

Der Pfad der für das Beispiel berechnet wird, ist folgender

Code:
#######
#.E...#
#.+++.#
#...G.#
#######

Ich möchte aber diesen Pfad haben

Code:
#######
#.E++.#
#...+.#
#...G.#
#######

Ich hab mir überlegt, dass jeder Knoten ja mehrere Eltern hat. In meinem Codebeispiel setze ich allerdings immer nur 1 Elternknoten. Dann müsste ich ja eine Liste von Eltern für jeden Knoten führen oder gibt es einen anderen Weg?
 

lennero

Bekanntes Mitglied
Du meinst von oben nach unten und von links nach rechts gelesen?

Unabhängig davon wird in deinen beiden Beispielen jeweils das gleiche ! angesteuert.

Ja, genau, allerdings soll auch der Pfad gewählt werden, welcher von oben, unten, links, rechts als erstes gelesen wird.

Was genau gemacht werden soll ist folgendes.

1. Suche nach allen 'G' Zeichen.
2. Überprüfe ob Stellen neben (links rechts oben unten) 'G' frei sind und speichere sie in einer Liste (possibleTargets im Code oben ).
3. Suche per BFS nach der nächstgelegenen Freien Stelle neben einem 'G'
4. Führe einen einzigen Schritt in Richtung des 'G' aus.

Das ist der Pfad der berechnet wird.

Code:
#######
#.E...#
#.+++.#
#...G.#
#######

Das ist auch richtig so, allerdings gibt es noch einen zweiten Pfad der genauso kurz ist und der erste Schritt auf diesem Pfad kommt von oben unten links rechts gelesen als erstes vor und ist in diesem Fall der "optimale" Pfad.

Code:
#######
#.E++.#
#...+.#
#...G.#
#######
 

httpdigest

Top Contributor
Dann verändere einfach die Priorität der (links, rechts, unten, oben)-Schritte. Da du ja anscheinend denjenigen Weg präferierst, der zuerst auf der Horizontalen (nach links oder rechts) geht, und erst _dann_ nach oben/unten, prüfe also zuerst solche Wege, die zuerst nach links/rechts gehen würden.
Z.B.:
Java:
import static java.util.stream.Stream.concat;
import static java.util.stream.Stream.of;
import static java.util.Arrays.asList;
import static java.util.Comparator.comparingInt;
import static java.util.stream.Collectors.toList;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.stream.Stream;
public class ShortestWay {
    private static class Point {
        public int x, y;
        Point(int x, int y) {
            this.x = x;
            this.y = y;
        }
        public boolean equals(Object obj) {
            Point pt = (Point) obj;
            return x == pt.x && y == pt.y;
        }
        public String toString() {
            return "(" + x + ", " + y + ")";
        }
    }
    private static Point pt(int x, int y) {
        return new Point(x, y);
    }
    private static Stream<Point> nextSteps(Point p) {
        return Arrays.stream(new Point[] {
            pt(p.x, p.y - 1), // up
            pt(p.x, p.y + 1), // down
            pt(p.x - 1, p.y), // left
            pt(p.x + 1, p.y)  // right
        });
    }
    public static Optional<List<Point>> way(char[][] m, Point start) {
        return way(m, start, asList(start));
    }
    private static Optional<List<Point>> way(char[][] m, Point pt, List<Point> way) {
        if (m[pt.y][pt.x] == '!')
            return Optional.of(way);
        return nextSteps(pt)
              .filter(p -> visitable(m, way, p))
              .map(p -> way(m, p, concat(way.stream(), of(p)).collect(toList())))
              .filter(Optional::isPresent)
              .map(Optional::get)
              .sorted(comparingInt(List::size))
              .findFirst();
    }
    private static boolean visitable(char[][] m, List<Point> way, Point p) {
        return !way.contains(p) &&
                p.y >= 0 && p.y < m.length &&
                p.x >= 0 && p.x < m[p.y].length
                && m[p.y][p.x] != '#';
    }

    // Test
    public static void main(String[] args) {
        char[][] m = {
            {'G', '.', '.', '.'},
            {'.', '.', '.', '.'},
            {'.', '.', '!', '.'},
            {'.', '.', '.', '.'},
        };
        way(m, pt(0, 0)).ifPresent(System.out::println);
    }
}
 

lennero

Bekanntes Mitglied
Dann verändere einfach die Priorität der (links, rechts, unten, oben)-Schritte. Da du ja anscheinend denjenigen Weg präferierst, der zuerst auf der Horizontalen (nach links oder rechts) geht, und erst _dann_ nach oben/unten, prüfe also zuerst solche Wege, die zuerst nach links/rechts gehen würden.
Z.B.:
Java:
import static java.util.stream.Stream.concat;
import static java.util.stream.Stream.of;
import static java.util.Arrays.asList;
import static java.util.Comparator.comparingInt;
import static java.util.stream.Collectors.toList;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.stream.Stream;
public class ShortestWay {
    private static class Point {
        public int x, y;
        Point(int x, int y) {
            this.x = x;
            this.y = y;
        }
        public boolean equals(Object obj) {
            Point pt = (Point) obj;
            return x == pt.x && y == pt.y;
        }
        public String toString() {
            return "(" + x + ", " + y + ")";
        }
    }
    private static Point pt(int x, int y) {
        return new Point(x, y);
    }
    private static Stream<Point> nextSteps(Point p) {
        return Arrays.stream(new Point[] {
            pt(p.x, p.y - 1), // up
            pt(p.x, p.y + 1), // down
            pt(p.x - 1, p.y), // left
            pt(p.x + 1, p.y)  // right
        });
    }
    public static Optional<List<Point>> way(char[][] m, Point start) {
        return way(m, start, asList(start));
    }
    private static Optional<List<Point>> way(char[][] m, Point pt, List<Point> way) {
        if (m[pt.y][pt.x] == '!')
            return Optional.of(way);
        return nextSteps(pt)
              .filter(p -> visitable(m, way, p))
              .map(p -> way(m, p, concat(way.stream(), of(p)).collect(toList())))
              .filter(Optional::isPresent)
              .map(Optional::get)
              .sorted(comparingInt(List::size))
              .findFirst();
    }
    private static boolean visitable(char[][] m, List<Point> way, Point p) {
        return !way.contains(p) &&
                p.y >= 0 && p.y < m.length &&
                p.x >= 0 && p.x < m[p.y].length
                && m[p.y][p.x] != '#';
    }

    // Test
    public static void main(String[] args) {
        char[][] m = {
            {'G', '.', '.', '.'},
            {'.', '.', '.', '.'},
            {'.', '.', '!', '.'},
            {'.', '.', '.', '.'},
        };
        way(m, pt(0, 0)).ifPresent(System.out::println);
    }
}

Also die Priorität musste ich nicht ändern, die war schon richtig so. Erst oben nachschauen, dann links, dann rechts und dann unten . Das sollte sicherstellen, dass immer der richtige Pfad gewählt wird.

Hiermit klappt es wie es soll.

Java:
    while (!nodes.isEmpty()) {
            Node current = nodes.poll();
            examinedPoints.add(current.point);

            // add all relevant nodes in the vicinity (up, left, right, down) to
            // the queue (the way the grid is set-up makes it impossible to be
            // out of bounds here)
            Point up = new Point(current.point.x, current.point.y - 1);
            Point left = new Point(current.point.x - 1, current.point.y);
            Point right = new Point(current.point.x + 1, current.point.y);
            Point down = new Point(current.point.x, current.point.y + 1);

            if (grid[current.point.y - 1][current.point.x] == '.' && !examinedPoints.contains(up)) {
                if (possibleTargets.contains(up)) {
                    optimalNode = new Node(up, current);
                    break;
                } else
                    nodes.offer(new Node(up, current));
            }
            if (grid[current.point.y][current.point.x - 1] == '.' && !examinedPoints.contains(left)) {
                if (possibleTargets.contains(left)) {
                    optimalNode = new Node(left, current);
                    break;
                }
                nodes.offer(new Node(left, current));
            }
            if (grid[current.point.y][current.point.x + 1] == '.' && !examinedPoints.contains(right)) {
                if (possibleTargets.contains(right)) {
                    optimalNode = new Node(right, current);
                    break;
                }
                nodes.offer(new Node(right, current));
            }
            if (grid[current.point.y + 1][current.point.x] == '.' && !examinedPoints.contains(down)) {
                if (possibleTargets.contains(down)) {
                    optimalNode = new Node(down, current);
                    break;
                }
                nodes.offer(new Node(down, current));
            }
        }

Musste nur die Schleife abbrechen sobald eines der Knoten in Reichweite mein gesuchter Knoten ist. Die Reihenfolge in der ich die Knoten untersuche sorgt dann dafür, dass ich am Ende den richtigen Pfad bekomme.

Ein Schritt für einen etwas komplexeren Fall sieht dann so aus

Code:
initial
#########
#G..G..G#
#.......#
#.......#
#G..E..G#
#.......#
#.......#
#G..G..G#
#########

after round 1
#########
#.G...G.#
#...G...#
#...E..G#
#.G.....#
#.......#
#G..G..G#
#.......#
#########
 
Zuletzt bearbeitet:
Ähnliche Java Themen
  Titel Forum Antworten Datum
mervanpolat Binary Search Algorithmus ausführen Java Basics - Anfänger-Themen 1
S Java TelephoneBookEntry search Java Basics - Anfänger-Themen 2
T filecontentheader search Java Basics - Anfänger-Themen 16
S Binary Search Tree - Nummerierung in Inorder Java Basics - Anfänger-Themen 5
C binary search trees - order Java Basics - Anfänger-Themen 8
M Search in ArrayList<T>? Java Basics - Anfänger-Themen 8
G Frage zu Binary Search Java Basics - Anfänger-Themen 3
S [EDIT] Tiefensuche / Depth-First-Search / Greedy Algorithmus Java Basics - Anfänger-Themen 6
S leeres Array statt Null Pointer Exception ausgeben Java Basics - Anfänger-Themen 20
H SimpleDataFormat.format() spuckt falsches Jahr aus (statt 1999 = 3899) Java Basics - Anfänger-Themen 7
berserkerdq2 Findet eine parallele Verarbeitung in Java bei Threads erst statt, wenn man die Methoden auch synchronized? Und wie sieht bei Conditions aus? Java Basics - Anfänger-Themen 8
berserkerdq2 Wozu benötigt man den BiPredicate, kann ich nicht einfach eine normale Methode nutzen, statt BiPredicate? Java Basics - Anfänger-Themen 3
A Wenn eine Zahl durch 7 teilbar ist, soll statt der Zahl ein ‘*‘ angezeigt werden. java? Java Basics - Anfänger-Themen 47
S Methode von vererbter Klasse, statt die der "Mutterklasse" aufrufen Java Basics - Anfänger-Themen 28
A Eclipse-Fenster starten statt Konsoleausgabe Java Basics - Anfänger-Themen 2
M Threads Threads laufen sequenziell, statt gleichzeitig. Java Basics - Anfänger-Themen 9
T Datentypen char als Buchstaben statt als Zahl ausgeben Java Basics - Anfänger-Themen 4
P Datentypen Kann ich bei double Komma statt Punkt eingeben? Java Basics - Anfänger-Themen 14
L Dialogbox statt Konsole verwenden Java Basics - Anfänger-Themen 5
R ArrayList - System.out.println nur einmal, statt 10 mal Java Basics - Anfänger-Themen 5
Z Lottoprogramm - Zeigt manchmal nur 5 Zahlen statt 6 an? Java Basics - Anfänger-Themen 4
Y Warum void statt Datentyp + return Java Basics - Anfänger-Themen 4
D Methode die statt char[] ein "null" zurück gibt Java Basics - Anfänger-Themen 8
B GridBagLayout in der oberen rechten Ecke starten statt mittig Java Basics - Anfänger-Themen 2
M toString gibt "null" statt "0.0" aus Java Basics - Anfänger-Themen 5
S Threads Programm terminiert statt zu warten Java Basics - Anfänger-Themen 20
J Objectreferenz statt value an Methode uebergeben? Java Basics - Anfänger-Themen 2
H inputfeld dezimalzahl mit komme statt punkt Java Basics - Anfänger-Themen 12
R Double: auf einmal Komma statt Punkt Java Basics - Anfänger-Themen 4
H TreeMap<> statt TreeMap<Long, String> Java Basics - Anfänger-Themen 2
J Polymorphie Iteratoren statt Collections Java Basics - Anfänger-Themen 13
C lokale Variable verwenden statt globale Java Basics - Anfänger-Themen 7
R POI HSSF liesst in Excel Formel statt Ergebnis Java Basics - Anfänger-Themen 4
K Compiler-Fehler Probleme mit UTF-8 (statt ANSI) und Notepad++ Java Basics - Anfänger-Themen 2
C Objektreferenz holen statt übergeben Java Basics - Anfänger-Themen 2
T Ausgabe findet nicht statt Java Basics - Anfänger-Themen 4
R Objektname statt Adresse ausgeben Java Basics - Anfänger-Themen 4
S Datentypen float statt void Java Basics - Anfänger-Themen 3
S Decimalpunkt statt Dezimalkomma Java Basics - Anfänger-Themen 2
T Array statt String Java Basics - Anfänger-Themen 12
Guybrush Threepwood array.length statt array.length() Java Basics - Anfänger-Themen 6
G Bild in Buffer statt temporäre Bilddatei Java Basics - Anfänger-Themen 6
A log4j - wie kann ich im Quellcode initialisieren statt in der properties-Datei? Java Basics - Anfänger-Themen 2
Landei Annotations statt Listeners? Java Basics - Anfänger-Themen 36
K Txt statt in TextArea in JTable einlesen und bearbeiten können Java Basics - Anfänger-Themen 4
J statt modulo "if-Anweisung" Java Basics - Anfänger-Themen 9
N paint methode statt Event-Handling-Methode Java Basics - Anfänger-Themen 3
D JSP include wie bei php? (inhalt statt ergebnis einfügen)) Java Basics - Anfänger-Themen 24
L Ausgabe in arabischen Zahlen statt in ASCII Java Basics - Anfänger-Themen 9
G Griechische statt lateinischen Buchstaben beim Schreiben Java Basics - Anfänger-Themen 9
M Beispiel-Webprojekt: Statt HSQLDB Postgres verwenden Java Basics - Anfänger-Themen 12
S kompletten Datensatz statt nur ein Feld auslesen lassen,wie? Java Basics - Anfänger-Themen 3
M was wenn der benutzer ein double statt int eingibt ? Java Basics - Anfänger-Themen 3
N Vergleich findet nicht statt. Java Basics - Anfänger-Themen 13
G JDialog auf Jpanel statt Frame? Java Basics - Anfänger-Themen 4
T Wie Eingabe von Gleitkommazahl mit Komma statt Punkt Java Basics - Anfänger-Themen 4
F Sanduhr statt Mauszeiger anzeigen Java Basics - Anfänger-Themen 3
A .statt,anzeigen beim NumberFormat + Wie JTable formatieren? Java Basics - Anfänger-Themen 4
G Toolbar buttons sollen dialoge anzeigne statt pop up Java Basics - Anfänger-Themen 44
R Nur Double statt Sting oder Integer Combo sortieren und ! Java Basics - Anfänger-Themen 16
D ausgabe verändern (statt zeilenumbruch leerzeichen) Java Basics - Anfänger-Themen 2
M statt drop down menü - buttons Java Basics - Anfänger-Themen 5
O JToolBar wird Tab in JTabbedPane statt Toolbar in JFrame Java Basics - Anfänger-Themen 6
W Punkt statt Komma? Java Basics - Anfänger-Themen 2
M Zeichen aneinander Reihen, statt diese zu ersetzen! Java Basics - Anfänger-Themen 3
Dilandau html applet: .jar laden statt .class Java Basics - Anfänger-Themen 4
V Betätigung des Buttons Erhöhung der Variablen um 2 statt 1 Java Basics - Anfänger-Themen 5

Ähnliche Java Themen

Neue Themen


Oben