【问题标题】:Garbage value in parent node父节点的垃圾值
【发布时间】:2018-02-20 01:25:44
【问题描述】:

我正在实施广度优先搜索来解决 8 难题。当我尝试使用 this 指针创建新节点并指向当前节点时,父指针返回垃圾值。这是由于迭代器无效吗?有没有办法绕过这个问题,也许是通过使用与向量不同的容器?

代码:

#include <iostream>
#include <string>
#include <iostream>     
#include <algorithm>    
#include <vector>       
#include <queue>
using namespace std;
class Node {
public:

vector<Node> children;
vector<int> puzzle;
vector<int> goal = {1, 2, 3, 4, 5, 6, 7, 8, 0};
Node *parent;
Node(vector<int> _puzzle, Node *_parent){
    puzzle=_puzzle;
    parent=_parent;
}

void printPuzzle() {
    int count = 0;
    for (auto i: puzzle) {
    if ( count % 3 == 0)
    std::cout << std::endl;
    std::cout << i << ' ';
    count++;   
}
}

int findZero(){
    std::vector<int>::iterator it;
    it = find (puzzle.begin(), puzzle.end(), 0);
    auto z = std::distance(puzzle.begin(), it);
    return (int)z;
}

bool isGoal(){
    bool goalFound = false;
    if(puzzle == goal)
    goalFound = true;
    return goalFound;
}

void moveUp(){
    int zPos = findZero();
    vector<int> temp = puzzle;
    if ( zPos != 0 && zPos != 1 && zPos != 2 )
    std::swap(temp[zPos], temp[zPos-3]);
    Node child = Node(temp, this);
    children.push_back(child);

}

void moveDown(){
    int zPos = findZero();
    vector<int> temp = puzzle;
    if ( zPos != 6 && zPos != 7 && zPos != 8 )
    std::swap(temp[zPos], temp[zPos+3]);
    Node child = Node(temp, this);
    children.push_back(child); 
}

void moveRight(){
    int zPos = findZero();
    vector<int> temp = puzzle;
    if ( zPos != 2 && zPos != 5 && zPos != 8 )
    std::swap(temp[zPos], temp[zPos+1]);
    Node child = Node(temp, this);
    children.push_back(child);
}

void moveLeft(){ 
    int zPos = findZero();
    vector<int> temp = puzzle;
    if ( zPos != 0 && zPos != 3 && zPos != 6 )
    std::swap(temp[zPos], temp[zPos-1]);
    Node child = Node(temp, this);
    children.push_back(child); 
}
};

bool contains(std::queue<Node> q, Node n){
    std::queue<Node> tempQ = q;
    bool exist = false;
    while (!tempQ.empty()){
        if (tempQ.front().puzzle == n.puzzle)
        exist = true;
        tempQ.pop();
    }
    return exist;
 }

int main()
{
std::vector<int> initial;
initial.push_back(3);
initial.push_back(5);
initial.push_back(8);
initial.push_back(1);
initial.push_back(0);
initial.push_back(4);
initial.push_back(2);
initial.push_back(7);
initial.push_back(6);
Node init = Node(initial, NULL);
std::queue<Node> openList;
std::queue<Node> closedList;
openList.push(init);
bool goalFound = false;
while(!openList.empty() && !goalFound){
    Node currentNode = openList.front();
    closedList.push(currentNode);
    openList.pop();       
    currentNode.moveUp();
    currentNode.moveDown();
    currentNode.moveRight();
    currentNode.moveLeft();

    for (auto i: currentNode.children){
        Node currentChild = i;
        if (currentChild.isGoal()){
            std::cout << "Goal Found." << endl;
            goalFound = true;


        }
        if (!contains(openList, currentChild) && !contains(closedList, 
 currentChild))
            openList.push(currentChild); 
    }   
}
}

【问题讨论】:

  • 不要发布 sn-ps - 发布所有代码。
  • 这个可能性很大,不是相关代码sn-p。
  • 道歉。我现在已经发布了整个代码。

标签: c++ pointers this breadth-first-search sliding-tile-puzzle


【解决方案1】:

复制和引用/指针是不同的东西。让我们看一下您的代码的问题之一。这是有问题的代码:

while(!openList.empty() && !goalFound){
    Node currentNode = openList.front();
    closedList.push(currentNode);
    openList.pop();       
    currentNode.moveUp();
    currentNode.moveDown();
    currentNode.moveRight();
    currentNode.moveLeft();

    for (auto i: currentNode.children){
        Node currentChild = i;
        if (currentChild.isGoal()){
            std::cout << "Goal Found." << endl;
            goalFound = true;
        }
        if (!contains(openList, currentChild) && !contains(closedList,currentChild))
            openList.push(currentChild); 
        }   
    }
}

为简单起见,我们假设 openList 中只有一个 Node,我们称其地址为 oL0。现在让我们逐行看看发生了什么:

Node currentNode = openList.front(); // creates copy of first element in queue

正如您可能已经猜到的那样,创建副本会创建具有相同内部状态的新节点。我们称当前节点的地址为cN0。要记住的重要一点是地址 oL0cN0 是不同的(它们是不同的对象)。让我们继续:

closedList.push(currentNode); // creates COPY of currentNode in closedList
openList.pop(); // deletes Node at address oL0

很简单 - closedList 在地址上有 currentNode 的副本,我们可以称之为 cL0

// do stuff with currentNode (address cN0)
currentNode.moveUp();
currentNode.moveDown();
currentNode.moveRight();
currentNode.moveLeft();

这里有一个问题。您正在更改地址 cN0 处的变量 currentNode(不是 存储的值 closedList)。随着我们在您的代码中进一步移动,进入 for 循环,我们可以找出第二个问题,即您的问题所在的位置。这个 for 循环的重点是将 currentNode 的子节点推入 openList:

for (/* ... */) {
    // ...
    // same problem of creating copies
    if (/* ... */)
        openList.push(currentChild); 
    }
}

这些子节点的父指针设置为当前节点的地址(即cN0)。 在 while 循环迭代结束时,所有局部变量(while 范围内的变量)的析构函数被调用,这导致 currentNode 被销毁。这意味着 openList 中的每个子节点对其父节点的地址(您引用的垃圾值)都是无效的。请注意,在 for 循环中也会出现同样的问题。

关于如何解决这个问题的想法,我建议查看references,或者例如使用指针队列...

【讨论】:

    猜你喜欢
    • 2012-01-18
    • 2022-06-16
    • 1970-01-01
    • 1970-01-01
    • 2015-05-15
    • 1970-01-01
    • 2022-06-14
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多