实现指针队列

Implementing Queue of Pointers

本文关键字:队列 指针 实现      更新时间:2023-10-16

我正在尝试使用两个指针队列来实现8-puzzle(BFS)。我在将未访问的节点添加到打开列表中时遇到问题。我哪里出错了?

#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 isSamePuzzle(vector<int> p){
bool samePuzzle = false;
if(puzzle == p)
samePuzzle =  true;
return samePuzzle;
}
};

bool contains(std::queue<Node*> q, Node n){
bool exist = false;
while (!q.empty()){
cout << endl;
if (q.front()->puzzle == n.puzzle)
exist = true;
q.pop();
}
return exist;
}
int main()
{
std::vector<int> initial;
initial.push_back(2);
initial.push_back(1);
initial.push_back(3);
initial.push_back(4);
initial.push_back(0);
initial.push_back(6);
initial.push_back(7);
initial.push_back(5);
initial.push_back(8);
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);
cout << "open list size " << openList.size() <<endl;
cout << "closed list size " << closedList.size() <<endl;
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);             
}
}
}

这似乎是有问题的一点:

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

将一个或两个节点添加到打开列表后,程序崩溃。当我在打开的列表中打印出拼图时,我看到开头有一些垃圾值,然后是实际的 pluzzle。

我将您的代码粘贴到我的 IDE 中并通过调试器运行它。我在这里看到的是当您在第二次迭代中经历 while 循环时;它打印 2 行代码,从 openList 弹出,调用并执行moveUp()但当它进入moveDown()时,它崩溃了。

它在此行上崩溃:

std::swap( temp[zPos], temp[zPos+3] );

你可能想在调试器中检查这行代码,同时单步执行 while 循环,检查变量和容器是否具有有效信息;还要检查是否没有exceedingcontainerbound。您可能还想检查从上一次调用moveUp()调用swapmemory是否仍然有效。

不是在第一次调用moveDown(),而是在第二次调用中,您将temp设置为等于puzzle这里的问题是puzzle的值或大小为0.因此,当您尝试在temp[zPos]&temp[zPos+3]上调用std::swap时,索引无效,因为tempsize = 0

程序的主要问题是,您将指针推送到打开列表中的局部变量。一旦离开作用域,变量currentChild将被销毁(因此,一旦您进入循环的下一次迭代或离开循环),然后指针指向未使用/垃圾内存。

但是,您将在其他地方遇到类似的问题,因为您无法可靠地保存指向vector<Node>列表中任何节点的指针。这是因为std::vector类会在第一次达到一定大小时销毁/复制其中的所有内容,这将使指向其中任何内容的指针无效。因此,要么在所有硬币保持器中保存指向节点的指针(queuevector,所以只有指针被销毁/复制),要么你不保存任何指向节点的指针,而只保存节点本身 - 任何最适合你的。

我能够通过使孩子成为 Node 指针的向量来修复代码,如下所示:

vector<Node*> children;

每个子节点指针:

Node* child = new Node(temp, this);