【问题标题】:How to free memory from minHeap C++如何从最小堆 C++ 中释放内存
【发布时间】:2019-11-24 16:29:00
【问题描述】:

我有一个来自https://www.geeksforgeeks.org/dijkstras-algorithm-for-adjacency-list-representation-greedy-algo-8/ 的 Dijkstra 算法的实现,但是当我尝试使用它时会出现严重的内存泄漏。根据我使用 valgrind 运行 minHeap 时的问题。我试过像这样释放每个指针:

for (int i=0; i<size; i+=1){
    free(minHeap->array[i]);
}

但是 valgrind 给出了无效的 free() 并且泄漏没有解决。这是 Dijkstra 的代码。

// C++ program for Dijkstra's shortest path algorithm for adjacency 
// list representation of graph 

#include <stdio.h> 
#include <stdlib.h> 
#include <limits.h> 
#include <float.h>

// A structure to represent a node in adjacency list 
struct AdjListNode 
{ 
    int dest; 
    double weight; 
    struct AdjListNode* next; 
}; 

// A structure to represent an adjacency list 
struct AdjList 
{ 
    struct AdjListNode *head;  // pointer to head node of list 
}; 

// A structure to represent a graph. A graph is an array of adjacency lists. 
// Size of array will be V (number of vertices in graph) 
struct Graph 
{ 
    int V; 
    struct AdjList* array; 
}; 

// A utility function to create a new adjacency list node 
struct AdjListNode* newAdjListNode(int dest, double weight) 
{ 
    struct AdjListNode* newNode = (struct AdjListNode*) malloc(sizeof(struct AdjListNode)); 
    newNode->dest = dest; 
    newNode->weight = weight; 
    newNode->next = NULL; 
    return newNode; 
} 

// A utility function that creates a graph of V vertices 
struct Graph* createGraph(int V) 
{ 
    struct Graph* graph = (struct Graph*) malloc(sizeof(struct Graph)); 
    graph->V = V; 

    // Create an array of adjacency lists.  Size of array will be V 
    graph->array = (struct AdjList*) malloc(V * sizeof(struct AdjList)); 

     // Initialize each adjacency list as empty by making head as NULL 
    for (int i = 0; i < V; ++i) 
        graph->array[i].head = NULL; 

    return graph; 
} 

// Adds an edge to an undirected graph 
void addEdge(struct Graph* graph, int src, int dest, double weight) 
{ 
    // Add an edge from src to dest.  A new node is added to the adjacency 
    // list of src.  The node is added at the beginning 
    struct AdjListNode* newNode = newAdjListNode(dest, weight); 
    newNode->next = graph->array[src].head; 
    graph->array[src].head = newNode; 

    // Since graph is undirected, add an edge from dest to src also 
    newNode = newAdjListNode(src, weight); 
    newNode->next = graph->array[dest].head; 
    graph->array[dest].head = newNode; 
} 

// Structure to represent a min heap node 
struct MinHeapNode 
{ 
    int  v; 
    double dist; 
}; 

// Structure to represent a min heap 
struct MinHeap 
{ 
    int size;      // Number of heap nodes present currently 
    int capacity;  // Capacity of min heap 
    int *pos;     // This is needed for decreaseKey() 
    struct MinHeapNode **array; 
}; 

// A utility function to create a new Min Heap Node 
struct MinHeapNode* newMinHeapNode(int v, double dist) 
{ 
    struct MinHeapNode* minHeapNode = (struct MinHeapNode*) malloc(sizeof(struct MinHeapNode)); 
    minHeapNode->v = v; 
    minHeapNode->dist = dist; 
    return minHeapNode; 
} 

// A utility function to create a Min Heap 
struct MinHeap* createMinHeap(int capacity) 
{ 
    struct MinHeap* minHeap = (struct MinHeap*) malloc(sizeof(struct MinHeap)); 
    minHeap->pos = (int *)malloc(capacity * sizeof(int)); 
    minHeap->size = 0; 
    minHeap->capacity = capacity; 
    minHeap->array = 
         (struct MinHeapNode**) malloc(capacity * sizeof(struct MinHeapNode*)); 
    return minHeap; 
} 

