简体   繁体   English

在链表中插入节点

[英]Inserting node in linked list

I'm trying to insert a node dynamically between two indices which hold struct of type Node. 我试图在两个索引之间动态插入一个节点,这两个索引包含Node类型的结构。 The first element in array is head pointer and second element is tail. 数组中的第一个元素是头指针,第二个元素是尾。

I'm trying to dynamically grow the double linkedlist between the two indices of the array. 我正在尝试动态增长数组的两个索引之间的双链表。 Following is the code I've tried so far. 以下是我到目前为止尝试过的代码。

I could have created head and tail as an node dynamically as well but as per the requirement I've to do like this. 我可以动态地创建头部和尾部作为节点,但是根据我要求我这样做。

It is guarenteed to have the node which is to be inserted data value in between the value of qllentry[0].data and qllentry[1].data 保证在qllentry[0].dataqllentry[1].data值之间插入要插入data值的节点。

#include <stdio.h>
#include <stdlib.h>
#include <limits.h>

struct Node {
    int data;
    struct Node *qprev;
    struct Node *qnext;
}Node;

struct Node qllentry[2];


int main()
{

struct Node head, tail;
    head.data = INT_MAX;
    tail.data = INT_MIN;

    head.qnext = &tail;
    tail.qprev = &head;
    head.qprev = NULL;
    tail.qnext = NULL;


    qllentry[0] = head;
    qllentry[1] = tail;
    int key = 20;
    struct Node *curr ;
    struct Node *prev;
    curr= &qllentry[0];

    while(curr->qnext != NULL && curr->data >= key) {
                curr = curr->qnext;
        }
    prev = curr->qprev;

    struct Node *new_node = (struct Node*)malloc(sizeof(struct Node));
        new_node->data = key;
        new_node->qnext = prev->qnext;
        prev->qnext = new_node;
        new_node->qprev = prev;
        if (new_node->qnext != NULL)
            new_node->qnext->qprev = new_node;


    return 0;
}

The insertion of the new node isn't happening between the head and tail indices as expected. 正如预期的那样,新节点的插入不会发生在头部和尾部索引之间。 I've added few print statements for debugging 我添加了一些用于调试的print语句

Any help is appreciated. 任何帮助表示赞赏。

While there is nothing wrong with keeping an array (or for that matter a pointer) that points to the head and tail of your list, if you use an array, after assigning the address keep your array references out of your list operations . 虽然保持一个指向列表头部和尾部的数组(或指针)没有任何问题,但如果使用数组,则在分配地址后, 请将数组引用保留在列表操作之外 Mixing &array[x] in with your list operations does nothing but cause confusion. 将list &array[x]与列表操作混合会产生混淆。 When working with the list, treat it as a list and forget about the array. 使用列表时,将其视为列表并忘记数组。

Your primary problem is you iterate one node to far looking for where to insert the new_node resulting in you iterating to tail before you stop. 您的主要问题是迭代一个节点到远处查找插入new_node导致您在停止之前迭代到tail Stop your iteration on the node before you insert new_node . 插入new_node 之前停止在节点上的迭代。 You do this by testing for: 你通过测试来做到这一点:

    /* test curr->qnext->data > key to stop before tail */
    while (curr->qnext && curr->qnext->data > key)
                curr = curr->qnext;

( note: masking levels of indirection with a variable like you do next with prev = curr->qprev; just hides details -- which can add to confusion later on. It's perfectly legal, but use with discretion...) 注意: prev = curr->qprev;级别的间接使用变量,就像你接下来使用prev = curr->qprev;只是隐藏细节 - 这可能会在以后增加混乱。这是完全合法的,但可以谨慎使用......)

Now you can concentrate on inserting new_node between &head and &tail where it needs to go. 现在,您可以专注于在&head&tail之间插入new_node

In any list insertion, you are simply re-wiring the pointer->next of the current node to point to the new_node and the pointer->prev of the next node to point to new_node . 在任何列表插入中,您只需重新连接指针 - >当前节点的下一个指向new_node并指向下一个节点的指针 - > prev以指向new_node To finish the insertion your new_node->qprev points to curr and new_node->qnext points to curr->next , eg 要完成插入, new_node->qprev指向currnew_node->qnext指向curr->next ,例如

    new_node->qprev = curr;         /* rewire pointers */
    new_node->qnext = curr->qnext;
    curr->qnext->qprev = new_node;
    curr->qnext = new_node;

( note: the easy way to figure it out is to pull at a piece of paper and a No. 2 pencil and draw a block for curr a block for new_node and a block for tail and then draw lines for prev/next pointers (for both the list without the new_node and with it). Then, with the logic straight, sit down to the keyboard and pecking it out.) 注意:解决问题的简单方法是拉一张纸和一支2号铅笔,然后绘制一个块用于curr一个块用于new_node和一个块用于尾部 ,然后为prev / next指针绘制线条(用于两个没有new_node的列表,还有它。)然后,在逻辑笔直的情况下,坐下来键盘并将其啄出来。)

