在链表中插入节点
Inserting node in linked list
我正在尝试在两个包含 Node 类型结构的索引之间动态插入一个节点。数组的第一个元素是头指针,第二个元素是尾指针。
我正在尝试动态增长数组的两个索引之间的双链表。以下是我到目前为止尝试过的代码。
我也可以动态创建头和尾作为节点,但根据要求我必须这样做。
保证要插入的节点 data
的值介于 qllentry[0].data
和 qllentry[1].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;
}
新节点的插入未按预期发生在头索引和尾索引之间。我添加了一些用于调试的打印语句
感谢任何帮助。
以下是根据问题中的代码进行了一些修改的代码,它打印的结果符合我的预期:
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;
}
运行结果:
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
建议
- 不要混用 struct (head, tail) 和 struct pointer (new_node),这很容易混淆,也容易出错。
- 一个单向链表就足以进行这样的插入,有一种巧妙的方法可以在单向链表中插入元素。
- 为了获得良好的性能,您可以分配一个大缓存,然后从缓存中创建新节点。
- 编译你的c代码时,添加
-Wall
选项,这会给你更多的警告。
虽然保留指向列表头部和尾部的数组(或指针)没有错,但如果您使用数组,在分配地址后 保持您的列表操作之外的数组引用。将 &array[x]
与您的列表操作混合使用只会造成混乱。使用列表时,将其视为列表而忽略数组。
您的主要问题是您将一个节点迭代到 far 寻找插入 new_node
的位置,导致您在停止之前迭代到 tail
。在插入 new_node
之前 停止对 节点的迭代。您可以通过以下测试来做到这一点:
/* test curr->qnext->data > key to stop before tail */
while (curr->qnext && curr->qnext->data > key)
curr = curr->qnext;
(注意: 像接下来使用 prev = curr->qprev;
那样用变量屏蔽间接级别只是隐藏了细节——这会在以后增加混乱。它非常完美合法,但请谨慎使用...)
现在您可以专注于在 &head
和 &tail
之间插入 new_node
它需要去的地方。
在任何列表插入中,您只需re-wiring当前节点的指针->下一个指向new_node
和指针- >prev 的下一个节点指向 new_node
。要完成插入,您的 new_node->qprev
指向 curr
并且 new_node->qnext
指向 curr->next
,例如
new_node->qprev = curr; /* rewire pointers */
new_node->qnext = curr->qnext;
curr->qnext->qprev = new_node;
curr->qnext = new_node;
(注意:最简单的计算方法是拿一张纸和一支2号铅笔画一个方块curr new_node
的块和 tail 的块,然后为 prev/next 指针画线(对于没有 [=20= 的列表) ] 并用它)。然后,顺着逻辑,坐到键盘前啄出来。)
此外,您必须始终验证您的分配,例如
/* allocate and VALIDATE! */
if (!(new_node = malloc (sizeof *new_node))) {
perror ("malloc - new_node");
exit (EXIT_FAILURE);
}
在您编写的任何动态分配内存的代码中,您对分配的任何内存块负有 2 责任:(1) 始终保留指向内存块的起始地址,因此,(2) 当不再需要它时可以释放。因此,如果您分配它,请跟踪指向该块的指针,并在完成后跟踪 free
。例如,当完成输出列表值(或在专用循环中)时,您可以释放分配的内存,类似于:
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);
}
}
总而言之,您可以执行以下操作:
#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;
}
例子Use/Output
$ ./bin/llarray
2147483647
20
-2147483648
内存Use/Error检查
您必须使用内存错误检查程序来确保您不会尝试访问内存或写入 beyond/outside 您分配的块的边界,尝试读取或基于未初始化的条件跳转值,最后,确认您释放了所有已分配的内存。
对于Linux valgrind
是正常的选择。每个平台都有类似的内存检查器。它们都很简单易用,只需运行你的程序就可以了。
$ 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)
始终确认您已释放所有分配的内存并且没有内存错误。
简单指针Dump/Check
最后,除了使用调试器单步调试地址外,您始终可以编写一个简短的调试路由来帮助您找出您的指针处理是否存在问题以及在何处存在问题。 (你根本不需要输出任何东西,如果你愿意,你可以只检查地址是否相等)这让你可以一次查看所有指针。输出节点指针的简单路由通常很有帮助。你只需要,例如
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');
}
这将提供类似于以下内容的输出:
$ ./bin/llarray
list pointers:
prev: (nil) curr: 0x7ffd56371910 next: 0x1038010
prev: 0x7ffd56371910 curr: 0x1038010 next: 0x7ffd56371930
prev: 0x1038010 curr: 0x7ffd56371930 next: (nil)
我总是发现从头到尾再从头到尾从视觉上遍历地址很有帮助。如果节点的任何 prev 或 next 不是上一行(或下一行)上该节点的地址输出,那么您知道问题出在哪里。
检查一下,如果您还有其他问题,请告诉我。
我正在尝试在两个包含 Node 类型结构的索引之间动态插入一个节点。数组的第一个元素是头指针,第二个元素是尾指针。
我正在尝试动态增长数组的两个索引之间的双链表。以下是我到目前为止尝试过的代码。
我也可以动态创建头和尾作为节点,但根据要求我必须这样做。
保证要插入的节点 data
的值介于 qllentry[0].data
和 qllentry[1].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;
}
新节点的插入未按预期发生在头索引和尾索引之间。我添加了一些用于调试的打印语句
感谢任何帮助。
以下是根据问题中的代码进行了一些修改的代码,它打印的结果符合我的预期:
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;
}
运行结果:
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
建议
- 不要混用 struct (head, tail) 和 struct pointer (new_node),这很容易混淆,也容易出错。
- 一个单向链表就足以进行这样的插入,有一种巧妙的方法可以在单向链表中插入元素。
- 为了获得良好的性能,您可以分配一个大缓存,然后从缓存中创建新节点。
- 编译你的c代码时,添加
-Wall
选项,这会给你更多的警告。
虽然保留指向列表头部和尾部的数组(或指针)没有错,但如果您使用数组,在分配地址后 保持您的列表操作之外的数组引用。将 &array[x]
与您的列表操作混合使用只会造成混乱。使用列表时,将其视为列表而忽略数组。
您的主要问题是您将一个节点迭代到 far 寻找插入 new_node
的位置,导致您在停止之前迭代到 tail
。在插入 new_node
之前 停止对 节点的迭代。您可以通过以下测试来做到这一点:
/* test curr->qnext->data > key to stop before tail */
while (curr->qnext && curr->qnext->data > key)
curr = curr->qnext;
(注意: 像接下来使用 prev = curr->qprev;
那样用变量屏蔽间接级别只是隐藏了细节——这会在以后增加混乱。它非常完美合法,但请谨慎使用...)
现在您可以专注于在 &head
和 &tail
之间插入 new_node
它需要去的地方。
在任何列表插入中,您只需re-wiring当前节点的指针->下一个指向new_node
和指针- >prev 的下一个节点指向 new_node
。要完成插入,您的 new_node->qprev
指向 curr
并且 new_node->qnext
指向 curr->next
,例如
new_node->qprev = curr; /* rewire pointers */
new_node->qnext = curr->qnext;
curr->qnext->qprev = new_node;
curr->qnext = new_node;
(注意:最简单的计算方法是拿一张纸和一支2号铅笔画一个方块curr new_node
的块和 tail 的块,然后为 prev/next 指针画线(对于没有 [=20= 的列表) ] 并用它)。然后,顺着逻辑,坐到键盘前啄出来。)
此外,您必须始终验证您的分配,例如
/* allocate and VALIDATE! */
if (!(new_node = malloc (sizeof *new_node))) {
perror ("malloc - new_node");
exit (EXIT_FAILURE);
}
在您编写的任何动态分配内存的代码中,您对分配的任何内存块负有 2 责任:(1) 始终保留指向内存块的起始地址,因此,(2) 当不再需要它时可以释放。因此,如果您分配它,请跟踪指向该块的指针,并在完成后跟踪 free
。例如,当完成输出列表值(或在专用循环中)时,您可以释放分配的内存,类似于:
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);
}
}
总而言之,您可以执行以下操作:
#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;
}
例子Use/Output
$ ./bin/llarray
2147483647
20
-2147483648
内存Use/Error检查
您必须使用内存错误检查程序来确保您不会尝试访问内存或写入 beyond/outside 您分配的块的边界,尝试读取或基于未初始化的条件跳转值,最后,确认您释放了所有已分配的内存。
对于Linux valgrind
是正常的选择。每个平台都有类似的内存检查器。它们都很简单易用,只需运行你的程序就可以了。
$ 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)
始终确认您已释放所有分配的内存并且没有内存错误。
简单指针Dump/Check
最后,除了使用调试器单步调试地址外,您始终可以编写一个简短的调试路由来帮助您找出您的指针处理是否存在问题以及在何处存在问题。 (你根本不需要输出任何东西,如果你愿意,你可以只检查地址是否相等)这让你可以一次查看所有指针。输出节点指针的简单路由通常很有帮助。你只需要,例如
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');
}
这将提供类似于以下内容的输出:
$ ./bin/llarray
list pointers:
prev: (nil) curr: 0x7ffd56371910 next: 0x1038010
prev: 0x7ffd56371910 curr: 0x1038010 next: 0x7ffd56371930
prev: 0x1038010 curr: 0x7ffd56371930 next: (nil)
我总是发现从头到尾再从头到尾从视觉上遍历地址很有帮助。如果节点的任何 prev 或 next 不是上一行(或下一行)上该节点的地址输出,那么您知道问题出在哪里。
检查一下,如果您还有其他问题,请告诉我。