矢量问题 (std::out_of_range)

A Problem with Vectors (std::out_of_range)

本文关键字:of range out std 问题      更新时间:2023-10-16

这是我问题的描述:

该计划的说明:
我正在C++中实现一个程序,该程序测试 Prim 的算法以查找最小生成树。该程序的目标是计算为选定数量的随机图找到最小生成树所需的秒数。

到目前为止,我做了什么?
我完成了整个程序的函数和头文件的实现。由于源代码很小,为了清楚起见,我决定将其粘贴到此邮件中,以便更好地可视化问题。

问题:
出于某种原因,我在应用程序运行时遇到了某种"超出范围"的矢量问题。问题标记在("Prim_and_Kruskal_Algorithms.cpp")文件中。

请求帮助:
如果有人能帮助我发现问题,我将不胜感激。我已经用这个问题内联了源代码。

源代码:

(Undirected_Graph.h) 文件:

#ifndef UNDIRECTED_GRAPH_H
#define UNDIRECTED_GRAPH_H
#include <vector>
using std::vector;
#include <climits>
class Edge;
class Node
{
    public:
        Node(int);                 //The constructor.
        int id;                    //For the id of the node.
        bool visited;              //For checking visited nodes.
        int distance;
        vector <Edge*> adj;       //The adjacent nodes.
};
class Edge
{
    public:
        Edge(Node*, Node*, int);   //The constructor.
        Node* start_Node;          //The start_Node start of the edge.
        Node* end_Node;            //The end of the edge.
        int w;                     //The weight of the edge.
        bool isConnected(Node* node1, Node* node2)  //Checks if the nodes are connected.
        {
            return((node1 == this->start_Node && node2 == this->end_Node) ||
                   (node1 == this->end_Node   && node2 == this->start_Node));
        }
};
class Graph
{
    public:
        Graph(int);                    //The Constructor.
        int max_Nodes;                 //Maximum Number of allowed Nodes.
        vector <Edge*> edges_List;     //For storing the edges of the graph.
        vector <Node*> nodes_List;     //For storing the nodes of the graph.
        void insertEdge(int, int, int);
        int getNumNodes();
        int getNumEdges();
};
#endif

(Undirected_Graph.cpp)文件:

#include "Undirected_Graph.h"
Node::Node(int id_Num)
{
    id = id_Num;
    visited = 0;
    distance = INT_MAX;
}
Edge::Edge(Node* a, Node* b, int weight)
{
    start_Node = a;
    end_Node = b;
    w = weight;
}
Graph::Graph(int size)
{
    max_Nodes = size;
    for (int i = 1; i <= max_Nodes; ++i)
    {
        Node* temp = new Node(i);
        nodes_List.push_back(temp);
    }
}
void Graph::insertEdge(int x, int y, int w)
{
    Node* a = nodes_List[x-1];
    Node* b = nodes_List[y-1];
    Edge* edge1 = new Edge(a, b, w);
    Edge* edge2 = new Edge(b, a, w);
    edges_List.push_back(edge1);
    a->adj.push_back(edge1);
    b->adj.push_back(edge2);
}
int Graph::getNumNodes()
{
    return max_Nodes;
}
int Graph::getNumEdges()
{
    return edges_List.size();
}

(Prim_and_Kruskal_Algorithms.h) 文件:

#ifndef PRIM_AND_KRUSKAL_ALGORITHMS_H
#define PRIM_AND_KRUSKAL_ALGORITHMS_H
class PKA
{
    private:
    //inline void generateRandomGraph();
    protected:
    //-No Protected Data Members in this Class.
    public:
        void runAlgorithms();
        void prim();
};
#endif

(Prim_and_Kruskal_Algorithms.cpp) 文件*(问题出在此文件中,标记如下):*