// A utility function to swap two nodes of min heap. Needed for min heapify 
void swapMinHeapNode(struct MinHeapNode** a, struct MinHeapNode** b) 
{ 
    struct MinHeapNode* t = *a; 
    *a = *b; 
    *b = t; 
} 

// A standard function to heapify at given idx 
// This function also updates position of nodes when they are swapped. 
// Position is needed for decreaseKey() 
void minHeapify(struct MinHeap* minHeap, int idx) 
{ 
    int smallest, left, right; 
    smallest = idx; 
    left = 2 * idx + 1; 
    right = 2 * idx + 2; 

    if (left < minHeap->size && 
        minHeap->array[left]->dist < minHeap->array[smallest]->dist ) 
      smallest = left; 

    if (right < minHeap->size && 
        minHeap->array[right]->dist < minHeap->array[smallest]->dist ) 
      smallest = right; 

    if (smallest != idx) 
    { 
        // The nodes to be swapped in min heap 
        MinHeapNode *smallestNode = minHeap->array[smallest]; 
        MinHeapNode *idxNode = minHeap->array[idx]; 

        // Swap positions 
        minHeap->pos[smallestNode->v] = idx; 
        minHeap->pos[idxNode->v] = smallest; 

        // Swap nodes 
        swapMinHeapNode(&minHeap->array[smallest], &minHeap->array[idx]); 

        minHeapify(minHeap, smallest); 
    } 
} 

// A utility function to check if the given minHeap is ampty or not 
int isEmpty(struct MinHeap* minHeap) 
{ 
    return minHeap->size == 0; 
} 

// Standard function to extract minimum node from heap 
struct MinHeapNode* extractMin(struct MinHeap* minHeap) 
{ 
    if (isEmpty(minHeap)) 
        return NULL; 

    // Store the root node 
    struct MinHeapNode* root = minHeap->array[0]; 

    // Replace root node with last node 
    struct MinHeapNode* lastNode = minHeap->array[minHeap->size - 1]; 
    minHeap->array[0] = lastNode; 

    // Update position of last node 
    minHeap->pos[root->v] = minHeap->size-1; 
    minHeap->pos[lastNode->v] = 0; 

    // Reduce heap size and heapify root 
    --minHeap->size; 
    minHeapify(minHeap, 0); 

    return root; 
} 

// Function to decreasy dist value of a given vertex v. This function 
// uses pos[] of min heap to get the current index of node in min heap 
void decreaseKey(struct MinHeap* minHeap, int v, double dist) 
{ 
    // Get the index of v in  heap array 
    int i = minHeap->pos[v]; 

    // Get the node and update its dist value 
    minHeap->array[i]->dist = dist; 

    // Travel up while the complete tree is not hepified. 
    // This is a O(Logn) loop 
    while (i && minHeap->array[i]->dist < minHeap->array[(i - 1) / 2]->dist) 
    { 
        // Swap this node with its parent 
        minHeap->pos[minHeap->array[i]->v] = (i-1)/2; 
        minHeap->pos[minHeap->array[(i-1)/2]->v] = i; 
        swapMinHeapNode(&minHeap->array[i],  &minHeap->array[(i - 1) / 2]); 

        // move to parent index 
        i = (i - 1) / 2; 
    } 
} 

// A utility function to check if a given vertex 
// 'v' is in min heap or not 
bool isInMinHeap(struct MinHeap *minHeap, int v) 
{ 
   if (minHeap->pos[v] < minHeap->size) 
     return true; 
   return false; 
} 