Further, you must always validate your allocations, eg 此外,您必须始终验证您的分配,例如

    /* allocate and VALIDATE! */
    if (!(new_node = malloc (sizeof *new_node))) {
        perror ("malloc - new_node");
        exit (EXIT_FAILURE);
    }

In any code you write that dynamically allocates memory, you have 2 responsibilities regarding any block of memory allocated: (1) always preserve a pointer to the starting address for the block of memory so, (2) it can be freed when it is no longer needed. 在您编写的任何动态分配内存的代码中,您对分配的任何内存块都有2个职责 :(1) 始终保留指向内存块起始地址的指针,因此,(2)当它为no时可以释放它需要更久。 So if you allocate it, keep track of a pointer to the block and free when you are done with it. 因此,如果您分配它,请跟踪指向块的指针,并在完成后free For example, when done outputting the list values (or in a dedicated loop), you can free the memory you allocate similar to: 例如,当完成输出列表值(或在专用循环中)时,您可以释放您分配的内存,类似于:

    curr = &head;                   /* output list */
    while (curr) {
        printf ("%d\n", curr->data);
        struct Node *victim = curr; /* self-explanatory */
        curr = curr->qnext;
        /* do not forget to free allocated memory */
        if (victim != &head && victim != &tail) {
            free (victim);
        }
    }

Putting it altogether, you can do something like the following: 完全放在一起,您可以执行以下操作:

#include <stdio.h>
#include <stdlib.h>
#include <limits.h>

struct Node {
    int data;
    struct Node *qprev;
    struct Node *qnext;
} Node;

struct Node qllentry[2];


int main (void) {

    struct Node head = { .data = INT_MAX }, 
                tail = { .data = INT_MIN },
                *curr,
                *new_node;

    qllentry[0] = head;     /* keep your array and list operations separate */
    qllentry[1] = tail;

    head.qnext = &tail;     /* begin list operations */
    tail.qprev = &head;

    int key = 20;

    curr = &head;

    /* test curr->qnext->data > key to stop before tail */
    while (curr->qnext && curr->qnext->data > key)
                curr = curr->qnext;

    /* allocate and VALIDATE! */
    if (!(new_node = malloc (sizeof *new_node))) {
        perror ("malloc - new_node");
        exit (EXIT_FAILURE);
    }

    new_node->data = key;           /* assign value to new_node */

    new_node->qprev = curr;         /* rewire pointers */
    new_node->qnext = curr->qnext;
    curr->qnext->qprev = new_node;
    curr->qnext = new_node;

    curr = &head;                   /* output list */
    while (curr) {
        printf ("%d\n", curr->data);
        struct Node *victim = curr; /* self-explanatory */
        curr = curr->qnext;
        /* do not forget to free allocated memory */
        if (victim != &head && victim != &tail) {
            free (victim);
        }
    }

    return 0;
}

Example Use/Output 示例使用/输出

$ ./bin/llarray
2147483647
20
-2147483648

Memory Use/Error Check 内存使用/错误检查

It is imperative that you use a memory error checking program to insure you do not attempt to access memory or write beyond/outside the bounds of your allocated block, attempt to read or base a conditional jump on an uninitialized value, and finally, to confirm that you free all the memory you have allocated. 您必须使用内存错误检查程序,以确保您不会尝试访问内存或写入超出/超出已分配块的范围,尝试读取或基于未初始化值的条件跳转,最后,确认你释放了你分配的所有内存。

For Linux valgrind is the normal choice. 对于Linux, valgrind是正常的选择。 There are similar memory checkers for every platform. 每个平台都有类似的记忆检查器。 They are all simple to use, just run your program through it. 它们都很简单易用,只需通过它运行程序即可。

$ valgrind ./bin/llarray
==8665== Memcheck, a memory error detector
==8665== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==8665== Using Valgrind-3.11.0 and LibVEX; rerun with -h for copyright info
==8665== Command: ./bin/llarray
==8665==
2147483647
20
-2147483648
==8665==
==8665== HEAP SUMMARY:
==8665==     in use at exit: 0 bytes in 0 blocks
==8665==   total heap usage: 1 allocs, 1 frees, 24 bytes allocated
==8665==
==8665== All heap blocks were freed -- no leaks are possible
==8665==
==8665== For counts of detected and suppressed errors, rerun with: -v
==8665== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

Always confirm that you have freed all memory you have allocated and that there are no memory errors. 始终确认已释放已分配的所有内存并且没有内存错误。

Simple Pointer Dump/Check 简单的指针转储/检查