#include "Prim_and_Kruskal_Algorithms.h"
#include "Undirected_Graph.h"
#include <iostream>
using std::cout;
using std::cin;
using std::endl;
#include <cstdlib>
using std::rand;
using std::srand;
#include <ctime>
using std::time;
//=============================================================================
//============Global Variables and Settings for the program====================
//=============================================================================
const int numIterations = 1; //How many times the Prim function will run.
const int numNodes = 10;     //The number of nodes in each graph.
const int numEdges = 9;      //The number of edges for each graph.
const int sRandWeight = 1;   //The "start" range of the weight of each edge in the graph.
const int eRandWeight = 100; //The "end" range of the weight of each edge in the graph.
//=============================================================================
//=============================================================================
//=============================================================================
void PKA::runAlgorithms()   //Runs the Algorithms
{
        srand( time(0) );
    cout << "------------------------------" << endl;
        //Calling the Functions:
        cout << "nRunning the Prim's Algorithms:nPlease wait till the completion of the execution time" << endl;
        //===============================================
        //Start the clock for Prim's Algorithm:
        clock_t start, finish;
        start = clock();
        for(int iter1 = 1; iter1 <= numIterations; ++iter1)
        {
            prim();
        }
        //Stop the clock for Prim and print the results:
    finish = clock();
        cout << "ntThe execution time of Prim's Algorithm:t" << ((double)(finish - start) / CLOCKS_PER_SEC) << " s";
    return;
}
void PKA::prim()
{
    //=============================================================================
    //=============================Generating A Random Graph=======================
    //=============================================================================
    //Randomizing Values:
    //===============================================
    int randStartNode = rand() % numNodes;     //Generation a random start node.
    int randEndNode = rand() % numNodes;       //Generating a random end node.
    int randWeight;               //Random weight for the edge.
    while(randEndNode == randStartNode)   //Checking if both randomized nodes are equal.
    {
        randEndNode = (rand() % numNodes);
    }
    //===============================================
    Graph myGraph(numNodes);
    for(int i = 0; i < numEdges; ++i)
    {
        //Generating a random weight:
        randWeight =  sRandWeight + rand() % eRandWeight;
        //Inserting a new Edge:
        myGraph.insertEdge(randStartNode, randEndNode, randWeight);
    }

    //=============================================================================
    //=============================================================================
    //=============================================================================
    int currentNode = 0;           //The current Node being under investigation.
    int adjCounter = NULL;         //How many adjacent nodes do we have for the current node.
    int minDistance = NULL;        
    int minIndex = 0;
    myGraph.nodes_List[0]->distance = 0;   //Indicate the start node.
    myGraph.nodes_List[0]->visited = 1;    //The starting node is already considered as a visited node.
    for(int i = 0; i < numNodes - 1; i++)
    {
        //Determine how many adjacent nodes there are for the current node:
        adjCounter = myGraph.nodes_List[currentNode]->adj.size();
        if(adjCounter == 0) //If there are no adjacent nodes to the current node:
        {
            myGraph.nodes_List[currentNode]->adj.at(minIndex)->end_Node->visited = 1;
                    cout << "n*******Not all nodes are connected!*******" << endl;
            continue;
        }
        minDistance = myGraph.nodes_List[currentNode]->adj.at(0)->w;
        minIndex = 0;
        for(int counter = 0; adjCounter > 0; adjCounter--, counter++)
        {
            if(myGraph.nodes_List[currentNode]->adj[counter]->end_Node->visited == false)
            {
                if(myGraph.nodes_List[currentNode]->distance > myGraph.nodes_List[currentNode]->adj[counter]->w)
                {
                    myGraph.nodes_List[currentNode]->distance = myGraph.nodes_List[currentNode]->adj[counter]->w;
                }
                if(minDistance > myGraph.nodes_List[currentNode]->adj[counter]->w)
                {
                    minDistance = myGraph.nodes_List[currentNode]->adj[counter]->w;
                    minIndex = counter;
                }
            }
        }
                //======================================================================================
                //=========================The Problem is in the following two lines====================
        //======================================================================================
        //Mark the current node as visited:
        myGraph.nodes_List[currentNode]->adj.at(minIndex)->end_Node->visited = 1;
        //Switching to the next node that we have just visited:
        currentNode = myGraph.nodes_List[currentNode]->adj.at(minIndex)->start_Node->id;
        //======================================================================================
        //======================================================================================
        //======================================================================================
    }
}

