一个小小的双链表拼图

问题描述

免责声明

所以最初当我发布这个时,它是我的错误的前向列表,即使我打算将它作为一个双链表来做 - 这就是为什么有些回复与问题不匹配。

问题

我正在做一些编码,我必须在下面编码。乍一看,代码看起来没什么问题,但逻辑上存在缺陷。你能找到吗?

class Node {
  int val;
  Node *next,*prev;

 public: 
  Node(int val,Node *prev,Node *next) {
    this->val = val;
    this->prev= prev;
    this->next = next;
  }
  int get() {return val;}
  Node* getNext() {return next;}
  Node* getPrev() {return prev;}
};

int main() {
  Node *n1,*n2,*n3;
  n1 = new Node(1,NULL,n2);
  n2 = new Node(2,n1,n3);
  n3 = new Node(3,n2,NULL);

  Node *next = n1;
  while (next != NULL) {
    cout << next->get() << endl;
    next = next->getNext();
  }
}

问题是指针 n1、n2、n3 只有在使用运算符 new 分配内存后才能获得正确的地址。这意味着这些指针的地址在 Node 类实例化时是不同的。例如。在 n2 = new Node(2,n3); 行中的 n3 将是一个错误的指针(因为内存尚未分配 jet 并且当前指向一些废话)。

问题

解决问题不是问题,但让我感到疑惑:

  1. 你能找到一种方法来修复它而不向类 Node 添加任何新的属性方法吗?
  2. 更进一步,您能找到一种在不更改 Node 类中的任何代码的情况下修复它的方法吗?

我的看法

对于第一个问题,我想到了最快的解决方案。我只是将属性 next 和 prev 指向指针并更改了代码,以便它将引用传递给构造函数。还必须更改 while 循环的语法以获取取消引用的值。

对于第二个问题,我们在回复中得到了一个可能的解决方案。我们可以简单地制作一个节点数组。这样我们就知道每个成员的确切地址,因为 n - 数组的第一个成员将有第一个成员的地址 + n - 1;

解决方法

例如。在行 n2 = new Node(2,n3); n3 将是一个坏指针

有一个简单的解决方案。固定初始化顺序,以便从没有依赖项的对象开始,并在它们的依赖项初始化后初始化对象。在这种情况下,“1”依赖于“2”而“2”依赖于“3”,因此:

n3 = new Node(3,nullptr);
n2 = new Node(2,n3);
n1 = new Node(1,n2);

可以提前分配内存吗?

当然。可以分配内存。如果你在做其他事情之前分配内存,那么你就是在做其他事情之前分配内存。

您可以使用数组一次性创建所有节点。您甚至不需要任何动态分配:

Node nodes[] {
    {1,nodes + 1},{2,nodes + 2},{3,nullptr},};
Node* next = nodes;

附言

  • 我建议不要使用裸指针。您的示例会泄漏内存。
  • 不要在 C++ 中使用 NULL。它已被 nullptr 废弃。
  • 无论出于何种原因,您都忘记初始化 prev 成员。
,

我认为改为使用指针到指针是一个非常非常糟糕的主意,而且会非常奇怪。

最简单的方法是反向构建树:

Node * n3 = new Node(3,nullptr);
Node * n2 = new Node(2,n3);
Node * n1 = new Node(1,n2);

这在创建 n2 之前使用 n2 消除了 n1。

但是,您仍然有问题。您在任何时候都不是在管理上一个。所以你可能想这样做:

Node(int val,Node *next) { 这-> val = val; this->next = next;

 // Add these lines.
 if (next != nullptr) {
     next->prev = this;
 }

}

另请注意,使用 NULL 来引用未初始化的指针是非常老式的 C。如果您要编写现代 C++,请改用 nullptr,如您在我的示例中所见。

我还做了一项与您不同的更改。我个人不喜欢这个代码:

Node *n1,*n2,*n3;

我不喜欢它有两个原因,至少在一些公司风格指南中都会提到这两个原因。首先,一些风格指南会告诉你每行只引用一个变量,而不是像你那样引用三个。而且,一些指南会警告您不要让指针未初始化。所以我打破了它,正如你在我上面的例子中看到的那样。

这最后一部分只是风格,但我无法告诉您由于其中一个或其他原因我追查的错误数量。例如,这可以隐藏:

Node *n1,n2,*n3;

根据您的字体大小、眼睛的锐度和月相,您可能看不到 n2 未声明为指针。现在,现代编译器可能会发现不好的用法,但仍然......此外,如果您同意所有变量在创建时都应初始化为合理的值,那么您的代码可能会变得冗长,并且很容易看不到您没有t 初始化一切。

,

似乎您正在尝试创建一个双向链表。错误是您在构造函数中传递了 null 作为参数。如果您按以下顺序分配内存,则可以解决此问题:

Node *n3=new Node(3,NULL);
Node *n2=new Node(2,n3)
Node *n1=new Node(1,n2)
Node *next=n1
while(next!=NULL){
  cout<<next->get();
  next=next->getNext();
}