Lastly, in addition to stepping though the addresses with a debugger, you can always write a short debug routing to help you pick out if, and where, you have any problems with your pointer handling. 最后,除了使用调试器逐步调试地址之外,您还可以编写一个简短的调试路由,以帮助您选择是否以及在哪里,您的指针处理有任何问题。 (you don't have to output anything at all, you can just check the addresses with an equality if you like) This lets you look at all pointers at once. (你根本不需要输出任何内容,如果你愿意的话,你可以用相等的方法检查地址)这可以让你一次查看所有指针。 Just a simple routing to output your node pointers is often helpful. 只需输出节点指针的简单路由通常很有帮助。 All you need is, eg 你需要的只是,例如

void debugptrs (struct Node *list)
{
    printf ("list pointers:\n\n");
    for (struct Node *iter = list; iter; iter = iter->qnext)
        printf ("prev: %16p    curr: %16p    next: %16p\n", 
                (void*)iter->qprev, (void*)iter, (void*)iter->qnext);
    putchar ('\n');
}

Which would provide output similar to: 这将提供类似于以下的输出:

$ ./bin/llarray
list pointers:

prev:            (nil)    curr:   0x7ffd56371910    next:        0x1038010
prev:   0x7ffd56371910    curr:        0x1038010    next:   0x7ffd56371930
prev:        0x1038010    curr:   0x7ffd56371930    next:            (nil)

I always found it helpful just to visually traverse the address from head to tail and back. 我总是觉得从头到尾可视地遍历地址是有帮助的。 If any prev or next for a node isn't what is output as the address for that node on the previous (or next) line, you know where you problem is. 如果节点的任何prev或next不是作为前一行(或下一行)上该节点的地址输出的内容,则您知道问题所在。

Look things over and let me know if you have further questions. 仔细看看,如果您有其他问题,请告诉我。

Following is the code with some modification based on the code from the question, it prints the result as expected I guess: 以下是基于问题代码进行一些修改的代码,它按预期打印结果我猜:

dlink.c: dlink.c:

#include <stdio.h>
#include <stdlib.h>
#include <limits.h>

struct Node {
    int data;
    struct Node *qprev;
    struct Node *qnext;
} Snode;

int main() {
    struct Node *head = (struct Node*)malloc(sizeof(struct Node));
    struct Node *tail = (struct Node*)malloc(sizeof(struct Node));

    // init head,
    head->data = INT_MAX;
    head->qnext = tail;
    head->qprev = NULL;

    // init tail,
    tail->data = INT_MIN;
    tail->qprev = head;
    tail->qnext = NULL;

    int key = 20;
    struct Node *curr = head;
    struct Node *prev;

    //get the pointer of the process which has less priority than the current process
    while(curr->data >= key && curr->qnext != NULL) {
        curr = curr->qnext;
    }
    prev = curr->qprev;

    printf("head %p, data is %d, next is %p, prev is %p\n", head, head->data, (void *)head->qnext, (void *)head->qprev);
    printf("tail %p, data is %d, next is %p, prev is %p\n", tail, tail->data, (void *)tail->qnext, (void *)tail->qprev);
    printf("prev of new node %p, data is %d, next is %p, prev is %p\n", prev, prev->data, (void *)prev->qnext, (void *) prev->qprev);
    printf("--------------------\n\n");

    struct Node *new_node = (struct Node*)malloc(sizeof(struct Node));
    new_node->data = key;
    new_node->qnext = prev->qnext;
    prev->qnext = new_node;
    new_node->qprev = prev;

    if (new_node->qnext != NULL)
        new_node->qnext->qprev = new_node;
    else
        tail = new_node;

    printf("head %p, data is %d, next is %p, prev is %p\n", head, head->data, (void *)head->qnext, (void *)head->qprev);
    printf("new_node %p, data is %d, next is %p, prev is %p\n", new_node, new_node->data, (void *)new_node->qnext, (void *)new_node->qprev);
    printf("tail %p, data is %d, next is %p, prev is %p\n", tail, tail->data, (void *)tail->qnext, (void *)tail->qprev);

    return 0;
}

The running result: 运行结果:

head 0x2380010, data is 2147483647, next is 0x2380030, prev is (nil)
tail 0x2380030, data is -2147483648, next is (nil), prev is 0x2380010
prev of new node 0x2380010, data is 2147483647, next is 0x2380030, prev is (nil) // this is same as head,
--------------------

head 0x2380010, data is 2147483647, next is 0x2380460, prev is (nil)
new_node 0x2380460, data is 20, next is 0x2380030, prev is 0x2380010
tail 0x2380030, data is -2147483648, next is (nil), prev is 0x2380460

Suggestions 建议

  • Don't mix struct (head, tail) & struct pointer (new_node), it's confusing, and easy to make mistake. 不要混合struct(head,tail)和struct pointer(new_node),它很容易混淆,容易出错。
  • A singly linked list could be enough to do such insert, there is a tricky way to insert element in a singly linked list. 单个链表可能就足以进行这样的插入,在单链表中插入元素有一种棘手的方法。
  • To get good performance, you can allocate a large cache, then create new node from the cache. 要获得良好性能,可以分配大型缓存,然后从缓存中创建新节点。
  • When compile your c code, add -Wall options, which will give you more warnings. 编译c代码时,添加-Wall选项,这将为您提供更多警告。

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

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