(Client_Code.cpp) 文件:用于测试程序。

#include "Prim_and_Kruskal_Algorithms.h"
#include <iostream>
using std::cout;
using std::endl;
int main()
{
    cout << "nWelcome to the Prim and Kruskal Algorithms Comparison!" << endl;
    cout << "nPlease wait until the completion of the algorithms." << endl;
    PKA myPKA;                //Creating an object of the class.
    myPKA.runAlgorithms();    //Running the Algorithm.
    cout << "nnThe program terminated successfully!" << endl;
    return 0;
}

请看这一行:

myGraph.nodes_List[currentNode]->adj.at(minIndex)->end_Node->visited = 1;

作为一个经验丰富的C++程序员,我觉得这句话很可怕。

麻烦的直接原因是adj没有你想象的那么多成员;你要求(在我的测试运行中)大小为零的列表的第 5 个元素。这会让你离开地图,然后你开始操纵记忆。

更一般地说,您不是在检查边界。

更一般地说,您应该允许这些类管理自己的成员。使用访问器和突变器(getX()setX(...)),以便成员访问都发生在一个地方,您可以在那里检查边界。像这样伸手myGraph的喉咙是非常不安全的。

你会注意到,我没有说程序在何处/何时/如何偏离意图,因此列表没有应有的元素。那是因为对我来说追踪它太麻烦了。如果你按照我的建议组织类,代码会干净得多,你可以在不同的地方检查你的假设,错误应该变得很明显。

编辑:
要创建随机连接图,请尝试以下操作:

  Graph myGraph(numNodes);              //Create a new Graph.
  // This ensures that the kth node is connected to the [1...(k-1)] subgraph.
  for(int k=2 ; k<=numNodes ; ++k)
    {
      randWeight =  rand() % eRandWeight;
      myGraph.insertEdge(k, rand()%(k-1)+1, randWeight);
    }
  // This adds as many extra links as you want.
  for(int i = 0; i < numExtraEdges; ++i)
    {
      randWeight =  rand() % eRandWeight;
      randStartNode = rand()%(numNodes-1)+1;
      randEndNode = rand()%(numNodes-1)+1;
      myGraph.insertEdge(randStartNode, randEndNode, randWeight);
    }

您有太多的代码进行随意检查,无法确定任何事情。 但是.at()方法将引发您提到的超出范围的异常,并且在您更新minIndex后立即发生崩溃行,因此我建议查看确定该值的代码。 您使用的是调试器吗? 异常点的minIndex值是多少,允许的范围是多少?

此外,当你有一行像这样的复合语句时,它可以帮助调试这样的问题,如果你把它分解,它给你更清晰、更简单的代码。 与其一遍又一遍地重复大块代码,不如这样:

Node * node = myGraph.nodes_List[currentNode];
assert(node);
Edge * minAdjEdge = node->adj.at(minIndex);
assert(minAdjEdge);

然后使用 minAdjEdge 引用该边,而不是重复的复合语句。

对我来说,您第一次在大循环中使用 minIndex 仍然使用上一次迭代中从节点确定的值,但它将其应用于新的当前节点,这对我来说也很奇怪。 然后在可能使用过时值后将其重置为零。 但这并不接近你所说的导致崩溃的线,所以这可能不是你的问题。 就像我说的,你在这里粘贴了很多代码,所以很难遵循整个事情。

这是太多的代码,但我乍一看可以观察到的是,由于某种原因,您混合了从 0 开始和基于 1 的迭代。

这是故意的吗?这难道不是你问题的原因吗?