Java - 在广度优先搜索中获取路径的好方法?

时间:2014-09-07 02:54:35

标签: java algorithm breadth-first-search

我整天都在努力,想不出一个好的解决方案。我将实现广度优先搜索算法以解决滑动拼图。到目前为止,这是我的代码的相关部分。 (我还没有测试它是否有效,因为它不完整) 到目前为止,预计此代码将遍历所有可能性并达到目标。但是,我想不出一种方法来记录从初始状态到目标状态的路径。

private void addToQueue(PuzzleState nextPS) {
    if (notVisitedAndNotNull(nextPS))
        queue.add(nextPS);
}

private void solveByBFS() {
    queue.clear();
    queue.add(this.initialState);
    long startTime = System.currentTimeMillis();

    while(!queue.isEmpty()) { //TODO create a way to backtrack and get a path
        if (queue.size() > maxQueueSize)
            maxQueueSize = queue.size();
        this.currentState = queue.poll();

        if (this.currentState.equals(finalState)) { //TODO check if cannot progress any further and terminate
            System.out.println("Successful! Ending Time: " + startTime);
            return;
        }
        visited.add(this.currentState);

        this.addToQueue(this.currentState.moveUp());
        this.addToQueue(this.currentState.moveDown());
        this.addToQueue(this.currentState.moveRight());
        this.addToQueue(this.currentState.moveLeft());

    }
    return;
}

所以我想要一种方法来回溯跟踪并从目标节点到达初始状态,反转路径,然后在列表中打印出来。

以下是我使用的数据结构:

public class SimplePuzzleState implements PuzzleState{

private int rowSz;
private int sz;
private int zeroPos;
private int[] gameState;

@Override
public void configureState(int[] gameState) {
    rowSz = (int) Math.sqrt(gameState.length);
    sz = gameState.length;
    zeroPos = PuzzlePropertyUtility.findZeroPosition(gameState); 
    this.gameState = gameState;
}

@Override
public PuzzleState moveUp() { 
    if (zeroPos <= rowSz - 1) {
        return null;
    }
    this.swap(zeroPos, zeroPos - rowSz);
    return this.createNewUpdatedState();
}

@Override
public PuzzleState moveDown() {
    if (zeroPos >= sz - rowSz) {
        return null;
    }
    this.swap(zeroPos, zeroPos + rowSz);
    return this.createNewUpdatedState();
}

@Override
public PuzzleState moveLeft() {
    if (zeroPos % rowSz <= 0) {
        return null;
    }
    this.swap(zeroPos, zeroPos - 1);
    return this.createNewUpdatedState();
}

@Override
public PuzzleState moveRight() {
    if (zeroPos % rowSz >= rowSz -1) {
        return null;
    }
    this.swap(zeroPos, zeroPos + 1);
    return this.createNewUpdatedState();
}

@Override
public boolean isEqual(PuzzleState other) { 
    if (other != null) {
        if (this.getStateArray() instanceof int[] && other.getStateArray() instanceof int[])
            return (Arrays.equals(this.getStateArray(), other.getStateArray()));
    }
    return false;
}

@Override
public int[] getStateArray() {
    return gameState;
}

private void swap(int pos1, int pos2) {
    int temp = this.gameState[pos1];
    this.gameState[pos1] = this.gameState[pos2];
    this.gameState[pos2] = temp;
}

private PuzzleState createNewUpdatedState() {
    PuzzleState newState = new SimplePuzzleState();
    newState.configureState(this.getStateArray());
    return newState;
}

}

这是PuzzleState接口:

public interface PuzzleState {

public void configureState(int[] gameState);

PuzzleState moveUp();

PuzzleState moveDown();

PuzzleState moveLeft();

PuzzleState moveRight();



boolean isEqual(PuzzleState other);

int[] getStateArray();

}

我考虑过向SimplePuzzleState添加一个属性以包含父节点。 但是,我无法修改它实现的接口,因为我的教师不允许这样做。因此,我无法使用链表方法回溯。有没有聪明的方法来记录正确的路径?最后,我的导师要我打印一个包含表示动作的枚举的列表。所以我必须弄清楚如何将枚举映射到函数moveUpmoveDown等。

提前谢谢你。我为发布这么多代码而道歉,但我真的需要建议我应该采取哪些方向。

2 个答案:

答案 0 :(得分:1)

添加到SimplePuzzleState另一个名为gotHereFrom的字段,当您致电addToQueue()时,请更新此字段(每个项目)。完成后,如果打印成功&#34;并return;根据gotHereFrom开始迭代并一直打印节点:

public class SimplePuzzleState implements PuzzleState{

    private int rowSz;
    private int sz;
    private int zeroPos;
    private int[] gameState;
    private SimplePuzzleState gotHereFrom; // add this guy
    ...

    protected void updateParent(SimplePuzzleState gotHereFrom) {
        this.gotHereFrom = gotHereFrom;
    }
    ...
}

private void addToQueue(PuzzleState nextPS) {
    if (notVisitedAndNotNull(nextPS)) {
        queue.add(nextPS);
        nextPS.updateParent(this); // and update where we got from
    }
}

迭代结果:

...
if (this.currentState.equals(finalState)) { //TODO check if cannot progress any further and terminate
     System.out.println("Successful! Ending Time: " + startTime);
     String path = "";
     while (gotHereFrom != null) {
         path += " -> " + gotHereFrom;
         gotHereFrom = gotHereFrom.getGotHereFrom();
     }
     System.out.println(path);
     return;
}

答案 1 :(得分:1)

你有正确的想法。如果您无法将父指针添加到状态,那么只需使用HashMap之类的相同信息维护previous。当你创建四个 子状态,添加从父级到这四个的映射。

// A map to hold parent relations.   
HashMap<SimplePuzzleState, SimplePuzzleState> previous = new HashMap<>();

...

// Now change the add function.
private void addToQueue(PuzzleState parentPS, PuzzleState nextPS) {
    if (notVisitedAndNotNull(nextPS)) {
        queue.add(nextPS);
        previous.add(nextPS, parentPS);
        nextPS.updateParent(this); // and update where we got from
    }
}

// Then update the calls to match:
this.addToQueue(currentState, this.currentState.moveUp());
... 

当你找到目标时,就像使用父指针一样,使用哈希追溯到开始。

    if (this.currentState.equals(finalState)) { 
        System.out.println("Successful! Ending Time: " + startTime);
        System.out.println("Path back to start:");
        PuzzleState state = currentState;
        do {
          state.print();
          state = previous.get(state);
        } while (state != null);
    }