// The main function that calulates distances of shortest paths from src to all 
// vertices. It is a O(ELogV) function 
void dijkstra(struct Graph* const graph, vector<double> &dist, vector<double> const &max, vector<double> const &P_s, const int src){
    int size = graph->V;

    // minHeap represents set E 
    struct MinHeap* minHeap = createMinHeap(size); 

    // Initialize min heap with all vertices. dist value of all vertices  
    for (int v = 0; v < size; ++v) 
    { 
        // Testing different initialization
        dist[v] = max[v];
        // Make dist value of src vertex as 0 so that it is extracted first 
        if (v==src){
            dist[v] = 0;
        }
        minHeap->array[v] = newMinHeapNode(v, dist[v]); 
        minHeap->pos[v] = v; 
    }
    decreaseKey(minHeap, src, dist[src]); 

    // Initially size of min heap is equal to size 
    minHeap->size = size; 

    // In the followin loop, min heap contains all nodes 
    // whose shortest distance is not yet finalized. 
    while (!isEmpty(minHeap)) 
    { 
        // Extract the vertex with minimum distance value 
        struct MinHeapNode* minHeapNode = extractMin(minHeap); 
        int u = minHeapNode->v; // Store the extracted vertex number 

        // Traverse through all adjacent vertices of u (the extracted 
        // vertex) and update their distance values
        struct AdjListNode* pCrawl = graph->array[u].head; 
        while (pCrawl != NULL) 
        { 
            int v = pCrawl->dest;

            // If shortest distance to v is not finalized yet, and distance to v 
            // through u is less than its previously calculated distance 
            if (isInMinHeap(minHeap, v) && dist[u] != DBL_MAX &&  
                                          (pCrawl->weight)/P_s[v] + dist[u] < dist[v]) 
            { 
                dist[v] = dist[u] + pCrawl->weight/P_s[v]; 

                // update distance value in min heap also 
                decreaseKey(minHeap, v, dist[v]); 
            } 
            pCrawl = pCrawl->next; 
        } 
    }

    free(minHeap->pos);
    free(minHeap->array);
    free(minHeap);
}

【问题讨论】:

  • 为什么还要在 C++ 中使用 mallocfree?你的代码是纯 C,除了vector&lt;double&gt; at one place. And you are freeing memory allocated in createMinHeap, but what about memory allocated via newMinHeapNode`? 什么时候你试图释放它,所以它给你一个错误?
  • 鉴于第一条评论,您知道std::vector 的用途吗?似乎您没有,因为在您的代码中的多个地方,您使用C-style 使用mallocfree 创建动态数组。为什么?为此使用std::vector——这就是std::vector 的全部目的,也就是创建动态数组。您已经在使用std::vector,所以这不是您完全不能将它用于您的作业的情况。这就像有一把锤子(矢量),一堆钉子,只用锤子敲几个钉子,然后用你的手作为锤子敲其他钉子。
  • 这段代码取自我在最初帖子中的链接。我最初有一个带有向量的简化实现,但由于它更快(至少 100 倍)而切换到这个实现。我想在最后释放堆,因为我的主程序调用了很多次并且没有修复内存泄漏,它最终会因为内存不足而被杀死。
  • 因为它要快得多(至少 100 倍)。 -- 您是在运行调试版本而不是发布版本吗?除非您做错了什么,或者您没有运行优化的构建,否则您的“快 100 倍”的发现是不可能的。
  • 另外,您未能发布main 函数,因此没有人能够复制您的错误。

标签: c++ memory-leaks dijkstra


【解决方案1】:

我同意前面的cmets,这段代码是纯C,不应该标记为C++。用 C++ 编写它会更容易阅读和调试。使用std::vector 可以防止您的程序丢失任何内存,因为内存分配被封装在这个专用类中。

您可能想学习有关std::vector 和面向对象编程的教程:https://www.tutorialspoint.com/cplusplus/cpp_stl_tutorial.htm

通常,newdelete (C++) 比 mallocfree (C) 更不容易出错。它们有助于避免一些常见错误(错误大小的缓冲区分配......) 类 (C++) 具有初始化(构造函数)和销毁机制,有助于保证有效状态,它们是 C 结构的更好替代品...

【讨论】:

    猜你喜欢
    • 2018-11-29
    • 2012-09-17
    • 2016-07-25
    • 2013-02-28
    • 1970-01-01
    • 2016-03-06
    • 1970-01-01
    • 2020-09-10
    • 2016-05-07
    相关资源
    最近更新 更多