Удаление массива связанного списка в c ++ - PullRequest
0 голосов
/ 25 марта 2020

Я создал сегменты для вставки моих данных, и все работает нормально, за исключением удаления всех узлов. Если я удаляю свой контейнер, используя delete[] container, тогда будут удалены только первые узлы всех моих связанных списков. Я думаю, что я сделал, было логично, но это не работает. Не могли бы вы сказать мне, почему? (Только последний l oop функции имеет значение, другие биты кода для справки.)

#include <iostream>
#include <cmath>
using namespace std;

struct bucket{
    int val;
    bucket* next;
};


int countDigit(int max){
    int digits = 0;
    while(max>0){
        max = max/10;
        digits++;
    }
    return digits;
}

int findMax(int* arr, int l, int r){
    int max = arr[l];
    for(int i = l+1; i<=r; i++){
        if(max<arr[i]){
            max = arr[i];
        }
    }
    return max;
}

void createBucket(int* arr, int l, int r){
    int max = findMax(arr, l, r);
    int digits = findDigit(max);
    int divide = pow(10, digits-1);
    int maxPos = max/divide;
    int pos;//position of elements in the container
    bucket* container = new bucket[maxPos+1];
    bucket* cursor;
    //copying elements to their respective buckets
    for(int i=l; i<=r; i++){

        pos = arr[i]/divide;

        if(container[pos].val == 0){
            container[pos].val = arr[i];
            container[pos].next = NULL;
        }
        else{
            bucket* node = new bucket;
            cursor = &container[pos];
            node->val = arr[i];
            while(cursor->next!=NULL){
                cursor = cursor->next;
            }
            cursor->next = node;
            node->next = NULL;
        }
    }
    //copying the elements from the buckets to their respective position
    int j = 0;
    for(int i = 0; i<maxPos+1; i++){
        cursor = &container[i]; 
        while(cursor!=NULL && cursor->val!=0){
            arr[j] = cursor->val;
            cursor = cursor->next;
            j++;
        }
    }
    //deleting all nodes
    bucket* tmp;
    for(int i= 0; i<maxPos+1; i++){
        cursor = &container[i];
        while(cursor!=NULL){
            tmp = cursor;
            cursor = cursor->next;
            delete tmp;
        }
    }
}

Выдает ошибку следующим образом:

double free or corruption (out)
Aborted (core dumped)

1 Ответ

1 голос
/ 25 марта 2020

Самая большая проблема в том, что у вас нет связанного списка. У вас есть структура узлов, и вы пытаетесь связать узлы вместе, но вам не хватает фактического класса для поддержки вашей структуры данных.

Естественно, когда вы пытаетесь удалить массив, только первый узел каждого индекса удаляется. У вас есть нулевой код для обработки удаления всего списка.

Ниже приведен краткий пример класса связанного списка. Ему не хватает функциональности, но этого достаточно, чтобы продемонстрировать некоторые хорошие практики и то, как это работает.

#include <iostream>

class IntList {
 public:
  IntList() = default;
  ~IntList();                           // Rule of 5
  IntList(const IntList& other);        // Rule of 5
  IntList(IntList&& other) noexcept;    // Rule of 5
  IntList& operator=(IntList other);    // Rule of 5
  IntList& operator=(IntList&& other);  // Rule of 5
  void push_back(int value);
  void print() const;  // Only here for ease of demo

  friend void swap(IntList& lhs, IntList& rhs);

 private:
  struct Node {
    int m_key = 0;
    Node* m_next = nullptr;

    Node(int key) : m_key(key) {}
  };

  Node* m_head = nullptr;
  Node* m_tail = nullptr;
};

IntList::~IntList() {
  while (m_head) {
    Node* tmp = m_head;
    m_head = m_head->m_next;
    delete tmp;
  }

  m_head = nullptr;
  m_tail = nullptr;
}

IntList::IntList(const IntList& other) {
  if (other.m_head) {
    // Set up first node
    this->m_head = new Node(other.m_head->m_key);
    m_tail = m_head;
    Node* otherWalker = other.m_head->m_next;
    while (otherWalker) {  // Handles the rest of the nodes
      m_tail->m_next = new Node(otherWalker->m_key);
      m_tail = m_tail->m_next;
      otherWalker = otherWalker->m_next;
    }
  }
}

IntList::IntList(IntList&& other) noexcept : IntList() { swap(*this, other); }

IntList& IntList::operator=(IntList other) {
  swap(*this, other);

  return *this;
}

IntList& IntList::operator=(IntList&& other) {
  swap(*this, other);

  return *this;
}

void IntList::push_back(int value) {
  Node* tmp = new Node(value);
  if (!m_head) {
    m_head = tmp;
    m_tail = m_head;
    return;
  }

  m_tail->m_next = tmp;
  m_tail = tmp;

  return;
}

void IntList::print() const {
  Node* walker = m_head;

  if (!walker) {
    std::cout << "Empty List.\n";
  }

  while (walker) {
    std::cout << walker->m_key << (walker->m_next ? ' ' : '\n');
    walker = walker->m_next;
  }
}

void swap(IntList& lhs, IntList& rhs) {
  using std::swap;

  swap(lhs.m_head, rhs.m_head);
  swap(lhs.m_tail, lhs.m_tail);
}

int main() {
  IntList one;
  one.push_back(42);
  one.push_back(55);

  IntList two(one);

  IntList three;
  three.push_back(350);
  three.push_back(240);
  three.push_back(609);

  IntList four(three);
  four.push_back(666);


  // While I can appreciate learning, there's no reason for a dynamically 
  // allocated array. SO trope of use a vector instead applies here. NOTE: a 
  // vector would not have helped with your code, it just takes away your 
  // responsibility to delete the array later. You'd still leak memory.
  IntList* const container = new IntList[4];
  container[0] = one;
  container[1] = two;
  container[2] = three;
  container[3] = four;

  for (int i = 0; i < 4; ++i) {
    container[i].print();
  }

  delete[] container;
}

IntList - это класс, представляющий односвязный список целых чисел. Элементы данных m_head и m_tail представляют начало и конец списка соответственно. m_tail необязательно, но я считаю, что это облегчает жизнь, если вы знаете, где заканчивается список. Двойные связанные списки также значительно облегчают вашу жизнь, когда дело доходит до стирания с середины, и их не так сложно писать.

Список состоит из узлов, где узел указывает на местоположение следующего узла. Вы уже получили это.

Ключевой функцией, решающей вашу проблему, является деструктор, ~IntList(). Изучите эту функцию внимательно. Обратите внимание, как он перемещается по всему списку, удаляя каждый узел по мере его прохождения. Это то, что вам не хватает. Это, и фактический класс списка, чтобы делать все это.

Поскольку каждый IntList объект заботится о своей собственной памяти, мне нужно только удалить массив Dynami c. Поскольку массив уничтожает себя, он будет вызывать деструктор каждого элемента. Это означает, что после очистки каждого элемента IntList память массива затем удаляется. Никаких утечек и двойных освобождений.

Самая большая вещь, которая помогает в понимании структур данных, - это рисовать их.

Упоминаемые мной хорошие практики - это правило 5 функций (правило 0/3/5) и копия поменять идиому. Правило 5 необходимо, поскольку класс связанного списка управляет динамическими c ресурсами. Основная функция в полной мере использует конструктор копирования. Обратите внимание, что массив содержит копии списков. Если бы я не хотел копий, мне пришлось бы объявить массив как двойной указатель и выделить массив указателей для IntLists. Затем IntList будут очищаться в конце основной функции.

Идиома копирования-замены просто упрощает жизнь и уменьшает дублирование кода.

...