473,396 Members | 1,965 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 473,396 software developers and data experts.

Doubly linked list

I'm attempting to sort a doubly linked list at the time that an item is
added. When I add the items in increasing order everything works
correctly, however when I add them in reverse order the correlation is
messed up. The list, however, is sorted except for element 0 (or
whatever value was the first to be added). I've been working on this
most of the night so maybe I am overlooking something. Any help would be
much appreciated. Below is a minimal example that can be compiled (sorry
for the length of the code, however I couldn't make it any smaller).

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

enum { false, true };

typedef int bool;

struct dll_t
{
int val;
struct dll_t *next;
struct dll_t *prev;
};

#define compare(x, y) memcmp(x, y, sizeof(x))

#define icompare(x, y) ((x) - (y))

bool add_node_value(struct dll_t **dll, int val);

int main(void)
{
struct dll_t *dll;
struct dll_t *p;
int i;

dll = NULL;

/* this works perfectly */
/*for(i = 0; i < 10; i++)
add_node_value(&dll, i);*/

/* this does not */
for(i = 9; i > -1; i--)
add_node_value(&dll, i);

printf("walking\n");

for(p = dll; p; p = p->next)
{
printf("%d (p: %d n: %d)\n", p->val,
p->prev == NULL ? -1 : p->prev->val,
p->next == NULL ? -1 : p->next->val);
}

return(0);
}

bool add_node_value(struct dll_t **dll, int val)
{
struct dll_t *tmp;
struct dll_t *p;

tmp = malloc(sizeof(*tmp));
tmp->next = NULL;
tmp->val = val;

if(*dll == NULL)
{
tmp->prev = NULL;
*dll = tmp;
return(true);
}

p = *dll;
printf("icompare(%d, %d) = %d\n",
val, p->val, icompare(val, p->val));

if(icompare(tmp->val, p->val) > 0)
{
/* tmp->val is larger - insert tmp after p */
for(p = *dll; (val > p->val) && p != NULL; p = p->next)
{
if(p->next == NULL)
break;
}

tmp->prev = p;
tmp->next = p->next;
p->next = tmp;

printf("p->prev: %d p->val: %d p->next: %d\n",
p->prev == NULL ? -1 : p->prev->val, p->val,
p->next == NULL ? -1 : p->next->val);
}
else
{
/* tmp->val is smaller - insert p before tmp */
for(p = *dll; (tmp->val < p->val) && p != NULL; p = p->next)
{
if(p->next == NULL)
break;
}

/* this is where the problem is */
tmp->prev = (*dll);
tmp->next = (*dll)->next;
(*dll)->next = tmp;

printf("p->prev: %d p->val: %d p->next: %d\n",
p->prev == NULL ? -1 : p->prev->val, p->val,
p->next == NULL ? -1 : p->next->val);
}

return(true);
}
Mar 19 '06 #1
6 2040
Joe Estock wrote:
struct dll_t
{
int val;
struct dll_t *next;
struct dll_t *prev;
};

#define compare(x, y) memcmp(x, y, sizeof(x))
#define icompare(x, y) ((x) - (y))
Those look at the very least suspicious.
bool add_node_value(struct dll_t **dll, int val);
What does the returnvalue mean?
bool add_node_value(struct dll_t **dll, int val)
{
struct dll_t *tmp;
struct dll_t *p;

tmp = malloc(sizeof(*tmp));
tmp->next = NULL;
tmp->val = val;

if(*dll == NULL)
{
tmp->prev = NULL;
*dll = tmp;
return(true);
}
Just as a note, I would not have initialised tmp->next to NULL right after
malloc() but here when it's clear that it's the first element of the list.

p = *dll;
printf("icompare(%d, %d) = %d\n",
val, p->val, icompare(val, p->val));
OK, here you use memcmp() over the whole structure, including two pointers
that should not be significant and and possible padding bytes. Overall,
this makes the outcome of icompare() meaningless.

[snipped insertion]

And here you sort the element into the list based on insignificant and
meaningless values.
return(true);


return is not a function. ;)

cheers

Uli
Mar 19 '06 #2
Ulrich Eckhardt wrote:
Joe Estock wrote:
struct dll_t
{
int val;
struct dll_t *next;
struct dll_t *prev;
};

#define compare(x, y) memcmp(x, y, sizeof(x))
#define icompare(x, y) ((x) - (y))

Those look at the very least suspicious.


How so? icompare is quite obvious...if x - y is negative then x < y,
otherwise if x - y is zero then x == y, otherwise x > y. They don't look
at all suspicious to me.

bool add_node_value(struct dll_t **dll, int val);

What does the returnvalue mean?


Success or failure. Hence they reason for bool.

bool add_node_value(struct dll_t **dll, int val)
{
struct dll_t *tmp;
struct dll_t *p;

tmp = malloc(sizeof(*tmp));
tmp->next = NULL;
tmp->val = val;

if(*dll == NULL)
{
tmp->prev = NULL;
*dll = tmp;
return(true);
}

Just as a note, I would not have initialised tmp->next to NULL right after
malloc() but here when it's clear that it's the first element of the list.


I don't see any benifit from changing it that way other than
clarification of the code, perhaps. Advice noted.


p = *dll;
printf("icompare(%d, %d) = %d\n",
val, p->val, icompare(val, p->val));

OK, here you use memcmp() over the whole structure, including two pointers
that should not be significant and and possible padding bytes. Overall,
this makes the outcome of icompare() meaningless.


Not correct and not correct. icompare does not get expanded to memcmp();
that's compare that you are thinking of. The outcome of icompare is in
fact not meaningless.

[snipped insertion]

And here you sort the element into the list based on insignificant and
meaningless values.
According to you they are meaningless values, however you should have
left at least the relevant code to which you were replying to intact in
case my original article expires before someone reading this has a
chance to look at it.

return(true);

return is not a function. ;)


I know that. I've used return in this fashion for the past several
years. Old habits die hard. Since it is not functionally different
either way, it's a matter of preference.

cheers

Uli


Thanks for attempting to help me understand what is going on, however I
am still just as clueless as I was when I first posted this.

Joe
Mar 19 '06 #3
Joe Estock wrote:

I'm attempting to sort a doubly linked list at the time that an
item is added. When I add the items in increasing order everything
works correctly, however when I add them in reverse order the
correlation is messed up. The list, however, is sorted except for
element 0 (or whatever value was the first to be added). I've been
working on this most of the night so maybe I am overlooking
something. Any help would be much appreciated. Below is a minimal
example that can be compiled (sorry for the length of the code,
however I couldn't make it any smaller).
I fixed your descending problem and your walking problem
(surprise!). When you look at the fixes I think you will see the
ascending insert also has problems. Don't forget to consider the
equality cases.

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

enum { false, true };

typedef int bool;

struct dll_t
{
int val;
struct dll_t *next;
struct dll_t *prev;
};

#define compare(x, y) memcmp(x, y, sizeof(x))

#define icompare(x, y) ((x) - (y))

bool add_node_value(struct dll_t **dll, int val);

int main(void)
{
struct dll_t *dll;
struct dll_t *p;
int i;

dll = NULL;

/* this works perfectly */
/*for(i = 0; i < 10; i++)
add_node_value(&dll, i);*/

/* this does not */
for(i = 9; i > -1; i--)
add_node_value(&dll, i);

printf("walking\n"); p = dll;
while (p && p->prev) p = p->prev; /* find smallest */
while (p) {
printf("%d (p: %2d n: %2d)\n",
p->val,
p->prev == NULL ? -1 : p->prev->val,
p->next == NULL ? -1 : p->next->val);
p = p->next;
}
/* for(p = dll; p; p = p->next)
{
printf("%d (p: %d n: %d)\n", p->val,
p->prev == NULL ? -1 : p->prev->val,
p->next == NULL ? -1 : p->next->val);
}
*/
return(0);
}

bool add_node_value(struct dll_t **dll, int val)
{
struct dll_t *tmp;
struct dll_t *p;

tmp = malloc(sizeof(*tmp));
tmp->next = NULL;
tmp->val = val;

if(*dll == NULL)
{
tmp->prev = NULL;
*dll = tmp;
return(true);
}

p = *dll;
printf("icompare(%d, %d) = %d\n",
val, p->val, icompare(val, p->val));

if(icompare(tmp->val, p->val) > 0)
{
/* tmp->val is larger - insert tmp after p */
for(p = *dll; (val > p->val) && p != NULL; p = p->next)
{
if(p->next == NULL)
break;
}

tmp->prev = p;
tmp->next = p->next;
p->next = tmp;

printf("p->prev: %d p->val: %d p->next: %d\n",
p->prev == NULL ? -1 : p->prev->val, p->val,
p->next == NULL ? -1 : p->next->val);
}
else
{
/* tmp->val is smaller - insert p before tmp */
/* for(p = *dll; (tmp->val < p->val) && p != NULL; p = p->next)
{
if(p->next == NULL)
break;
}
*/
/* this is where the problem is */
/* tmp->prev = (*dll);
tmp->next = (*dll)->next;
(*dll)->next = tmp;
*/
while (p->prev && (icompare(tmp->val, p->val) <= 0))
p = p->prev;

tmp->prev = p->prev;
tmp->next = p;
p->prev = tmp;
if (tmp->prev) tmp->prev->next = tmp;
printf("p->prev: %d p->val: %d p->next: %d\n",
p->prev == NULL ? -1 : p->prev->val, p->val,
p->next == NULL ? -1 : p->next->val);
}

return(true);
}


Now build a separate callable list display mechanism, and build the
lists by inserting random values, which you can get from rand().
Note that your dll variable points somewhere within the list, not
to either end. Consider end effects, including the empty list.

--
Some informative links:
news:news.announce.newusers
http://www.geocities.com/nnqweb/
http://www.catb.org/~esr/faqs/smart-questions.html
http://www.caliburn.nl/topposting.html
http://www.netmeister.org/news/learn2quote.html

Mar 19 '06 #4
Joe Estock wrote:
Ulrich Eckhardt wrote:
Joe Estock wrote:
#define compare(x, y) memcmp(x, y, sizeof(x))
#define icompare(x, y) ((x) - (y))

Those look at the very least suspicious.


How so? icompare is quite obvious...if x - y is negative then x < y,
otherwise if x - y is zero then x == y, otherwise x > y. They don't look
at all suspicious to me.


There are two things that strike me as dangerous here
1. they are macros
This could have the reason that they need to be generic, i.e. work with
different types but often they can be replaced with static inline
functions that protect you from side-effects.
2. they don't add much
In particular the second one would be much better spelled out, so you can
immediately see what they are for. Anyhow, that point is mostly one of
taste, though some people would even question the first one.
p = *dll;
printf("icompare(%d, %d) = %d\n",
val, p->val, icompare(val, p->val));

OK, here you use memcmp() over the whole structure, including two
pointers that should not be significant and and possible padding
bytes. Overall, this makes the outcome of icompare() meaningless.


Not correct and not correct. icompare does not get expanded to memcmp();
that's compare that you are thinking of. The outcome of icompare is in
fact not meaningless.


Right, I was wrong. This uses the integer comparison while the really
dangerous memcmp() comparison is in fact unused here. Sorry, I think I
just saw what I wanted to see....

BTW: you should return false when malloc() returns NULL.

Uli

Mar 19 '06 #5
Joe Estock <je*****@NOSPAMnutextonline.com> writes:
[...]
#define icompare(x, y) ((x) - (y))
If the subtraction overflows, this invokes undefined behavior.
Consider icompare(MIN_INT, MAX_INT).

And your calls to icompare:
printf("icompare(%d, %d) = %d\n",
val, p->val, icompare(val, p->val));

if(icompare(tmp->val, p->val) > 0)
{

[...]

would be much clearer if you just compared the values directly:

if (tmp->val > p->val)
{
...
}

You also use printf to display the result of the call to icompare; if
you like, you can do that too:

printf("(%d > %d) = %d\n",
val, p->val, val > p->val);

--
Keith Thompson (The_Other_Keith) ks***@mib.org <http://www.ghoti.net/~kst>
San Diego Supercomputer Center <*> <http://users.sdsc.edu/~kst>
We must do something. This is something. Therefore, we must do this.
Mar 19 '06 #6
On Sun, 19 Mar 2006 08:33:00 GMT, Joe Estock
<je*****@NOSPAMnutextonline.com> wrote:
I'm attempting to sort a doubly linked list at the time that an item is
added. When I add the items in increasing order everything works
correctly, however when I add them in reverse order the correlation is
messed up. The list, however, is sorted except for element 0 (or
whatever value was the first to be added). I've been working on this
most of the night so maybe I am overlooking something. Any help would be
much appreciated. Below is a minimal example that can be compiled (sorry
for the length of the code, however I couldn't make it any smaller).

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

enum { false, true };

typedef int bool;

struct dll_t
{
int val;
struct dll_t *next;
struct dll_t *prev;
};

#define compare(x, y) memcmp(x, y, sizeof(x))
You don't use this and it wouldn't work for negative numbers.

#define icompare(x, y) ((x) - (y))

bool add_node_value(struct dll_t **dll, int val);

int main(void)
{
struct dll_t *dll;
struct dll_t *p;
int i;

dll = NULL;

/* this works perfectly */
/*for(i = 0; i < 10; i++)
add_node_value(&dll, i);*/

/* this does not */
for(i = 9; i > -1; i--)
add_node_value(&dll, i);

printf("walking\n");

for(p = dll; p; p = p->next)
{
printf("%d (p: %d n: %d)\n", p->val,
p->prev == NULL ? -1 : p->prev->val,
p->next == NULL ? -1 : p->next->val);
}

return(0);
}

bool add_node_value(struct dll_t **dll, int val)
{
struct dll_t *tmp;
struct dll_t *p;

tmp = malloc(sizeof(*tmp));
You should check for success.
tmp->next = NULL;
tmp->val = val;

if(*dll == NULL)
{
tmp->prev = NULL;
*dll = tmp;
return(true);
}

p = *dll;
printf("icompare(%d, %d) = %d\n",
val, p->val, icompare(val, p->val));
While it is true that val and tmp->val are equal here, if you are
going to print out debugging information like this it ought to match
what your code is testing. Your test will be against tmp->val.

if(icompare(tmp->val, p->val) > 0)
{
/* tmp->val is larger - insert tmp after p */
for(p = *dll; (val > p->val) && p != NULL; p = p->next)
p is already set to *dll.

Your previous test was against tmp->val, this test is against val. You
should be consistent to avoid problems if you ever modify the code.

Your test expression is in the wrong order. When you reach the end of
the list and p becomes NULL, you attempt to dereference it before you
check to see if it is NULL.

But then you avoid the problem with the test in the next two lines so
this one is redundant.
{
if(p->next == NULL)
break;
}

tmp->prev = p;
tmp->next = p->next;
p->next = tmp;

printf("p->prev: %d p->val: %d p->next: %d\n",
p->prev == NULL ? -1 : p->prev->val, p->val,
p->next == NULL ? -1 : p->next->val);
All the p-> here should be tmp-> since you want to see the newly
inserted node along with its predecessor and successor. The code you
have will show the new node, the preceding node, and the one before
that (in the reverse order). For debugging purposes, you don't know
that the successor node has a value greater than the new node.
}
else
{
/* tmp->val is smaller - insert p before tmp */
you seem to have forgotten about equals which is processed by this
code also.
for(p = *dll; (tmp->val < p->val) && p != NULL; p = p->next)
This is backwards. If tmp->val < p->val, you want to look at p->prev,
not p->next. However, since you list is in ascending order and you
have already established that tmp->val < first element, the only
option is to put tmp at the head of the list. All the code in this
else block (except the debug printf) should be replaced with the
changes noted below.
{
if(p->next == NULL)
break;
}

/* this is where the problem is */
There are two problems. The loop above is backwards and you want to
replace the three following with
tmp->next = p;
p->prev = tmp;
*dll = tmp;
tmp->prev = (*dll);
tmp->next = (*dll)->next;
(*dll)->next = tmp;

printf("p->prev: %d p->val: %d p->next: %d\n",
p->prev == NULL ? -1 : p->prev->val, p->val,
p->next == NULL ? -1 : p->next->val);
All the p-> here should be tmp-> since you want to see the newly
inserted node along with its non-existent predecessor and successor.
}

return(true);
}

Remove del for email
Mar 19 '06 #7

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

3
by: surrealtrauma | last post by:
I want to ask what's the differences between doubly liked list and linear liked list, and also the circular doubly liked list in terms of implementation. THX
4
by: dssuresh6 | last post by:
Whether browsing forward or backward can be done using a singly linked list. Is there any specific case where a doubly linked list is needed? For people who say that singly linked list allows...
5
by: free2cric | last post by:
Hi, how to detect head and tail in cyclic doubly link list ? Thanks, Cric
1
by: drewy2k12 | last post by:
Heres the story, I have to create a doubly linked list for class, and i have no clue on how to do it, i can barely create a single linked list. It has to have both a head and a tail pointer, and...
2
by: murali | last post by:
Hi, I want to insert a node in an sorted doubly linked list of integers in ascending order. The list should not have duplicate nodes. I need an algorithm (assuming an object oriented language) ...
8
by: tonywinslow1986 | last post by:
I'm reading MIT's book "Introduction to Algorithms". The following is one of the excercises from it: < 10.2-8 Explain how to implement doubly linked lists using only one pointer value np per...
3
by: maruf.syfullah | last post by:
Consider the following Class definitions: class AClass { int ai1; int ai2; public: CClass* c; AClass(){}
5
by: adam.kleinbaum | last post by:
Hi there, I'm a novice C programmer working with a series of large (30,000 x 30,000) sparse matrices on a Linux system using the GCC compiler. To represent and store these matrices, I'd like to...
4
kim6987
by: kim6987 | last post by:
can you please spend a little time evaluating this code. I can not run it successfully thanks :) #include<stdio.h> #include<conio.h> #include<stdlib.h> #define SIZE 10 typedef struct dlist
10
by: kalar | last post by:
Hello. we have this struct and we must to make a linked list struct node { char name; char phone; struct node *prevName; // previous node alphabetically struct node *prevNumber; //...
0
by: ryjfgjl | last post by:
In our work, we often receive Excel tables with data in the same format. If we want to analyze these data, it can be difficult to analyze them because the data is spread across multiple Excel files...
0
by: emmanuelkatto | last post by:
Hi All, I am Emmanuel katto from Uganda. I want to ask what challenges you've faced while migrating a website to cloud. Please let me know. Thanks! Emmanuel
0
BarryA
by: BarryA | last post by:
What are the essential steps and strategies outlined in the Data Structures and Algorithms (DSA) roadmap for aspiring data scientists? How can individuals effectively utilize this roadmap to progress...
1
by: Sonnysonu | last post by:
This is the data of csv file 1 2 3 1 2 3 1 2 3 1 2 3 2 3 2 3 3 the lengths should be different i have to store the data by column-wise with in the specific length. suppose the i have to...
0
by: Hystou | last post by:
There are some requirements for setting up RAID: 1. The motherboard and BIOS support RAID configuration. 2. The motherboard has 2 or more available SATA protocol SSD/HDD slots (including MSATA, M.2...
0
Oralloy
by: Oralloy | last post by:
Hello folks, I am unable to find appropriate documentation on the type promotion of bit-fields when using the generalised comparison operator "<=>". The problem is that using the GNU compilers,...
0
jinu1996
by: jinu1996 | last post by:
In today's digital age, having a compelling online presence is paramount for businesses aiming to thrive in a competitive landscape. At the heart of this digital strategy lies an intricately woven...
0
tracyyun
by: tracyyun | last post by:
Dear forum friends, With the development of smart home technology, a variety of wireless communication protocols have appeared on the market, such as Zigbee, Z-Wave, Wi-Fi, Bluetooth, etc. Each...
0
agi2029
by: agi2029 | last post by:
Let's talk about the concept of autonomous AI software engineers and no-code agents. These AIs are designed to manage the entire lifecycle of a software development project—planning, coding, testing,...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.