【问题标题】:Changing nodes in linked list更改链表中的节点
【发布时间】:2015-05-24 22:36:21
【问题描述】:

这有点令人难以置信

我会尽力解释我的疑问。

例如检查这个函数:

void snoc(Lint *l, int val){
    Lint i, new;

    new = (Lint) malloc(sizeof(Nodo));
    new->value = val;
    new->next = NULL;

    i=(*l);

    while(i->next!=NULL){
        i=i->next;
    }

    i->next = new;
}

我理解背后的概念,并且我在使用列表时没有问题,考虑到我无法使用列表初始指针本身迭代列表(如果我这样做,我会丢失初始指针)

问题是,创建 i=(*l) 并随后使用 i=i->next 遍历列表,会使 i 变量不断变化。

在这个特定的例子中,原始列表不会改变,直到我找到链表的末尾,然后我做一个归属,瞧!我在最后插入一个元素。

我的疑问是,如果通过更改i,并在末尾设置i->next = new,这是否意味着每次我设置i=i->next,都会更改原始列表中的所有节点?

另一个例子是 init:

void init (Lint *l){
    Lint i, prev;
    i=(*l);
    prev=NULL;

    while(i!=NULL){
        if( (i->next)->next==NULL){
            i->next = NULL;
        }
        i=i->next;
    }
}

如果我这样做,最后一个元素将在适当的时候通过将i->next 更改为 NULL 来删除。但在此之前,我一直在对i 本身进行更改,告诉i=i->next

如果我要对(*l) 本身进行此更改(通过执行(*l)=(*l)->next),我将破坏原始列表。

我真的希望你们能理解我的疑问。

【问题讨论】:

  • 请提供Lint的定义。
  • LintNodo 的定义会有所帮助。我认为 Nodo 是一个结构,而 Lint 是一个 Nodo *
  • 请不要使用“new”作为变量名:Lint i, new;还是我理解不正确?

标签: c list linked-list


【解决方案1】:

是的,我们确实理解您的困惑,这源于没有在一张纸上计算出列表指针,因此您可以直观地看到正在发生的事情。当您处理node 链接问题时,请始终使用图表。例如,在您使用单链表的情况下,当前节点称为 i(名称的可疑选择)和您的新节点称为 new,一个有用的图表将是:

Singly Linked-List (non-circular)

         Tail                 Current                Head
      (list start)               i                   new
     +------------+        +------------+        +------------+
     |   Payload  |        |   Payload  |        |   Payload  |
     +------------+        +------------+        +------------+
     |   Next     |------->|   Next     |------->|   Next     |--->NULL   
     +------------+        +------------+        +------------+        

现在,我敢打赌,你可以说:

修改i,最后改成i->next = new

会的。这不是挖掘或居高临下的意思,它确实是您需要解决链表问题的方式,直到您完成它为止,您可以在脑海中可视化指针布线。当您进行循环或双向链表插入和删除时,这一点更为重要。不管是什么问题,如果你把它写下来并标记所有正在建立或断开的连接,你就可以在你的编辑器中干净地编码。

【讨论】:

  • 那个 ASCII 真的很有启发性!非常感谢,知道了,以后开始画原理图了:)
  • 好旧的铅笔和纸是无可替代的。当我把自己缠绕在车轴上试图找出一个双向链接的循环列表上的列表插入时,我咬紧牙关,我准备尖叫。坐下来画出所涉及的节点后,需要的指针连接——一切都到位了。数据结构越复杂,老2号越重要就变成:p
【解决方案2】:

经过一些小的修改,您的代码是这样的

void snoc(Lint *l, int val)
{
   Lint i, new;
   new = (Lint) malloc(sizeof(Nodo));
   new->value = val;
   new->next = NULL;
   i = *l;  
   if(!(*l))
   {
     *l = new;
     return;
   }
   while(i->next) i = i->next;   // (1)
   i->next = new;                // (2)
}

而且,这是它的解释:

在 (1) 中,我们遍历链表直到其结束。我们知道我们已经结束了,因为i->next(在每个循环步骤之后指向下一个节点项)为空,所以在这种情况下,我们让i->next 指向(2)中新创建的节点项。但是l在函数执行期间从未改变,l总是指向我们链表的开头,所以这个函数的目的是在不改变l的值的情况下在末尾添加一个新元素。

但我们也可以用这个函数初始化我们的链表...例如:

   Lint l = NULL;
   snoc(&l, 10); // l will be initialized with a new nodo item with 10 as the value its value member 

(0) 检查l 是否为空,在这种情况下,我们让*l 指向新分配的节点项并返回...所以初始化完成。

我为上面的代码添加了另一种方式

void snoc(Lint *l, int val)
{
   Lint now, new, previous;
   new = (Lint) malloc(sizeof(Nodo));
   new->value = val;
   new->next = NULL;
   if(!(*l)) // (0)
   {
     *l = new;
     return;
   }
   for(now = *l; now; now = now->next)previous = now;
   previous->next = new;
}

【讨论】:

  • 您修改后的snoc() 将无法正常工作...您需要将i 更改为Lint *,测试*i,并在每个@ 处指向i 987654336@ 循环中的指针(例如:*i = *l; while(*i) { i = &((*i)->next); } *i = new;)。
  • @Dmitri 我不明白你的意思......在这段代码中i = *l 不是i = l 所以iLint 类型而不是Lint * 类型
  • 但它应该Lint *,否则你不会修改最后一个节点的next指针...你只需将i指向它并在函数退出时丢失它。您可以获取指向最后一个节点的指针,然后将其 next 指针设置为新节点(就像其他人所做的那样),或者获取指向最后一个 next 指针的指针并将该指针设置为新节点(这需要另一个级别的间接)。
  • 不看这个语句i=*l 我们只是给i nodo 项目的地址*l 指向它不会改变l 的实际值所以*l总是指向第一个节点项。 (i->next)Lint 类型,不是吗?当我们有这个链表的开始地址时,我们不会错过任何东西,我们总是可以遍历链表......
  • 照原样,在您的while 循环之后,i 将具有与最后一个节点的next 指针相同的值(即为NULL)。然后将i 设置为指向新节点......但这只会修改i,而不是最后一个节点的next 指针。是的,您将保持 *l 不变,并找到列表的末尾,但您实际上并没有添加新节点,因为您没有修改列表末尾的指针 - 只是您的本地 @987654362 @。这就是为什么您需要多一层间接来使其工作......或者停止在最后一个节点(而不是最后一个指针)并设置i->next而不是i
【解决方案3】:

我不确定我是否理解您的问题,但您为什么不简单地复制指针?您可以保留一个指向初始元素的指针,然后使用第二个指针遍历它。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-11-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-04-27
    • 1970-01-01
    • 1970-01-01
    • 2019-10-03
    相关资源
    最近更新 更多