【问题标题】:Purpose of Using a double pointer in Linked Lists在链表中使用双指针的目的
【发布时间】:2021-05-07 12:00:58
【问题描述】:

我有几个关于这个函数的问题,它打算删除列表中所有出现的给定整数:

void deleteAllOcc (node**headRef,int d)
{   
    node*temp;
    if (*headRef==NULL)
        return;
       
    while (*headRef)
    {
        if ((*headRef)->data==d)
        {
            temp=*headRef;
            *headRef=(*headRef)->next;
            free(temp);        
        }
        else
        {  
            headRef=&((*headRef)->next);
        }
    }
}

首先为什么我们使用**headRef而不是*head?

其次,考虑到这一点:

if ((*headRef)->data==d)
{
    temp=*headRef;
    *headRef=(*headRef)->next;
    free(temp);               
}

我们不需要更新链接吗?

第三,考虑到这一点:

else
{
    headRef=&((*headRef)->next);
}

为什么我们没有做 *headRef=(*headRef)->next);在这里?

提前致谢

【问题讨论】:

    标签: c pointers linked-list pass-by-reference singly-linked-list


    【解决方案1】:

    对于初学者来说,函数可以写得更简单

    void deleteAllOcc( node **headRef, int d )
    {   
        while ( *headRef != NULL )
        {
            if ( ( *headRef )->data == d )
            { 
                node *temp = *headRef;
                *headRef = ( *headRef )->next;
                free( temp );
            }                    
            else
            {  
                headRef = &( *headRef )->next;
            }
        }
    }
    

    即第一次检查通过引用传递的指向头节点的指针是否相等是多余的。

    首先为什么我们使用 **headRef 而不是 *head?

    这取决于函数是如何定义的。在您的问题中提供的函数定义的情况下,指向头节点的指针通过引用传递,即通过指向指针头的指针。在这种情况下,更改函数内的指针头,您正在更改原始函数。

    例如假设列表只包含一个节点,其数据成员data 等于参数d 的指定值。

    主要

    node *head = malloc( sizeof( mode ) );
    head->data = 1;
    head->next = NULL;
    
    deleteAllOcc( &head, 1 );
    

    //...

    在函数deleteAllOcc 内你有

    if((*headRef)->data==d)
    {
        temp=*headRef;
        *headRef=(*headRef)->next;
        free(temp);
        
    }
    

    由于 *headRef 在 main 中产生原始指针 head 然后这个语句

    *headRef=(*headRef)->next;
    

    将 main bot 中的原始指针 head 更改为指针值的副本。

    当指向头节点的指针不是通过引用传递而是通过值传递时,您可以用其他方式定义函数。但在这种情况下,您需要在 main 中为指向头节点的指针返回可能的新值。

    例如

    node * deleteAllOcc( node *head, int d )
    {
        while ( head != NULL && head->data == d )
        {
            node *temp = head;
            head = head->next;
            free( temp );
        }
    
        if ( head != NULL )
        {
            for ( node *current = head; current->next != NULL; )
            {
                if ( current->next->data == d )
                {
                    node *temp = current->next;
                    current->next = current->next->next;
                    free( temp );
                }
                else
                {
                    current = current->next;
                }
            }
        }
    
        return head;
    }
      
    

    并且在 main 中应该调用该函数,例如

    node *head = NULL;
    
    //filling the list
    
    head = deleteAllOcc( head, 1 );
    

    正如你所看到的,当指向头节点的指针通过引用传递时的函数定义看起来更简单。

    删除节点后我们如何更新节点之间的链接

    你有一个指向当前节点的数据成员next 的指针。当前节点的数据成员next指向的节点包含的值等于参数d的值。所以必须删除下一个节点。 ,表示当前节点的数据成员next应该指向被删除节点之后的节点。那就是你需要写

    *headRef = ( *headRef )->next;
    

    其中headRef在当前时刻指向当前时刻的数据成员next 节点。

    |node1| next |  -> |node2| next | -> |node3| next |
             |            |
           headRef      node that must be deleted
    

    所以在headRef指向的数据成员next中,你必须写入被删除节点node2的数据成员next的值。

    为什么我们没有做 *headRef=(*headRef)->next);在这里?

    在这段代码中sn-p

    else
    {
       headRef=&((*headRef)->next);
    }
    

    您正在重新分配指针headRef 以指向下一个节点的数据成员next。如果你会写

    *headRef = ( *headRef )->next;
    

    那么你将丢失当前指向的数据成员next中的表达式*headRef中存储的值当前指向的节点将被更改为重写存储的指针。

    【讨论】:

    • 非常感谢。我认为我在没有完全理解指针是如何工作的情况下直接跳入链表犯了一个大错误。我可以编写一个类似于上述函数的递归版本的工作函数,而无需真正了解每个步骤是如何完成的。
    • @mrreaper72 完全没有。不客气。:)
    【解决方案2】:

    首先为什么我们使用**headRef 而不是*head?

    当您将链表的地址发送给函数时,您会在该链表的第一个元素上创建一个指针。它允许您在列表上进行迭代等。但是如果您想操作完整的列表(或编辑它),您必须在此列表上创建一个指针,(这意味着通过发送链表

    为什么我们没有做 *headRef=(*headRef)->next);在这里?

    您的方法可以工作,但我建议您创建一个指向临时节点 *(node tmp) 的指针,以便为其分配值并交换数据它。它对每个人都更具可读性。

    【讨论】:

    • 对于我的第二个问题,我的意思是删除节点后我们如何更新节点之间的链接
    • 我认为你的解释“如果你想操纵完整的列表”并不完全清楚。我建议添加一个关于按值调用与使用指针模拟按引用调用的解释。
    • 是的,您可以通过这种方式进行操作,或者您可以使用节点 *tmp 来存储实际数据,并以最简单的方式进行交换(不是带有 *&*& *&*&) ;)
    • @mrreaper72 如果您想澄清您的问题,请编辑您的问题并在此处添加说明。
    猜你喜欢
    • 1970-01-01
    • 2020-12-20
    • 2012-04-20
    • 2019-02-08
    • 2019-04-20
    • 1970-01-01
    • 2015-09-09
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多