简体   繁体   English

如何使用指向插入链表的指针

[英]how to use a pointer to pointer to insert in a linked list

Think is a function to insert new element in the order of name. Think是一个按名称顺序插入新元素的函数。 I knew how to do it if I use a if to separate condition of inserting at the start and others. 如果我使用if分隔开始和其他人插入的条件,我知道该怎么做。 But I was asked to merge the if and while into a single while loop. 但是我被要求将if和while合并为一个while循环。 How could i integrate the insert function into one while loop with pointer to pointer? 我如何将插入函数与一个指向指针的指针集成到一个while循环中?

person* insert_sorted(person *people, char *name, int age)
{
    person *p=NULL;//,*t=NULL,*q=NULL;
    person *ptr= people;
    person **ptr2ptr=&ptr;

    p=malloc(sizeof(person));

    if ( p == NULL ){
        printf("malloc() failed\n");
        return NULL;
    }
    else {
        p->name = name;
        p->age = age;

        if ( people == NULL ){ // empty list
            people = p;
            people->next =NULL;
        }
        else{
            *ptr2ptr = ptr;
            while( (*ptr2ptr) !=NULL )
            {
                if ( compare_people(p, people)<=0 )  // insert at the start
                    break;
                else if ( (*ptr2ptr)->next == NULL) //insert at the end
                    break;
                else if ( compare_people(*ptr2ptr, p) <=0 && compare_people( p, (*ptr2ptr)->next)<=0 )//insert at the middle
                    break;
                *ptr2ptr = (*ptr2ptr)->next;
            }
            //insert at the end
            p->next =  (*ptr2ptr)->next;
            (*ptr2ptr)->next = p;

        }
    }

eInstead of trying to find the person element in the list which has no successor, try to find the first null pointer. e与其尝试在列表中没有后继的person元素中查找,不如尝试查找第一个空指针。 Something like this (untested): 像这样(未经测试):

void insert_sorted(person **p, char *name, int age)
{
  while (*p) {
    p = &(*p)->next;
  }
  *p = malloc( ... );
  /* ... */
}

This kind of problem is usually best solved with a pen an paper and then drawing a couple of boxes and arrows. 通常最好用笔,纸,然后画几个方框和箭头来解决这种问题。 The idea is that your 'p' pointer no longer points at a specific person but rather at some pointer which points to a person . 这个想法是,您的“ p”指针不再指向特定的person ,而是指向某person某个指针。

There can be a few options. 可能有一些选择。

I would move the if inside the compare_people function provided that you can change it. 如果可以更改,我将在compare_people函数内部移动if。 After all, adding the very first element in a list is like adding a new "top of the list" element (of least of the list). 毕竟,在列表中添加第一个元素就像添加一个新的“列表顶部”元素(至少是列表中的元素)一样。 I know this can be seen as "cheating". 我知道这可以看作是“作弊”。 And it is, indeed! 确实是这样!

You can create a "fake" list element which will always be tested to be the first (or the last) of the sorted list (like with an empty name). 您可以创建一个“假”列表元素,该元素将始终被测试为已排序列表的第一个(或最后一个)(例如,名称为空)。 So the list won't ever be empty and there won't ever be a "check for an empty list" test. 因此,列表永远不会为空,也不会进行“检查空列表”测试。 Of course the content of that fake item needs to comply with the semantics of the compare_people function. 当然,该假项目的内容需要符合compare_people函数的语义。

At a cost that's slightly higher than the current O(n), O(n*log(n)) actually, you could use a temporary support structure (like an array of pointers) and qsort() from stdlib.h in order to keep the list sorted. 实际上,您可以使用临时支持结构(如指针数组)和来自stdlib.h的qsort()来以稍微高于当前O(n),O(n * log(n))的代价进行操作,保持列表排序。

Finally, implement insertion sort which would exploit the fact that the original set is already sorted before inserting the new element. 最后,实现插入排序 ,它将利用在插入新元素之前已经对原始集合进行排序的事实。

The function can be written the following way (without testing because I do not know some definitions of the list) 该函数可以按以下方式编写(无需测试,因为我不知道列表的某些定义)

person * insert_sorted( person **people, char *name, int age )
{
    person *p = malloc( sizeof( person ) );

    if ( p == NULL )
    {
        printf( "malloc() failed\n" );
    }
    else 
    {
        p->name = name;
        p->age = age;

        person *prev = NULL;
        person *current = *people;

        while ( current && !( compare_people( p, current ) < 0 ) )
        {
            prev = current;
            current = current->next;
        }

        p->next = current;
        if ( prev == NULL ) *people = p;
        else prev->next = p;
    }

    return p;
}    

And the function should be called like 函数应该像

insert_sorted( &people, name, age );
               ^^^^^^^

Without testing: 未经测试:

person* insert_sorted(person** people, char *name, int age) {
    person* added = malloc(sizeof(person));
    added->name = name;
    added->age = age;
    added->next = NULL;

    person* previous = NULL;
    person* current = *people;
    while (current && compare_people(current, added) <= 0) {
        previous = current;
        current = current->next;
    }

    if (!people) {
        *people = added;
    } else {
        previous->next = added;
        added->next = current;
    }

    return added;   
}

The way you use the pointer to pointer doesn't make use of the indirection. 使用指针指向指针的方式没有利用间接寻址。 You only write (*ptr2ptr) where you would normally have written ´ptr`. 您只能在通常会写成“ ptr”的地方写(*ptr2ptr)

The idea of using a pointer to a node pointer is that by adding one level of indirection, you are able to access and modify the head pointer from the calling function. 使用指向节点指针的指针的想法是,通过添加一个间接级别,您可以从调用函数访问和修改头指针。 If you just pass in a node pointer, all changes to that pointer are local to the insert function and will not update the head pointer of your list in the calling function if necessary. 如果仅传递节点指针,则对该指针的所有更改都在insert函数的本地进行,并且在必要时不会更新列表的开头指针。

Your function signature should already pass a pointer to a node pointer: 您的函数签名应该已经将指针传递给节点指针:

void insert(person **p, const char *name, int age);

and call it like so: 并这样称呼它:

person *head = NULL;

insert(&head, "Betty", 26);
insert(&head, "Ralph", 23);
insert(&head, "Chuck", 19);
insert(&head, "Alice", 42);
insert(&head, "Simon", 34);

When you enter the fuction, p is the address of head in the calling function. 输入功能时, p是调用函数中head的地址。 As you iterate through the list with 当您遍历列表时

p = &(*p)->next;

*p hold the address of the next pointer of the previous node. *p保留前一个节点的next指针的地址。 p is a "whence" pointer: It holds the address of the pointer that points to the ode you are processing. p是“因此”指针:它保存指向您正在处理的ode的指针的地址。 That means an empty list isn't a special case any longer. 这意味着空列表不再是一种特殊情况。

Your function requires to return the new head pointer. 您的函数需要返回新的头指针。 It is easy to forget to assign it and it also adds some redundancy to the call. 忘记分配它很容易,它也增加了呼叫的冗余度。 The pointer-to-pointer approach also fixes this. 指针到指针方法也可以解决此问题。

Here's how your insertion code could look like with a function that takes a pointer to pointer as argument: 这是使用将指针的指针作为参数的函数的插入代码的样子:

struct person {
    const char *name;
    int age;
    person *next;
};

int compare(const person *p1, const person *p2)
{
    return strcmp(p1->name, p2->name);
}

person *person_new(const char *name, int age)
{
    person *p = malloc(sizeof(*p));

    p->name = name;
    p->age = age;
    p->next = NULL;

    return p;
}

void insert(person **p, const char *name, int age)
{
    person *pnew = person_new(name, age);

    while (*p && compare(*p, pnew) < 0) {
        p = &(*p)->next;
    }

    pnew->next = *p;
    *p = pnew;
}

here i found the most useful answer to this question: http://www.mvps.org/user32/linkedlist.html 在这里,我找到了这个问题的最有用答案: http : //www.mvps.org/user32/linkedlist.html

       ptr2ptr = &people;
        while ( *ptr2ptr!=NULL && compare_people(*ptr2ptr,p) ) {
            ptr2ptr = &(*ptr2ptr)->next;
        }
        p->next = *ptr2ptr;
        *ptr2ptr = p;

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM