无法删除C中双向链表的重复数据
Can't delete the dublicated data of doubly linked list in C
我在从双向链表中删除重复数据时遇到问题。所以这个列表的 data
元素是一个数字数组。
我想用这个 delete_duplicates
代码删除重复数据的节点。
我使用以下 print_list
函数来打印列表。该函数似乎没有错误,因为它很好地打印了带有重复项的未排序的第一个列表。
我还使用此 convert_array_to_list
函数从数组中创建列表。同样,这个函数似乎没有错误,因为第一个列表没有任何问题。
编辑:我用亲爱的@Craig Estey 的回答更改了我的代码。这是最新的代码:
编辑:我写了上面程序的完整代码:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
struct doubly_linked_list
{
int data[200];
struct doubly_linked_list *prev;
struct doubly_linked_list *next;
};
void print_list(struct doubly_linked_list *list_head)
{
int i;
printf("\n");
for(i=0; i<200; i++) {
printf("%d ", list_head->data[i]);
list_head = list_head->next;
if (i%25 == 0 & i != 0) {
printf("\n");
}
}
}
struct doubly_linked_list *convert_array_to_list(int array[], int size, struct doubly_linked_list *list_head)
{
struct doubly_linked_list *list_first = NULL; //as the first node
struct doubly_linked_list *list_new = NULL; //as the current node
int i;
if (size <= 0) //just to check
{
printf("Error!");
}
for(i=0; i<size; i++) {
struct doubly_linked_list *list_now = (struct doubly_linked_list*)malloc (sizeof (struct doubly_linked_list)); //as the temporary node
list_now->data[i] = array[i];
if (NULL == list_now) //just to check
{
printf("Error!\n");
break;
}
if(list_new == NULL)
{
list_now->data[i] = array[i];
list_new = list_now;
list_new->prev = NULL;
list_new->next = NULL;
list_first = list_new;
//starting the list as circular
}
else
{
list_now->data[i] = array[i];
list_new->next = list_now;
list_now->prev = list_new;
list_now->next = NULL;
list_new = list_now;
//adding the new node to the end of the list
}
}
return list_first;
}
struct doubly_linked_list *delete_duplicates(struct doubly_linked_list *list_head)
{
int i;
struct doubly_linked_list *left;
struct doubly_linked_list *right;
struct doubly_linked_list *prev;
struct doubly_linked_list *next;
int deleted;
for(left = list_head; left != NULL; left = left->next) {
prev = left;
for(right = left->next; right != NULL; right = next) {
next = right->next;
deleted = 0;
for (i = 0; i < sizeof(left->data) / sizeof(left->data[0]); ++i) {
deleted = (left->data[i] == right->data[i]);
if (deleted) {
break;
}
}
if (deleted) {
prev->next = next;
free(right);
}
else {
prev = right;
}
}
}
};
int *random_array_generator(int array[], int size)
{
int i;
for(i=0; i<size; i++) {
unsigned short int number = rand() % 50; //the array should be from [0,49]
array[i] = number;
}
return array;
};
int main()
{
int i;
int numbers[200];
srand(time(0));
random_array_generator(numbers, 200);
struct doubly_linked_list *list_head = NULL;
list_head = convert_array_to_list(numbers, 200, list_head);
printf("First list with dublication: \n");
print_list(list_head);
printf("\n\n");
list_head = delete_duplicates(list_head);
printf("Second list without dublication: \n");
print_list(list_head);
return 0;
}
第一个列表的输出是完美的,但它没有打印第二个列表。我调试了它并向 left->data[i]
和 right->data[i]
添加了监视,它们似乎无法指向正确的数据。起初,left->data[0]
具有正确的值,而 right->data[0]
具有无意义的大数值。然后随着循环的进行,随着i
的变化,left->data[i]
的值也得到这个无意义的大数的值,程序进入delete_duplicates
函数。毕竟,当它尝试打印第二个列表时,程序会收到一个名为“Segmentation fault”的错误,它无法访问 list_head->data[i]
值。我无法解决问题,我尝试了很多不同代码的组合,但是程序在指向数组的 next
元素时总是出错,它说它无法访问内存或 分段错误。我将不胜感激你的帮助。
非常感谢!
几个问题...
- 在删除
data
元素中字符的单个匹配上的节点后,代码 不会 跳出 i
循环,因此它最多尝试删除同一个节点 200 次。
- 我认为[我没有仔细看]:删除给定节点后,执行
list_head->next->next
是一个“释放后使用”的错误。我们需要在 删除之前获取值 (例如)next = list->next
- 要将每个每个节点与每个其他节点进行比较,我们需要第二个嵌套循环来进行节点比较。
list_head
可以 never 作为重复项删除,因此 no 需要有一个 return list_head;
作为list_head
不会 改变。
- 虽然我们可以有一个单独的
delete_node
函数 [用于其他目的],但实际的删除非常简单,如果 delete_duplicates
直接内联会更容易。
- 在
delete_duplicates
中,我们已经知道前一个节点是什么,所以快到不使用delete_node
(因为它必须重新扫描整个列表以找到前一个节点)。
- 选择重复的标准不明确。使用现有代码,如果
data
中的 any 元素匹配,则删除该节点。虽然这 是 一个 valid 要做的事情,但更常见的是删除一个节点 if all elements匹配。但是,我已经离开了初衷。
这是重构后的代码。注释为:
#include <stdlib.h>
struct doubly_linked_list {
int data[200];
struct doubly_linked_list *prev;
struct doubly_linked_list *next;
};
struct doubly_linked_list *
delete_duplicates(struct doubly_linked_list *list_head)
{
int i;
struct doubly_linked_list *left;
struct doubly_linked_list *right;
struct doubly_linked_list *prev;
struct doubly_linked_list *next;
int del;
for (left = list_head; left != NULL; left = left->next) {
prev = left;
for (right = left->next; right != NULL; right = next) {
// point to next node:
// (1) prevents "use after free" by the iteration expression above
// (2) more efficient
next = right->next;
// say _not_ a duplicate
del = 0;
// scan buffer for a match
for (i = 0; i < sizeof(left->data) / sizeof(left->data[0]); ++i) {
del = (left->data[i] == right->data[i]);
if (del)
break;
}
// delete duplicate node
if (del) {
prev->next = next;
free(right);
}
// advance previous node
else
prev = right;
}
}
// NOTE/BUG: list_head will never change so this isn't needed
return list_head;
}
更新:
Thank you all for your answers. Dear Craig Estey, I used the refactored code you wrote but the output was like, it prints the first element right but then all of the remaining elements were printed as 0. By the way I don't know why my question is closed because of debugging details, I'm still new here though :) –
Ozdamar Kevser
有时,这里的人可能会对票数接近的人过于热心,特别是如果 OP 不回应要求澄清的评论。但是,这里已经有了这个答案。
正如我所提到的,我编写了[并编译]了上面的代码,但没有对其进行测试。
我创建了一个测试程序来验证事情。看起来我的重构函数 确实 工作但我没有在节点中设置 prev
指针 [因为我认为它是一个 单独 链表--现已修复]。但是,我不认为这会导致问题 if 向前打印列表。
因此,这可能与您创建或打印列表的方式有关。
这是带有测试程序的完全重构代码:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
// NDATA -- number of elements in array
#ifndef NDATA
#define NDATA 200
#endif
// ALLMATCH -- duplicate detection critera
// 0=any element can match (original)
// 1=all must match (suitable for testing)
#ifndef ALLMATCH
#define ALLMATCH 1
#endif
typedef struct node node_t;
struct node {
node_t *prev;
node_t *next;
int idx; // easy identifier for debug
int data[NDATA];
};
node_t *global_head;
unsigned int opt_R; // random number seed
int opt_N; // number of nodes to generate
// print_node -- print a node
void
print_node(node_t *head)
{
int totlen = 0;
for (int dataidx = 0; dataidx < NDATA; ++dataidx) {
if (dataidx == 0)
totlen += printf(" NODE ");
totlen += printf(" %6d",head->data[dataidx]);
if (totlen >= 60) {
printf("\n");
totlen = 0;
}
}
totlen += printf(" [%d]",head->idx);
if (totlen > 0)
printf("\n");
}
// print_list -- print list of nodes
void
print_list(node_t *head,const char *who)
{
printf("\n");
printf("%s:\n",who);
for (; head != NULL; head = head->next)
print_node(head);
}
// delete_duplicates -- delete duplicate nodes in list
void
delete_duplicates(node_t *list_head)
{
int i;
node_t *left;
node_t *right;
node_t *prev;
node_t *next;
int del;
for (left = list_head; left != NULL; left = left->next) {
prev = left;
for (right = left->next; right != NULL; right = next) {
// point to next node:
// (1) prevents "use after free" by the iteration expression above
// (2) more efficient
next = right->next;
// say _not_ a duplicate
#if ALLMATCH
del = 1;
#else
del = 0;
#endif
// scan buffer for a match
for (i = 0; i < NDATA; ++i) {
// original -- delete if _any_ element matches
#if ! ALLMATCH
del = (left->data[i] == right->data[i]);
if (del)
break;
// modified -- delete if _all_ element matches
#else
if (left->data[i] != right->data[i]) {
del = 0;
break;
}
#endif
}
// delete duplicate node
if (del) {
printf("delete_duplicates: %d is dup of %d\n",
right->idx,left->idx);
prev = right->prev;
// cross link previous and next nodes (eliminating current)
if (prev != NULL)
prev->next = next;
if (next != NULL)
next->prev = prev;
free(right);
}
}
}
}
// select_node -- select a random previous node
node_t *
select_node(node_t *head,int count)
{
int lstidx = rand() % count;
for (; lstidx > 0; --lstidx)
head = head->next;
if (head == NULL) {
printf("find_dup: null head\n");
exit(1);
}
return head;
}
// generate_list -- generate a list with a random number of duplicates
node_t *
generate_list(int count)
{
node_t *head = NULL;
node_t *prev = NULL;
node_t temp;
int dupcnt = 0;
for (int lstidx = 0; lstidx < count; ++lstidx) {
int dupflg = (rand() % 100) < 20;
if (lstidx == 0)
dupflg = 0;
node_t *dup;
// find a node that we'll duplicate
if (dupflg) {
dup = select_node(head,lstidx);
++dupcnt;
}
// generate new random node
else {
dup = &temp;
for (int dataidx = 0; dataidx < NDATA; ++dataidx)
dup->data[dataidx] = rand() & 0xFFFF;
}
node_t *newnode = malloc(sizeof(*newnode));
// copy in the data
for (int dataidx = 0; dataidx < NDATA; ++dataidx)
newnode->data[dataidx] = dup->data[dataidx];
// set the node id and links
newnode->idx = lstidx;
newnode->prev = prev;
newnode->next = NULL;
// append to tail of list
if (prev != NULL)
prev->next = newnode;
// start new list
else
head = newnode;
prev = newnode;
}
printf("generate_list: %d dups of %d total nodes\n",dupcnt,count);
return head;
}
int
main(int argc,char **argv)
{
--argc;
++argv;
opt_N = 20;
opt_R = 1;
for (; argc > 0; --argc, ++argv) {
char *cp = *argv;
if (*cp != '-')
break;
cp += 2;
switch (cp[-1]) {
case 'N':
opt_N = (*cp != 0) ? atoi(cp) : 50;
break;
case 'R':
opt_R = (*cp != 0) ? atoi(cp) : time(NULL);
break;
}
}
printf("N = %d\n",opt_N);
printf("R = %u\n",opt_R);
srand(opt_R);
global_head = generate_list(opt_N);
print_list(global_head,"ORIGINAL");
delete_duplicates(global_head);
print_list(global_head,"NODUP");
return 0;
}
这是 -DNDATA=1
的程序输出:
N = 20
R = 1
generate_list: 2 dups of 20 total nodes
ORIGINAL:
NODE 9158 [0]
NODE 18547 [1]
NODE 23807 [2]
NODE 22764 [3]
NODE 31949 [4]
NODE 55211 [5]
NODE 7931 [6]
NODE 57670 [7]
NODE 25282 [8]
NODE 10232 [9]
NODE 25282 [10]
NODE 17293 [11]
NODE 9562 [12]
NODE 29283 [13]
NODE 55199 [14]
NODE 1946 [15]
NODE 23858 [16]
NODE 55223 [17]
NODE 58456 [18]
NODE 23858 [19]
delete_duplicates: 10 is dup of 8
delete_duplicates: 19 is dup of 16
NODUP:
NODE 9158 [0]
NODE 18547 [1]
NODE 23807 [2]
NODE 22764 [3]
NODE 31949 [4]
NODE 55211 [5]
NODE 7931 [6]
NODE 57670 [7]
NODE 25282 [8]
NODE 10232 [9]
NODE 17293 [11]
NODE 9562 [12]
NODE 29283 [13]
NODE 55199 [14]
NODE 1946 [15]
NODE 23858 [16]
NODE 55223 [17]
NODE 58456 [18]
更新#2:
So I tried to use your test program in my computer but it didn't work well, it can't escape from printing the list like an infinite loop or so.
当我 运行 上面的程序 [从我的第一次更新] 时,我 没有 看到这个问题。请确保您使用的是我的最新示例完全。
我在 [fedora] linux 下使用 gcc
。大多数其他系统也应该没问题:
- 其他 linux 发行版或 *BSD 系统应该没有问题。
- MacOS 的构建方式可能很古怪(因为编译器是
clang
和 OS 的不同 relocatable/executable 格式)。
- Windows (visual studio) 可能会有问题,但通常也没有问题。
- Windows(
cygwin
或mingw
)应该更好
唯一的其他问题可能是您的系统的 rand
实现产生了不同的随机数,这暴露了我算法中的一个缺陷,因为使用的测试值不同。
And about my program, I tried so many options to delete the duplicates but still couldn't figure out. I edit my question and add the full program so you could debug it that way.
我还没有看到你最新的完整程序的最新更新。
I use the same printing function so it doesn't seem to have a problem at all, the poblem occurs when I try to delete the duplicated data. Thanks a lot. –
Ozdamar Kevser
一些可能的调试思路:
- 运行调试器下的程序(例如
gdb
)
- 运行
valgrind
下的程序
- 用
-fsanitize=address
编译程序
此外,“问题”的确切性质是什么?
我能想到的唯一可能与我的程序不同的是 head
作为列表的使用。
有两种用途:
head
是列表的第一个 有效节点(这是我所做的)。
head
是指向“虚拟”节点的指针。 prev
是列表 head,next
是列表 tail。 IMO,这是“列表”结构的“穷人”实现。
就我个人而言,我不喜欢这两个选项(尤其是 (2))。为了使事情更清楚和更灵活,我更喜欢创建一个单独的列表结构(distinct 来自节点结构)。这类似于 (2),但更清晰、更明确。它还允许额外的信息,例如列表计数。
这是一个进一步重构的版本。它还 运行 使用不同的随机值多次测试:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define sysfault(_fmt...) \
do { \
printf(_fmt); \
exit(1); \
} while (0)
// NDATA -- number of elements in array
#ifndef NDATA
#define NDATA 1
#endif
// ALLMATCH -- duplicate detection critera
// 0=any element can match (original)
// 1=all must match (suitable for testing)
#ifndef ALLMATCH
#define ALLMATCH 1
#endif
typedef struct node node_t;
struct node {
node_t *prev;
node_t *next;
int idx; // easy identifier for debug
int data[NDATA];
};
typedef struct list {
int count;
node_t *head;
node_t *tail;
} list_t;
list_t *global_list; // list that may have duplicates
list_t *nodup_list; // same list _without_ duplicates
int opt_N; // number of nodes to generate
unsigned int opt_R; // random number seed
int opt_T; // number of tests
// node_print -- print a node
void
node_print(node_t *cur)
{
int totlen = 0;
for (int dataidx = 0; dataidx < NDATA; ++dataidx) {
if (dataidx == 0)
totlen += printf(" NODE ");
totlen += printf(" %6d",cur->data[dataidx]);
if (totlen >= 60) {
printf("\n");
totlen = 0;
}
}
totlen += printf(" [%d]",cur->idx);
if (totlen > 0)
printf("\n");
}
// list_print -- print list of nodes (forward)
void
list_print(list_t *list,const char *who)
{
node_t *cur;
printf("\n");
printf("%s: (%d elements)\n",who,list->count);
for (cur = list->head; cur != NULL; cur = cur->next)
node_print(cur);
}
// list_rprint -- print list of nodes (reverse)
void
list_rprint(list_t *list,const char *who)
{
node_t *cur;
printf("\n");
printf("%s: (%d elements)\n",who,list->count);
for (cur = list->tail; cur != NULL; cur = cur->prev)
node_print(cur);
}
// node_equal -- compare two nodes
// RETURNS: 0=mismatch, 1=equal
int
node_equal(node_t *lhs,node_t *rhs)
{
int match;
// say _not_ a match
#if ALLMATCH
match = 1;
#else
match = 0;
#endif
// scan buffer for a match
for (int dataidx = 0; dataidx < NDATA; ++dataidx) {
// original -- delete if _any_ element matches
#if ! ALLMATCH
match = (lhs->data[dataidx] == rhs->data[dataidx]);
if (match)
break;
// modified -- delete if _all_ element matches
#else
if (lhs->data[dataidx] != rhs->data[dataidx]) {
match = 0;
break;
}
#endif
}
return match;
}
// list_unlink -- unlink node from list
node_t *
list_unlink(list_t *list,node_t *del)
{
node_t *prev;
node_t *next;
next = del->next;
prev = del->prev;
// cross link previous and next nodes (eliminating current)
if (prev != NULL)
prev->next = next;
if (next != NULL)
next->prev = prev;
if (list->head == del)
list->head = next;
if (list->tail == del)
list->tail = prev;
list->count -= 1;
free(del);
return next;
}
// delete_duplicates -- delete duplicate nodes in list
void
delete_duplicates(list_t *list)
{
node_t *left;
node_t *right;
node_t *next;
int del;
for (left = list->head; left != NULL; left = left->next) {
for (right = left->next; right != NULL; right = next) {
// decide if the node is a duplicate
del = node_equal(left,right);
// delete duplicate node
if (del) {
printf("delete_duplicates: %d is dup of %d\n",
right->idx,left->idx);
next = list_unlink(list,right);
}
// advance to next node
else
next = right->next;
}
}
}
// list_select -- select a random previous node
node_t *
list_select(list_t *list)
{
int lstidx = rand() % list->count;
node_t *cur = list->head;
for (; lstidx > 0; --lstidx)
cur = cur->next;
if (cur == NULL)
sysfault("list_select: null cur\n");
return cur;
}
// list_append -- append to list
void
list_append(list_t *list,node_t *src)
{
node_t *newnode = malloc(sizeof(*newnode));
// copy in the data
for (int dataidx = 0; dataidx < NDATA; ++dataidx)
newnode->data[dataidx] = src->data[dataidx];
node_t *tail = list->tail;
// set the node id and links
newnode->idx = list->count++;
newnode->prev = tail;
newnode->next = NULL;
// start of new list
if (list->head == NULL)
list->head = newnode;
// set tail's forward link
if (tail != NULL)
tail->next = newnode;
// set new node's backward link
newnode->prev = tail;
// make new node the tail of the list
list->tail = newnode;
}
// list_generate -- generate a list with a random number of duplicates
void
list_generate(int count)
{
node_t temp;
int dupcnt = 0;
// list that can have dups
global_list = calloc(1,sizeof(*global_list));
// list without dups
nodup_list = calloc(1,sizeof(*nodup_list));
for (int lstidx = 0; lstidx < count; ++lstidx) {
int dupflg = (rand() % 100) < 20;
if (lstidx == 0)
dupflg = 0;
node_t *cur;
// find a node that we'll duplicate
if (dupflg) {
cur = list_select(global_list);
++dupcnt;
}
// generate new random node
else {
cur = &temp;
for (int dataidx = 0; dataidx < NDATA; ++dataidx)
cur->data[dataidx] = rand() & 0xFFFF;
list_append(nodup_list,cur);
}
list_append(global_list,cur);
}
printf("list_generate: %d dups of %d total nodes\n",dupcnt,count);
}
// list_destroy -- destroy a list
list_t *
list_destroy(list_t *list)
{
// free all nodes in list
node_t *next;
for (node_t *cur = list->head; cur != NULL; cur = next) {
next = cur->next;
free(cur);
}
// free the list itself
free(list);
list = NULL;
return list;
}
// list_compare -- compare two lists
void
list_compare(list_t *lhslist,list_t *rhslist)
{
node_t *lhs = lhslist->head;
node_t *rhs = rhslist->head;
if (lhslist->count != rhslist->count)
sysfault("list_compare: count mismatch -- lhs=%d rhs=%d\n",
lhslist->count,rhslist->count);
while (1) {
if (lhs == NULL)
break;
if (rhs == NULL)
break;
int same = node_equal(lhs,rhs);
if (! same) {
printf("list_compare: mismatch\n");
node_print(lhs);
node_print(rhs);
sysfault("list_compare: aborting\n");
}
lhs = lhs->next;
rhs = rhs->next;
}
printf("list_compare: complete\n");
}
// dotest -- do a test
void
dotest(int tstno)
{
if (tstno > 1)
printf("\n");
for (int sep = 1; sep <= 80; ++sep)
fputc('-',stdout);
fputc('\n',stdout);
printf("TEST %d of %d\n",tstno,opt_T);
list_generate(opt_N);
list_print(global_list,"global_list/ORIGINAL");
list_print(nodup_list,"nodup_list");
delete_duplicates(global_list);
list_print(global_list,"global_list/NODUP");
list_rprint(global_list,"global_list/REVERSE");
printf("\n");
list_compare(global_list,nodup_list);
global_list = list_destroy(global_list);
nodup_list = list_destroy(nodup_list);
}
int
main(int argc,char **argv)
{
--argc;
++argv;
opt_N = 20;
opt_R = 1;
opt_T = 5;
for (; argc > 0; --argc, ++argv) {
char *cp = *argv;
if (*cp != '-')
break;
cp += 2;
switch (cp[-1]) {
case 'N':
opt_N = (*cp != 0) ? atoi(cp) : 50;
break;
case 'R':
opt_R = (*cp != 0) ? atoi(cp) : time(NULL);
break;
case 'T':
opt_T = (*cp != 0) ? atoi(cp) : 10;
break;
}
}
printf("N = %d\n",opt_N);
printf("R = %u\n",opt_R);
srand(opt_R);
if (opt_T <= 0)
opt_T = 1;
printf("T = %d\n",opt_T);
for (int tstno = 1; tstno < opt_T; ++tstno)
dotest(tstno);
return 0;
}
下面是程序输出。
请注意,我还 built/ran 下的程序:
valgrind
没有 错误
- 使用
-fsanitize=address
编译(无错误)
N = 20
R = 1
T = 5
--------------------------------------------------------------------------------
TEST 1 of 5
list_generate: 2 dups of 20 total nodes
global_list/ORIGINAL: (20 elements)
NODE 9158 [0]
NODE 18547 [1]
NODE 23807 [2]
NODE 22764 [3]
NODE 31949 [4]
NODE 55211 [5]
NODE 7931 [6]
NODE 57670 [7]
NODE 25282 [8]
NODE 10232 [9]
NODE 25282 [10]
NODE 17293 [11]
NODE 9562 [12]
NODE 29283 [13]
NODE 55199 [14]
NODE 1946 [15]
NODE 23858 [16]
NODE 55223 [17]
NODE 58456 [18]
NODE 23858 [19]
nodup_list: (18 elements)
NODE 9158 [0]
NODE 18547 [1]
NODE 23807 [2]
NODE 22764 [3]
NODE 31949 [4]
NODE 55211 [5]
NODE 7931 [6]
NODE 57670 [7]
NODE 25282 [8]
NODE 10232 [9]
NODE 17293 [10]
NODE 9562 [11]
NODE 29283 [12]
NODE 55199 [13]
NODE 1946 [14]
NODE 23858 [15]
NODE 55223 [16]
NODE 58456 [17]
delete_duplicates: 10 is dup of 8
delete_duplicates: 19 is dup of 16
global_list/NODUP: (18 elements)
NODE 9158 [0]
NODE 18547 [1]
NODE 23807 [2]
NODE 22764 [3]
NODE 31949 [4]
NODE 55211 [5]
NODE 7931 [6]
NODE 57670 [7]
NODE 25282 [8]
NODE 10232 [9]
NODE 17293 [11]
NODE 9562 [12]
NODE 29283 [13]
NODE 55199 [14]
NODE 1946 [15]
NODE 23858 [16]
NODE 55223 [17]
NODE 58456 [18]
global_list/REVERSE: (18 elements)
NODE 58456 [18]
NODE 55223 [17]
NODE 23858 [16]
NODE 1946 [15]
NODE 55199 [14]
NODE 29283 [13]
NODE 9562 [12]
NODE 17293 [11]
NODE 10232 [9]
NODE 25282 [8]
NODE 57670 [7]
NODE 7931 [6]
NODE 55211 [5]
NODE 31949 [4]
NODE 22764 [3]
NODE 23807 [2]
NODE 18547 [1]
NODE 9158 [0]
list_compare: complete
--------------------------------------------------------------------------------
TEST 2 of 5
list_generate: 5 dups of 20 total nodes
global_list/ORIGINAL: (20 elements)
NODE 35165 [0]
NODE 41751 [1]
NODE 23273 [2]
NODE 43220 [3]
NODE 23273 [4]
NODE 41751 [5]
NODE 40628 [6]
NODE 34321 [7]
NODE 7554 [8]
NODE 34369 [9]
NODE 41751 [10]
NODE 61575 [11]
NODE 56809 [12]
NODE 54433 [13]
NODE 34321 [14]
NODE 9063 [15]
NODE 24321 [16]
NODE 35165 [17]
NODE 19164 [18]
NODE 30614 [19]
nodup_list: (15 elements)
NODE 35165 [0]
NODE 41751 [1]
NODE 23273 [2]
NODE 43220 [3]
NODE 40628 [4]
NODE 34321 [5]
NODE 7554 [6]
NODE 34369 [7]
NODE 61575 [8]
NODE 56809 [9]
NODE 54433 [10]
NODE 9063 [11]
NODE 24321 [12]
NODE 19164 [13]
NODE 30614 [14]
delete_duplicates: 17 is dup of 0
delete_duplicates: 5 is dup of 1
delete_duplicates: 10 is dup of 1
delete_duplicates: 4 is dup of 2
delete_duplicates: 14 is dup of 7
global_list/NODUP: (15 elements)
NODE 35165 [0]
NODE 41751 [1]
NODE 23273 [2]
NODE 43220 [3]
NODE 40628 [6]
NODE 34321 [7]
NODE 7554 [8]
NODE 34369 [9]
NODE 61575 [11]
NODE 56809 [12]
NODE 54433 [13]
NODE 9063 [15]
NODE 24321 [16]
NODE 19164 [18]
NODE 30614 [19]
global_list/REVERSE: (15 elements)
NODE 30614 [19]
NODE 19164 [18]
NODE 24321 [16]
NODE 9063 [15]
NODE 54433 [13]
NODE 56809 [12]
NODE 61575 [11]
NODE 34369 [9]
NODE 7554 [8]
NODE 34321 [7]
NODE 40628 [6]
NODE 43220 [3]
NODE 23273 [2]
NODE 41751 [1]
NODE 35165 [0]
list_compare: complete
--------------------------------------------------------------------------------
TEST 3 of 5
list_generate: 3 dups of 20 total nodes
global_list/ORIGINAL: (20 elements)
NODE 42040 [0]
NODE 20010 [1]
NODE 31920 [2]
NODE 31920 [3]
NODE 52399 [4]
NODE 36692 [5]
NODE 6936 [6]
NODE 42076 [7]
NODE 18458 [8]
NODE 47939 [9]
NODE 9978 [10]
NODE 49978 [11]
NODE 42281 [12]
NODE 16358 [13]
NODE 42040 [14]
NODE 51092 [15]
NODE 18458 [16]
NODE 43105 [17]
NODE 59897 [18]
NODE 9915 [19]
nodup_list: (17 elements)
NODE 42040 [0]
NODE 20010 [1]
NODE 31920 [2]
NODE 52399 [3]
NODE 36692 [4]
NODE 6936 [5]
NODE 42076 [6]
NODE 18458 [7]
NODE 47939 [8]
NODE 9978 [9]
NODE 49978 [10]
NODE 42281 [11]
NODE 16358 [12]
NODE 51092 [13]
NODE 43105 [14]
NODE 59897 [15]
NODE 9915 [16]
delete_duplicates: 14 is dup of 0
delete_duplicates: 3 is dup of 2
delete_duplicates: 16 is dup of 8
global_list/NODUP: (17 elements)
NODE 42040 [0]
NODE 20010 [1]
NODE 31920 [2]
NODE 52399 [4]
NODE 36692 [5]
NODE 6936 [6]
NODE 42076 [7]
NODE 18458 [8]
NODE 47939 [9]
NODE 9978 [10]
NODE 49978 [11]
NODE 42281 [12]
NODE 16358 [13]
NODE 51092 [15]
NODE 43105 [17]
NODE 59897 [18]
NODE 9915 [19]
global_list/REVERSE: (17 elements)
NODE 9915 [19]
NODE 59897 [18]
NODE 43105 [17]
NODE 51092 [15]
NODE 16358 [13]
NODE 42281 [12]
NODE 49978 [11]
NODE 9978 [10]
NODE 47939 [9]
NODE 18458 [8]
NODE 42076 [7]
NODE 6936 [6]
NODE 36692 [5]
NODE 52399 [4]
NODE 31920 [2]
NODE 20010 [1]
NODE 42040 [0]
list_compare: complete
--------------------------------------------------------------------------------
TEST 4 of 5
list_generate: 6 dups of 20 total nodes
global_list/ORIGINAL: (20 elements)
NODE 15513 [0]
NODE 16533 [1]
NODE 13803 [2]
NODE 20659 [3]
NODE 20659 [4]
NODE 48896 [5]
NODE 60065 [6]
NODE 2789 [7]
NODE 20659 [8]
NODE 13803 [9]
NODE 583 [10]
NODE 38589 [11]
NODE 38589 [12]
NODE 40616 [13]
NODE 20659 [14]
NODE 64965 [15]
NODE 31603 [16]
NODE 15513 [17]
NODE 9035 [18]
NODE 12131 [19]
nodup_list: (14 elements)
NODE 15513 [0]
NODE 16533 [1]
NODE 13803 [2]
NODE 20659 [3]
NODE 48896 [4]
NODE 60065 [5]
NODE 2789 [6]
NODE 583 [7]
NODE 38589 [8]
NODE 40616 [9]
NODE 64965 [10]
NODE 31603 [11]
NODE 9035 [12]
NODE 12131 [13]
delete_duplicates: 17 is dup of 0
delete_duplicates: 9 is dup of 2
delete_duplicates: 4 is dup of 3
delete_duplicates: 8 is dup of 3
delete_duplicates: 14 is dup of 3
delete_duplicates: 12 is dup of 11
global_list/NODUP: (14 elements)
NODE 15513 [0]
NODE 16533 [1]
NODE 13803 [2]
NODE 20659 [3]
NODE 48896 [5]
NODE 60065 [6]
NODE 2789 [7]
NODE 583 [10]
NODE 38589 [11]
NODE 40616 [13]
NODE 64965 [15]
NODE 31603 [16]
NODE 9035 [18]
NODE 12131 [19]
global_list/REVERSE: (14 elements)
NODE 12131 [19]
NODE 9035 [18]
NODE 31603 [16]
NODE 64965 [15]
NODE 40616 [13]
NODE 38589 [11]
NODE 583 [10]
NODE 2789 [7]
NODE 60065 [6]
NODE 48896 [5]
NODE 20659 [3]
NODE 13803 [2]
NODE 16533 [1]
NODE 15513 [0]
list_compare: complete
我在从双向链表中删除重复数据时遇到问题。所以这个列表的 data
元素是一个数字数组。
我想用这个 delete_duplicates
代码删除重复数据的节点。
我使用以下 print_list
函数来打印列表。该函数似乎没有错误,因为它很好地打印了带有重复项的未排序的第一个列表。
我还使用此 convert_array_to_list
函数从数组中创建列表。同样,这个函数似乎没有错误,因为第一个列表没有任何问题。
编辑:我用亲爱的@Craig Estey 的回答更改了我的代码。这是最新的代码:
编辑:我写了上面程序的完整代码:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
struct doubly_linked_list
{
int data[200];
struct doubly_linked_list *prev;
struct doubly_linked_list *next;
};
void print_list(struct doubly_linked_list *list_head)
{
int i;
printf("\n");
for(i=0; i<200; i++) {
printf("%d ", list_head->data[i]);
list_head = list_head->next;
if (i%25 == 0 & i != 0) {
printf("\n");
}
}
}
struct doubly_linked_list *convert_array_to_list(int array[], int size, struct doubly_linked_list *list_head)
{
struct doubly_linked_list *list_first = NULL; //as the first node
struct doubly_linked_list *list_new = NULL; //as the current node
int i;
if (size <= 0) //just to check
{
printf("Error!");
}
for(i=0; i<size; i++) {
struct doubly_linked_list *list_now = (struct doubly_linked_list*)malloc (sizeof (struct doubly_linked_list)); //as the temporary node
list_now->data[i] = array[i];
if (NULL == list_now) //just to check
{
printf("Error!\n");
break;
}
if(list_new == NULL)
{
list_now->data[i] = array[i];
list_new = list_now;
list_new->prev = NULL;
list_new->next = NULL;
list_first = list_new;
//starting the list as circular
}
else
{
list_now->data[i] = array[i];
list_new->next = list_now;
list_now->prev = list_new;
list_now->next = NULL;
list_new = list_now;
//adding the new node to the end of the list
}
}
return list_first;
}
struct doubly_linked_list *delete_duplicates(struct doubly_linked_list *list_head)
{
int i;
struct doubly_linked_list *left;
struct doubly_linked_list *right;
struct doubly_linked_list *prev;
struct doubly_linked_list *next;
int deleted;
for(left = list_head; left != NULL; left = left->next) {
prev = left;
for(right = left->next; right != NULL; right = next) {
next = right->next;
deleted = 0;
for (i = 0; i < sizeof(left->data) / sizeof(left->data[0]); ++i) {
deleted = (left->data[i] == right->data[i]);
if (deleted) {
break;
}
}
if (deleted) {
prev->next = next;
free(right);
}
else {
prev = right;
}
}
}
};
int *random_array_generator(int array[], int size)
{
int i;
for(i=0; i<size; i++) {
unsigned short int number = rand() % 50; //the array should be from [0,49]
array[i] = number;
}
return array;
};
int main()
{
int i;
int numbers[200];
srand(time(0));
random_array_generator(numbers, 200);
struct doubly_linked_list *list_head = NULL;
list_head = convert_array_to_list(numbers, 200, list_head);
printf("First list with dublication: \n");
print_list(list_head);
printf("\n\n");
list_head = delete_duplicates(list_head);
printf("Second list without dublication: \n");
print_list(list_head);
return 0;
}
第一个列表的输出是完美的,但它没有打印第二个列表。我调试了它并向 left->data[i]
和 right->data[i]
添加了监视,它们似乎无法指向正确的数据。起初,left->data[0]
具有正确的值,而 right->data[0]
具有无意义的大数值。然后随着循环的进行,随着i
的变化,left->data[i]
的值也得到这个无意义的大数的值,程序进入delete_duplicates
函数。毕竟,当它尝试打印第二个列表时,程序会收到一个名为“Segmentation fault”的错误,它无法访问 list_head->data[i]
值。我无法解决问题,我尝试了很多不同代码的组合,但是程序在指向数组的 next
元素时总是出错,它说它无法访问内存或 分段错误。我将不胜感激你的帮助。
非常感谢!
几个问题...
- 在删除
data
元素中字符的单个匹配上的节点后,代码 不会 跳出i
循环,因此它最多尝试删除同一个节点 200 次。 - 我认为[我没有仔细看]:删除给定节点后,执行
list_head->next->next
是一个“释放后使用”的错误。我们需要在 删除之前获取值 (例如)next = list->next
- 要将每个每个节点与每个其他节点进行比较,我们需要第二个嵌套循环来进行节点比较。
list_head
可以 never 作为重复项删除,因此 no 需要有一个return list_head;
作为list_head
不会 改变。- 虽然我们可以有一个单独的
delete_node
函数 [用于其他目的],但实际的删除非常简单,如果delete_duplicates
直接内联会更容易。 - 在
delete_duplicates
中,我们已经知道前一个节点是什么,所以快到不使用delete_node
(因为它必须重新扫描整个列表以找到前一个节点)。 - 选择重复的标准不明确。使用现有代码,如果
data
中的 any 元素匹配,则删除该节点。虽然这 是 一个 valid 要做的事情,但更常见的是删除一个节点 if all elements匹配。但是,我已经离开了初衷。
这是重构后的代码。注释为:
#include <stdlib.h>
struct doubly_linked_list {
int data[200];
struct doubly_linked_list *prev;
struct doubly_linked_list *next;
};
struct doubly_linked_list *
delete_duplicates(struct doubly_linked_list *list_head)
{
int i;
struct doubly_linked_list *left;
struct doubly_linked_list *right;
struct doubly_linked_list *prev;
struct doubly_linked_list *next;
int del;
for (left = list_head; left != NULL; left = left->next) {
prev = left;
for (right = left->next; right != NULL; right = next) {
// point to next node:
// (1) prevents "use after free" by the iteration expression above
// (2) more efficient
next = right->next;
// say _not_ a duplicate
del = 0;
// scan buffer for a match
for (i = 0; i < sizeof(left->data) / sizeof(left->data[0]); ++i) {
del = (left->data[i] == right->data[i]);
if (del)
break;
}
// delete duplicate node
if (del) {
prev->next = next;
free(right);
}
// advance previous node
else
prev = right;
}
}
// NOTE/BUG: list_head will never change so this isn't needed
return list_head;
}
更新:
Thank you all for your answers. Dear Craig Estey, I used the refactored code you wrote but the output was like, it prints the first element right but then all of the remaining elements were printed as 0. By the way I don't know why my question is closed because of debugging details, I'm still new here though :) – Ozdamar Kevser
有时,这里的人可能会对票数接近的人过于热心,特别是如果 OP 不回应要求澄清的评论。但是,这里已经有了这个答案。
正如我所提到的,我编写了[并编译]了上面的代码,但没有对其进行测试。
我创建了一个测试程序来验证事情。看起来我的重构函数 确实 工作但我没有在节点中设置 prev
指针 [因为我认为它是一个 单独 链表--现已修复]。但是,我不认为这会导致问题 if 向前打印列表。
因此,这可能与您创建或打印列表的方式有关。
这是带有测试程序的完全重构代码:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
// NDATA -- number of elements in array
#ifndef NDATA
#define NDATA 200
#endif
// ALLMATCH -- duplicate detection critera
// 0=any element can match (original)
// 1=all must match (suitable for testing)
#ifndef ALLMATCH
#define ALLMATCH 1
#endif
typedef struct node node_t;
struct node {
node_t *prev;
node_t *next;
int idx; // easy identifier for debug
int data[NDATA];
};
node_t *global_head;
unsigned int opt_R; // random number seed
int opt_N; // number of nodes to generate
// print_node -- print a node
void
print_node(node_t *head)
{
int totlen = 0;
for (int dataidx = 0; dataidx < NDATA; ++dataidx) {
if (dataidx == 0)
totlen += printf(" NODE ");
totlen += printf(" %6d",head->data[dataidx]);
if (totlen >= 60) {
printf("\n");
totlen = 0;
}
}
totlen += printf(" [%d]",head->idx);
if (totlen > 0)
printf("\n");
}
// print_list -- print list of nodes
void
print_list(node_t *head,const char *who)
{
printf("\n");
printf("%s:\n",who);
for (; head != NULL; head = head->next)
print_node(head);
}
// delete_duplicates -- delete duplicate nodes in list
void
delete_duplicates(node_t *list_head)
{
int i;
node_t *left;
node_t *right;
node_t *prev;
node_t *next;
int del;
for (left = list_head; left != NULL; left = left->next) {
prev = left;
for (right = left->next; right != NULL; right = next) {
// point to next node:
// (1) prevents "use after free" by the iteration expression above
// (2) more efficient
next = right->next;
// say _not_ a duplicate
#if ALLMATCH
del = 1;
#else
del = 0;
#endif
// scan buffer for a match
for (i = 0; i < NDATA; ++i) {
// original -- delete if _any_ element matches
#if ! ALLMATCH
del = (left->data[i] == right->data[i]);
if (del)
break;
// modified -- delete if _all_ element matches
#else
if (left->data[i] != right->data[i]) {
del = 0;
break;
}
#endif
}
// delete duplicate node
if (del) {
printf("delete_duplicates: %d is dup of %d\n",
right->idx,left->idx);
prev = right->prev;
// cross link previous and next nodes (eliminating current)
if (prev != NULL)
prev->next = next;
if (next != NULL)
next->prev = prev;
free(right);
}
}
}
}
// select_node -- select a random previous node
node_t *
select_node(node_t *head,int count)
{
int lstidx = rand() % count;
for (; lstidx > 0; --lstidx)
head = head->next;
if (head == NULL) {
printf("find_dup: null head\n");
exit(1);
}
return head;
}
// generate_list -- generate a list with a random number of duplicates
node_t *
generate_list(int count)
{
node_t *head = NULL;
node_t *prev = NULL;
node_t temp;
int dupcnt = 0;
for (int lstidx = 0; lstidx < count; ++lstidx) {
int dupflg = (rand() % 100) < 20;
if (lstidx == 0)
dupflg = 0;
node_t *dup;
// find a node that we'll duplicate
if (dupflg) {
dup = select_node(head,lstidx);
++dupcnt;
}
// generate new random node
else {
dup = &temp;
for (int dataidx = 0; dataidx < NDATA; ++dataidx)
dup->data[dataidx] = rand() & 0xFFFF;
}
node_t *newnode = malloc(sizeof(*newnode));
// copy in the data
for (int dataidx = 0; dataidx < NDATA; ++dataidx)
newnode->data[dataidx] = dup->data[dataidx];
// set the node id and links
newnode->idx = lstidx;
newnode->prev = prev;
newnode->next = NULL;
// append to tail of list
if (prev != NULL)
prev->next = newnode;
// start new list
else
head = newnode;
prev = newnode;
}
printf("generate_list: %d dups of %d total nodes\n",dupcnt,count);
return head;
}
int
main(int argc,char **argv)
{
--argc;
++argv;
opt_N = 20;
opt_R = 1;
for (; argc > 0; --argc, ++argv) {
char *cp = *argv;
if (*cp != '-')
break;
cp += 2;
switch (cp[-1]) {
case 'N':
opt_N = (*cp != 0) ? atoi(cp) : 50;
break;
case 'R':
opt_R = (*cp != 0) ? atoi(cp) : time(NULL);
break;
}
}
printf("N = %d\n",opt_N);
printf("R = %u\n",opt_R);
srand(opt_R);
global_head = generate_list(opt_N);
print_list(global_head,"ORIGINAL");
delete_duplicates(global_head);
print_list(global_head,"NODUP");
return 0;
}
这是 -DNDATA=1
的程序输出:
N = 20
R = 1
generate_list: 2 dups of 20 total nodes
ORIGINAL:
NODE 9158 [0]
NODE 18547 [1]
NODE 23807 [2]
NODE 22764 [3]
NODE 31949 [4]
NODE 55211 [5]
NODE 7931 [6]
NODE 57670 [7]
NODE 25282 [8]
NODE 10232 [9]
NODE 25282 [10]
NODE 17293 [11]
NODE 9562 [12]
NODE 29283 [13]
NODE 55199 [14]
NODE 1946 [15]
NODE 23858 [16]
NODE 55223 [17]
NODE 58456 [18]
NODE 23858 [19]
delete_duplicates: 10 is dup of 8
delete_duplicates: 19 is dup of 16
NODUP:
NODE 9158 [0]
NODE 18547 [1]
NODE 23807 [2]
NODE 22764 [3]
NODE 31949 [4]
NODE 55211 [5]
NODE 7931 [6]
NODE 57670 [7]
NODE 25282 [8]
NODE 10232 [9]
NODE 17293 [11]
NODE 9562 [12]
NODE 29283 [13]
NODE 55199 [14]
NODE 1946 [15]
NODE 23858 [16]
NODE 55223 [17]
NODE 58456 [18]
更新#2:
So I tried to use your test program in my computer but it didn't work well, it can't escape from printing the list like an infinite loop or so.
当我 运行 上面的程序 [从我的第一次更新] 时,我 没有 看到这个问题。请确保您使用的是我的最新示例完全。
我在 [fedora] linux 下使用 gcc
。大多数其他系统也应该没问题:
- 其他 linux 发行版或 *BSD 系统应该没有问题。
- MacOS 的构建方式可能很古怪(因为编译器是
clang
和 OS 的不同 relocatable/executable 格式)。 - Windows (visual studio) 可能会有问题,但通常也没有问题。
- Windows(
cygwin
或mingw
)应该更好
唯一的其他问题可能是您的系统的 rand
实现产生了不同的随机数,这暴露了我算法中的一个缺陷,因为使用的测试值不同。
And about my program, I tried so many options to delete the duplicates but still couldn't figure out. I edit my question and add the full program so you could debug it that way.
我还没有看到你最新的完整程序的最新更新。
I use the same printing function so it doesn't seem to have a problem at all, the poblem occurs when I try to delete the duplicated data. Thanks a lot. – Ozdamar Kevser
一些可能的调试思路:
- 运行调试器下的程序(例如
gdb
) - 运行
valgrind
下的程序
- 用
-fsanitize=address
编译程序
此外,“问题”的确切性质是什么?
我能想到的唯一可能与我的程序不同的是 head
作为列表的使用。
有两种用途:
head
是列表的第一个 有效节点(这是我所做的)。head
是指向“虚拟”节点的指针。prev
是列表 head,next
是列表 tail。 IMO,这是“列表”结构的“穷人”实现。
就我个人而言,我不喜欢这两个选项(尤其是 (2))。为了使事情更清楚和更灵活,我更喜欢创建一个单独的列表结构(distinct 来自节点结构)。这类似于 (2),但更清晰、更明确。它还允许额外的信息,例如列表计数。
这是一个进一步重构的版本。它还 运行 使用不同的随机值多次测试:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define sysfault(_fmt...) \
do { \
printf(_fmt); \
exit(1); \
} while (0)
// NDATA -- number of elements in array
#ifndef NDATA
#define NDATA 1
#endif
// ALLMATCH -- duplicate detection critera
// 0=any element can match (original)
// 1=all must match (suitable for testing)
#ifndef ALLMATCH
#define ALLMATCH 1
#endif
typedef struct node node_t;
struct node {
node_t *prev;
node_t *next;
int idx; // easy identifier for debug
int data[NDATA];
};
typedef struct list {
int count;
node_t *head;
node_t *tail;
} list_t;
list_t *global_list; // list that may have duplicates
list_t *nodup_list; // same list _without_ duplicates
int opt_N; // number of nodes to generate
unsigned int opt_R; // random number seed
int opt_T; // number of tests
// node_print -- print a node
void
node_print(node_t *cur)
{
int totlen = 0;
for (int dataidx = 0; dataidx < NDATA; ++dataidx) {
if (dataidx == 0)
totlen += printf(" NODE ");
totlen += printf(" %6d",cur->data[dataidx]);
if (totlen >= 60) {
printf("\n");
totlen = 0;
}
}
totlen += printf(" [%d]",cur->idx);
if (totlen > 0)
printf("\n");
}
// list_print -- print list of nodes (forward)
void
list_print(list_t *list,const char *who)
{
node_t *cur;
printf("\n");
printf("%s: (%d elements)\n",who,list->count);
for (cur = list->head; cur != NULL; cur = cur->next)
node_print(cur);
}
// list_rprint -- print list of nodes (reverse)
void
list_rprint(list_t *list,const char *who)
{
node_t *cur;
printf("\n");
printf("%s: (%d elements)\n",who,list->count);
for (cur = list->tail; cur != NULL; cur = cur->prev)
node_print(cur);
}
// node_equal -- compare two nodes
// RETURNS: 0=mismatch, 1=equal
int
node_equal(node_t *lhs,node_t *rhs)
{
int match;
// say _not_ a match
#if ALLMATCH
match = 1;
#else
match = 0;
#endif
// scan buffer for a match
for (int dataidx = 0; dataidx < NDATA; ++dataidx) {
// original -- delete if _any_ element matches
#if ! ALLMATCH
match = (lhs->data[dataidx] == rhs->data[dataidx]);
if (match)
break;
// modified -- delete if _all_ element matches
#else
if (lhs->data[dataidx] != rhs->data[dataidx]) {
match = 0;
break;
}
#endif
}
return match;
}
// list_unlink -- unlink node from list
node_t *
list_unlink(list_t *list,node_t *del)
{
node_t *prev;
node_t *next;
next = del->next;
prev = del->prev;
// cross link previous and next nodes (eliminating current)
if (prev != NULL)
prev->next = next;
if (next != NULL)
next->prev = prev;
if (list->head == del)
list->head = next;
if (list->tail == del)
list->tail = prev;
list->count -= 1;
free(del);
return next;
}
// delete_duplicates -- delete duplicate nodes in list
void
delete_duplicates(list_t *list)
{
node_t *left;
node_t *right;
node_t *next;
int del;
for (left = list->head; left != NULL; left = left->next) {
for (right = left->next; right != NULL; right = next) {
// decide if the node is a duplicate
del = node_equal(left,right);
// delete duplicate node
if (del) {
printf("delete_duplicates: %d is dup of %d\n",
right->idx,left->idx);
next = list_unlink(list,right);
}
// advance to next node
else
next = right->next;
}
}
}
// list_select -- select a random previous node
node_t *
list_select(list_t *list)
{
int lstidx = rand() % list->count;
node_t *cur = list->head;
for (; lstidx > 0; --lstidx)
cur = cur->next;
if (cur == NULL)
sysfault("list_select: null cur\n");
return cur;
}
// list_append -- append to list
void
list_append(list_t *list,node_t *src)
{
node_t *newnode = malloc(sizeof(*newnode));
// copy in the data
for (int dataidx = 0; dataidx < NDATA; ++dataidx)
newnode->data[dataidx] = src->data[dataidx];
node_t *tail = list->tail;
// set the node id and links
newnode->idx = list->count++;
newnode->prev = tail;
newnode->next = NULL;
// start of new list
if (list->head == NULL)
list->head = newnode;
// set tail's forward link
if (tail != NULL)
tail->next = newnode;
// set new node's backward link
newnode->prev = tail;
// make new node the tail of the list
list->tail = newnode;
}
// list_generate -- generate a list with a random number of duplicates
void
list_generate(int count)
{
node_t temp;
int dupcnt = 0;
// list that can have dups
global_list = calloc(1,sizeof(*global_list));
// list without dups
nodup_list = calloc(1,sizeof(*nodup_list));
for (int lstidx = 0; lstidx < count; ++lstidx) {
int dupflg = (rand() % 100) < 20;
if (lstidx == 0)
dupflg = 0;
node_t *cur;
// find a node that we'll duplicate
if (dupflg) {
cur = list_select(global_list);
++dupcnt;
}
// generate new random node
else {
cur = &temp;
for (int dataidx = 0; dataidx < NDATA; ++dataidx)
cur->data[dataidx] = rand() & 0xFFFF;
list_append(nodup_list,cur);
}
list_append(global_list,cur);
}
printf("list_generate: %d dups of %d total nodes\n",dupcnt,count);
}
// list_destroy -- destroy a list
list_t *
list_destroy(list_t *list)
{
// free all nodes in list
node_t *next;
for (node_t *cur = list->head; cur != NULL; cur = next) {
next = cur->next;
free(cur);
}
// free the list itself
free(list);
list = NULL;
return list;
}
// list_compare -- compare two lists
void
list_compare(list_t *lhslist,list_t *rhslist)
{
node_t *lhs = lhslist->head;
node_t *rhs = rhslist->head;
if (lhslist->count != rhslist->count)
sysfault("list_compare: count mismatch -- lhs=%d rhs=%d\n",
lhslist->count,rhslist->count);
while (1) {
if (lhs == NULL)
break;
if (rhs == NULL)
break;
int same = node_equal(lhs,rhs);
if (! same) {
printf("list_compare: mismatch\n");
node_print(lhs);
node_print(rhs);
sysfault("list_compare: aborting\n");
}
lhs = lhs->next;
rhs = rhs->next;
}
printf("list_compare: complete\n");
}
// dotest -- do a test
void
dotest(int tstno)
{
if (tstno > 1)
printf("\n");
for (int sep = 1; sep <= 80; ++sep)
fputc('-',stdout);
fputc('\n',stdout);
printf("TEST %d of %d\n",tstno,opt_T);
list_generate(opt_N);
list_print(global_list,"global_list/ORIGINAL");
list_print(nodup_list,"nodup_list");
delete_duplicates(global_list);
list_print(global_list,"global_list/NODUP");
list_rprint(global_list,"global_list/REVERSE");
printf("\n");
list_compare(global_list,nodup_list);
global_list = list_destroy(global_list);
nodup_list = list_destroy(nodup_list);
}
int
main(int argc,char **argv)
{
--argc;
++argv;
opt_N = 20;
opt_R = 1;
opt_T = 5;
for (; argc > 0; --argc, ++argv) {
char *cp = *argv;
if (*cp != '-')
break;
cp += 2;
switch (cp[-1]) {
case 'N':
opt_N = (*cp != 0) ? atoi(cp) : 50;
break;
case 'R':
opt_R = (*cp != 0) ? atoi(cp) : time(NULL);
break;
case 'T':
opt_T = (*cp != 0) ? atoi(cp) : 10;
break;
}
}
printf("N = %d\n",opt_N);
printf("R = %u\n",opt_R);
srand(opt_R);
if (opt_T <= 0)
opt_T = 1;
printf("T = %d\n",opt_T);
for (int tstno = 1; tstno < opt_T; ++tstno)
dotest(tstno);
return 0;
}
下面是程序输出。
请注意,我还 built/ran 下的程序:
valgrind
没有 错误- 使用
-fsanitize=address
编译(无错误)
N = 20
R = 1
T = 5
--------------------------------------------------------------------------------
TEST 1 of 5
list_generate: 2 dups of 20 total nodes
global_list/ORIGINAL: (20 elements)
NODE 9158 [0]
NODE 18547 [1]
NODE 23807 [2]
NODE 22764 [3]
NODE 31949 [4]
NODE 55211 [5]
NODE 7931 [6]
NODE 57670 [7]
NODE 25282 [8]
NODE 10232 [9]
NODE 25282 [10]
NODE 17293 [11]
NODE 9562 [12]
NODE 29283 [13]
NODE 55199 [14]
NODE 1946 [15]
NODE 23858 [16]
NODE 55223 [17]
NODE 58456 [18]
NODE 23858 [19]
nodup_list: (18 elements)
NODE 9158 [0]
NODE 18547 [1]
NODE 23807 [2]
NODE 22764 [3]
NODE 31949 [4]
NODE 55211 [5]
NODE 7931 [6]
NODE 57670 [7]
NODE 25282 [8]
NODE 10232 [9]
NODE 17293 [10]
NODE 9562 [11]
NODE 29283 [12]
NODE 55199 [13]
NODE 1946 [14]
NODE 23858 [15]
NODE 55223 [16]
NODE 58456 [17]
delete_duplicates: 10 is dup of 8
delete_duplicates: 19 is dup of 16
global_list/NODUP: (18 elements)
NODE 9158 [0]
NODE 18547 [1]
NODE 23807 [2]
NODE 22764 [3]
NODE 31949 [4]
NODE 55211 [5]
NODE 7931 [6]
NODE 57670 [7]
NODE 25282 [8]
NODE 10232 [9]
NODE 17293 [11]
NODE 9562 [12]
NODE 29283 [13]
NODE 55199 [14]
NODE 1946 [15]
NODE 23858 [16]
NODE 55223 [17]
NODE 58456 [18]
global_list/REVERSE: (18 elements)
NODE 58456 [18]
NODE 55223 [17]
NODE 23858 [16]
NODE 1946 [15]
NODE 55199 [14]
NODE 29283 [13]
NODE 9562 [12]
NODE 17293 [11]
NODE 10232 [9]
NODE 25282 [8]
NODE 57670 [7]
NODE 7931 [6]
NODE 55211 [5]
NODE 31949 [4]
NODE 22764 [3]
NODE 23807 [2]
NODE 18547 [1]
NODE 9158 [0]
list_compare: complete
--------------------------------------------------------------------------------
TEST 2 of 5
list_generate: 5 dups of 20 total nodes
global_list/ORIGINAL: (20 elements)
NODE 35165 [0]
NODE 41751 [1]
NODE 23273 [2]
NODE 43220 [3]
NODE 23273 [4]
NODE 41751 [5]
NODE 40628 [6]
NODE 34321 [7]
NODE 7554 [8]
NODE 34369 [9]
NODE 41751 [10]
NODE 61575 [11]
NODE 56809 [12]
NODE 54433 [13]
NODE 34321 [14]
NODE 9063 [15]
NODE 24321 [16]
NODE 35165 [17]
NODE 19164 [18]
NODE 30614 [19]
nodup_list: (15 elements)
NODE 35165 [0]
NODE 41751 [1]
NODE 23273 [2]
NODE 43220 [3]
NODE 40628 [4]
NODE 34321 [5]
NODE 7554 [6]
NODE 34369 [7]
NODE 61575 [8]
NODE 56809 [9]
NODE 54433 [10]
NODE 9063 [11]
NODE 24321 [12]
NODE 19164 [13]
NODE 30614 [14]
delete_duplicates: 17 is dup of 0
delete_duplicates: 5 is dup of 1
delete_duplicates: 10 is dup of 1
delete_duplicates: 4 is dup of 2
delete_duplicates: 14 is dup of 7
global_list/NODUP: (15 elements)
NODE 35165 [0]
NODE 41751 [1]
NODE 23273 [2]
NODE 43220 [3]
NODE 40628 [6]
NODE 34321 [7]
NODE 7554 [8]
NODE 34369 [9]
NODE 61575 [11]
NODE 56809 [12]
NODE 54433 [13]
NODE 9063 [15]
NODE 24321 [16]
NODE 19164 [18]
NODE 30614 [19]
global_list/REVERSE: (15 elements)
NODE 30614 [19]
NODE 19164 [18]
NODE 24321 [16]
NODE 9063 [15]
NODE 54433 [13]
NODE 56809 [12]
NODE 61575 [11]
NODE 34369 [9]
NODE 7554 [8]
NODE 34321 [7]
NODE 40628 [6]
NODE 43220 [3]
NODE 23273 [2]
NODE 41751 [1]
NODE 35165 [0]
list_compare: complete
--------------------------------------------------------------------------------
TEST 3 of 5
list_generate: 3 dups of 20 total nodes
global_list/ORIGINAL: (20 elements)
NODE 42040 [0]
NODE 20010 [1]
NODE 31920 [2]
NODE 31920 [3]
NODE 52399 [4]
NODE 36692 [5]
NODE 6936 [6]
NODE 42076 [7]
NODE 18458 [8]
NODE 47939 [9]
NODE 9978 [10]
NODE 49978 [11]
NODE 42281 [12]
NODE 16358 [13]
NODE 42040 [14]
NODE 51092 [15]
NODE 18458 [16]
NODE 43105 [17]
NODE 59897 [18]
NODE 9915 [19]
nodup_list: (17 elements)
NODE 42040 [0]
NODE 20010 [1]
NODE 31920 [2]
NODE 52399 [3]
NODE 36692 [4]
NODE 6936 [5]
NODE 42076 [6]
NODE 18458 [7]
NODE 47939 [8]
NODE 9978 [9]
NODE 49978 [10]
NODE 42281 [11]
NODE 16358 [12]
NODE 51092 [13]
NODE 43105 [14]
NODE 59897 [15]
NODE 9915 [16]
delete_duplicates: 14 is dup of 0
delete_duplicates: 3 is dup of 2
delete_duplicates: 16 is dup of 8
global_list/NODUP: (17 elements)
NODE 42040 [0]
NODE 20010 [1]
NODE 31920 [2]
NODE 52399 [4]
NODE 36692 [5]
NODE 6936 [6]
NODE 42076 [7]
NODE 18458 [8]
NODE 47939 [9]
NODE 9978 [10]
NODE 49978 [11]
NODE 42281 [12]
NODE 16358 [13]
NODE 51092 [15]
NODE 43105 [17]
NODE 59897 [18]
NODE 9915 [19]
global_list/REVERSE: (17 elements)
NODE 9915 [19]
NODE 59897 [18]
NODE 43105 [17]
NODE 51092 [15]
NODE 16358 [13]
NODE 42281 [12]
NODE 49978 [11]
NODE 9978 [10]
NODE 47939 [9]
NODE 18458 [8]
NODE 42076 [7]
NODE 6936 [6]
NODE 36692 [5]
NODE 52399 [4]
NODE 31920 [2]
NODE 20010 [1]
NODE 42040 [0]
list_compare: complete
--------------------------------------------------------------------------------
TEST 4 of 5
list_generate: 6 dups of 20 total nodes
global_list/ORIGINAL: (20 elements)
NODE 15513 [0]
NODE 16533 [1]
NODE 13803 [2]
NODE 20659 [3]
NODE 20659 [4]
NODE 48896 [5]
NODE 60065 [6]
NODE 2789 [7]
NODE 20659 [8]
NODE 13803 [9]
NODE 583 [10]
NODE 38589 [11]
NODE 38589 [12]
NODE 40616 [13]
NODE 20659 [14]
NODE 64965 [15]
NODE 31603 [16]
NODE 15513 [17]
NODE 9035 [18]
NODE 12131 [19]
nodup_list: (14 elements)
NODE 15513 [0]
NODE 16533 [1]
NODE 13803 [2]
NODE 20659 [3]
NODE 48896 [4]
NODE 60065 [5]
NODE 2789 [6]
NODE 583 [7]
NODE 38589 [8]
NODE 40616 [9]
NODE 64965 [10]
NODE 31603 [11]
NODE 9035 [12]
NODE 12131 [13]
delete_duplicates: 17 is dup of 0
delete_duplicates: 9 is dup of 2
delete_duplicates: 4 is dup of 3
delete_duplicates: 8 is dup of 3
delete_duplicates: 14 is dup of 3
delete_duplicates: 12 is dup of 11
global_list/NODUP: (14 elements)
NODE 15513 [0]
NODE 16533 [1]
NODE 13803 [2]
NODE 20659 [3]
NODE 48896 [5]
NODE 60065 [6]
NODE 2789 [7]
NODE 583 [10]
NODE 38589 [11]
NODE 40616 [13]
NODE 64965 [15]
NODE 31603 [16]
NODE 9035 [18]
NODE 12131 [19]
global_list/REVERSE: (14 elements)
NODE 12131 [19]
NODE 9035 [18]
NODE 31603 [16]
NODE 64965 [15]
NODE 40616 [13]
NODE 38589 [11]
NODE 583 [10]
NODE 2789 [7]
NODE 60065 [6]
NODE 48896 [5]
NODE 20659 [3]
NODE 13803 [2]
NODE 16533 [1]
NODE 15513 [0]
list_compare: complete