I have an exam tomorrow and I was trying to understand this doubly linked list example that the instructor placed on the class website but I'm having a hard time understanding a bit of it...
Here's the code:
#include <stdio.h>
#include <stdlib.h>
typedef struct dl {
int key;
float value;
struct dl *next;
struct dl *prev;
} DL;
DL *insert(int c, float f, DL *l) {
DL *new = (DL*) malloc(sizeof(DL));
if (new == NULL) exit(-1);
new->key=c; new->value=f;
if (l==NULL) {
new->next=NULL; new->prev=NULL;
}
else if (l->key < c) {
while((l->next != NULL) && (l->next->key < c)) { l=l->next; }
new->next=l->next; l->next=new; new->prev=l;
if (new->next != NULL) {
new->next->prev=new;
}
}
else {
while((l->prev != NULL) && (l->prev->key > c)) { l=l->prev; }
new->prev=l->prev; l->prev=new; new->next=l;
if(new->prev != NULL) {
new->prev->next=new;
}
}
return new;
}
int search(int c, float *f, DL **lptr) {
if (*lptr == NULL) return 0;
if (c < (*lptr)->key) {
while(((*lptr)->prev!=NULL)&&((*lptr)->prev->key >= c)) {
(*lptr)=(*lptr)->prev;
}
}
else if (c > (*lptr)->key) {
while(((*lptr)->next!=NULL)&&((*lptr)->next->key <= c)) {
(*lptr)=(*lptr)->next;
}
}
if ((*lptr)->key == c) {
*f = (*lptr)->value;
return 1;
}
return 0;
}
void printList(DL *l) {
if (l == NULL) return;
while (l->prev != NULL) { l=l->prev; };
while(l != NULL) {
printf("%d,%f\n",l->key,l->value);
l=l->next;
}
}
int main(void) {
DL *list=NULL;
float f;
list=insert(3,5.6,list); list=insert(4,5.3,list);
list=insert(7,3.6,list); list=insert(1,7.7,list);
list=insert(9,2.3,list); list=insert(0,9.0,list);
printList(list);
if (search(3,&f,&list)) {
printf("Found %f.\n",f);
}
else {
printf("Not found.\n");
}
printList(list);
return 0;
}
An here's the output:
0,9.000000
1,7.700000
3,5.600000
4,5.300000
7,3.600000
9,2.300000
Found 5.600000.
0,9.000000
1,7.700000
3,5.600000
4,5.300000
7,3.600000
9,2.300000
What I don't get is the "search" function. The list being passed is a pointer to a pointer of DL, right? And we are looking for a number, for that we keep doing (*lptr) = (*lptr)->next (or prev) to iterate through the whole list. What I don't get is why the second call to printList() prints the whole list... After the search() call has been made, shouldn't the "list" only have the elements after the one we looked for? The pointer was changed, how come when we return from search() the pointer is restored to the first element and the whole list is printed?
This is what I don't get cause if I change the search() function and add (*lptr) = NULL in the first line, the second call to printList() will not print anything, cause the pointer was changed, it is NULL now, there's nothing to print. Why doesn't (*lptr) = (*lptr)->next has a similar effect? The pointer is also being changed to the next one, shouldn't the second printList() call only print the remaining elements in the list?
EDIT:
Every answer seems to be the correct one and I'm going to sort it by "oldest" and accept the "fastest" one, don't be mad, I need to have some criteria. I could go on and see which answered provided better insight on the issue but it's irrelevant because I already know everything that was said. I was just stupid enough to not even look to the printList() function and assumed it was ok, I also assumed that the error was somewhere on the search() function. But I knew I was right, I knew the pointer was being change and the list couldn't print everything, but I understand why now...
printList rewinds the list before printing it.
while (l->prev != NULL) { l=l->prev; };
If it didn't have the above line, it would just print the things after the found element.
This line return pointer back:
while (l->prev != NULL) { l=l->prev; };
And those do the printing:
while(l != NULL) {
printf("%d,%f\n",l->key,l->value);
l=l->next;
}
And there is much better approach of doing this, just by adding the additional field or even two which will always point at the beginning and end of the list.
As far as I can read (and like rmeador commented, it is pretty awful code), the search call does modify the list pointer to point to the found element.
The trick is the printList function.
The first thing it does (other than checking for NULL) is this:
while (l->prev != NULL) { l=l->prev; };
So it basically follows the prev pointer back to the start of the list, so the actual printing starts from the start of the list even if it is passed a pointer to the middle or end of it.
In the printList() function you are going back from the found element using l = l->prev. Then you are printing all the contents.
What I don't get is why the second
call to printList() prints the whole
list... After the search() call has
been made, shouldn't the "list" only
have the elements after the one we
looked for? The pointer was changed,
how come when we return from search()
the pointer is restored to the first
element and the whole list is printed?
What you have is not really a pointer to the list, but a pointer to an element in the list. The first thing that the printList function does it to loop back through the prev references to find the first element of the list.
He backtrack the pointer inside the print:
while (l->prev != NULL) { l=l->prev; };
Remember that the list is doubly linked. Search doesn't change the list, just what part of it "list" is presently pointing to.
Related
im trying to insert one list in the end of another
with this code:
typedef struct Element
{
int vKey;
char vInfo[30];
struct Element *pNext;
} tsElement;
typedef struct
{
tsElemento *pFirst;
int vLength;
} tsLDSE;
void Unir(tsLDSE *pListIn, tsLDSE *pListOut)
{
tsElement *pElementOut;
pElementOut = pListOut->pFirst;
while (pElementOut != NULL)
{
pElementOut = pElemenoOut->pNext;
}
pElementOut = pListIn->pFirst;
pListOut->vLength = pListOut->vLength + pListIn->vLength ;
}
i checked printing the adresses, pElementoOut is really the end of the first list and is poiting to NULL, then it receives the fisrt adress of the second list, but when i print it out it only prints the first list and i can't figure out why.
Your function Unir only adds the length of the input list to the length of the output list.
This loop:
while (pElementOut != NULL)
{
pElementOut = pElemenoOut->pNext;
}
Only gets pElementOut to be NULL.
In addition, when you write pElementOut = pListIn->pFirst;, all you change is the local variable pElementOut.
What you want to do instead is this:
while (pElementOut->pNext != NULL)
{
pElementOut = pElementOut->pNext;
}
pElementOut->pNext = pListIn->pFirst;
This puts the first element of pListIn at the end of the last element of pListOut.
Also, add a NULL check at the beginning of your function! You can easily get a NULL pointer dereference there if you aren't careful.
Ok, so the problem that I am trying to find is why when I call print_inOrder(), I don't get anything printed back. The assignment I am suppose to do is write a tree algorithm in descending order (meaning higher values on left and lower values on the right). I had already created a function that created a tree a while back so I had just modified it and it works as it should; however the signature for this assignment is different from my old assignment and when I tried changing the pointers around, I got it to compile, but nothing prints out. So if someone could double check my changes and explain where I went wrong and how I need to fix it, that would make my day! ^.^
Working Original Function:
Tnode add_tnode(Tnode **current_tnode, char *value)
{
if(!(*current_tnode))
{
*current_tnode = (Tnode*) malloc(sizeof(Tnode));
(*current_tnode)->strValue = value;
//initialize the children to null
(*current_tnode)->left = NULL;
(*current_tnode)->right = NULL;
}
//Greater values go to left
else if(strcmp(value, (*current_tnode)->strValue) >= 0)
{
return add_tnode(&(*current_tnode)->left, value);
}
//Lesser values go to right
else if(strcmp(value, (*current_tnode)->strValue) < 0)
{
return add_tnode(&(*current_tnode)->right, value);
}
}
How it's called in main:
Tnode *root;
root = NULL;
//Add some nodes with string values
add_tnode(&root, "pie");
add_tnode(&root, "hi");
add_tnode(&root, "hi");
add_tnode(&root, "l");
add_tnode(&root, "leg");
//Print nodes in descending order
print_inOrder(root);
Signature Required:
Tnode *add_tnode(Tnode *current_tnode, char* value)
My Attempt to Fix:
Tnode *add_tnode(Tnode *current_tnode, char* value)
{
if(!(current_tnode))
{
current_tnode = (Tnode*) malloc(sizeof(Tnode));
(current_tnode)->strValue = value;
/* initialize the children to null */
(current_tnode)->left = NULL;
(current_tnode)->right = NULL;
}
// Greater values go to left
else if(strcmp(value, (current_tnode)->strValue) >= 0)
{
return add_tnode((current_tnode)->left, value);
}
// Lesser values go to right
else if(strcmp(value, (current_tnode)->strValue) < 0)
{
return add_tnode((current_tnode)->right, value);
}
}
How it's called in Main:
Tnode *root;
root = NULL;
//Add some nodes with string values
add_tnode(root, "pie");
add_tnode(root, "hi");
add_tnode(root, "hi");
add_tnode(root, "l");
add_tnode(root, "leg");
//Print nodes in descending order
print_inOrder(root);
Here's print_inOrder() just in case someone wants to look at it
void print_inOrder(Tnode *current_tnode)
{
if (current_tnode)
{
print_inOrder(current_tnode->left);
printf("%s\n",current_tnode->strValue);
print_inOrder(current_tnode->right);
}
}
When I run it through the gdb debugger and the print function is called, it only goes through the if statement and ends which my guess it means that the tree wasn't created at all or the pass in value is incorrect. If someone could inform me on what the mistake is, I'd greatly appreciate it!
Your problem is that your first function takes a Tnode **, that is a pointer to a pointer, and modifies the TNode * it points to. Your second function takes just the pointer, and modifies the passed-in argument; the caller can't see those changes, and so nothing is ever added to the tree.
You should allocate and assign the root node before doing anything, then change the function so that it modifies the TNode instead of the pointer thereto.
If your previous assignment worked, all you have to do is change the print function, exploring the right nodes before exploring the left nodes.
void print_inOrder(Tnode *current_tnode)
{
if (current_tnode)
{
print_inOrder(current_tnode->right);
printf("%s\n",current_tnode->strValue);
print_inOrder(current_tnode->left);
}
}
Hi I was trying to write a function that takes in the head of a linked list and free all the memory it allocated. Here is the original code I wrote.
void clear_nodes(List *h) {
if (!h->next) {
free(h);
***h = NULL;***
}
else {
clear_nodes(h->next);
clear_nodes(h);
}
}
However it does not work. So I changed it to
void clear_nodes(List *h) {
if (!h->next) {
free(h);
}
else {
clear_nodes(h->next);
***h->next = NULL;***
clear_nodes(h);
}
}
and now it worked.
Notice the difference between the two, I just wonder why can't I make the pointer to null directly. Is it because it's a local variable or something? But marking a pointer to null should make the pointer point to some memory address that I can't access right? Why would it matter if I do it locally or globally.
Thank you guys in advance.
I would probably do this NON-RECURSIVELY
void clear_nodes(List **h) {
List * copy = *h;
while( (copy = (*h)->next) != NULL){
free(*h);
*h = NULL;
*h = copy;
}
}
Recursivity is generally slower than looping. If you can try to design without recursive routines.
Since the value of h was passed, and not the location, you are not changing the pointer - you are changing a copy of the pointer.
If you changed your function to
void clear_nodes(List **h) {
if ((*h)->next == NULL) {
free(*h);
*h = NULL;
}
else {
clear_nodes(&((*h)->next));
clear_nodes(h);
}
}
You could change it "locally". I think I got that second part right... didn't try to compile / run it. I am positive about the first part though.
I just started learning C and as a self-learning excercise, I am implementing data structures and algos in C. Right now I am working on a graph and this is the data structure representation of it.
typedef int graphElementT;
typedef struct graphCDT *graphADT;
typedef struct vertexTag
{
graphElementT element;
int visited;
struct edgeTag *edges;
struct vertexTag *next;
} vertexT;
typedef struct edgeTag
{
int weight;
vertexT *connectsTo;
struct edgeTag *next;
} edgeT;
typedef struct graphCDT
{
vertexT *vertices;
} graphCDT;
To this graph I added a addVertex function.
int addVertex(graphADT graph, graphElementT value)
{
vertexT *new = malloc(sizeof(*new));
vertexT *vert;
new->element = value;
new->visited = 0;
new->edges = NULL;
new->next = NULL;
int i = 0;
for(vert=graph->vertices; vert->next != NULL; vert=vert->next)
{
if(vert->element == value)
{
printf("already exists\n");
return 0;
}
}
vert->next = new;
//free(new);
printf("\ninserted %d\n", vert->element);
return 1;
}
This works fine except for three things.
if the newly added vertex is the same as the last vertex in the list, it fails to see it. To prevent this i changed the for loop limiting condition to vert != NULL, but that gives a seg fault.
if i try to free the temporarily allocated pointer, it resets the memory pointer by the pointer and this adds an infinite loop at the end of the vertex list. Is there no way to free the pointer without writing over the memory it points to? Or is it not really needed to free the pointer?
Also would destroying the graph mean destroying every edge and vertices? or is there a better approach?
Also if this data structure for graph is not a good one and there are better implementations, i would appreciate that being pointed out.
1
If you change the limiting condition to vert!=NULL , and if the loop ends with vert==NULL ,i.e. ,the vertex to be added isn't present , then you will be reading next statement :
vert->next = new;
That means you are accesing the NULL ,vert pointer , hence the seg fault .
Now to allow checking if the last element isn't the vertex to be added ,and also to prevent seg fault ,do this :
for(vert=graph->vertices; vert->next != NULL; vert=vert->next)
{
if(vert->element == value)
{
printf("already exists\n");
return 0;
}
}
if(vert->element == value)
{
printf("already exists\n");
return 0;
}
vert->next = new;
2
The temporary "new" pointer is the memory location allocated to the Vertex you added .IT IS NOT to be freed ,as freeing it will mean that you deleted the vertex you just added :O .
3
Yes , detroying the graph essentialy means the same .
It is always a good practice to implement linked list as a adjacency list implementation of graph .Although you can always use a c++ "2 D Vector" to implement the same .
Here's a working addVertex function that you can use.
I am keeping the original declarations as it is.
I have added a main () to which you can give command line arguments to test.
int addVertex(graphADT graph, graphElementT value)
{
vertexT *tmpvert , *vert ;
vert=graph->vertices ;
/*check to see whether we really need to create a new vertex*/
tmpvert = vert;
while(tmpvert != NULL)
{
/* U can put a debug printf here to check what's there in graph:
* printf("tmpvert->elem=%d ", tmpvert->element);
*/
vert = tmpvert;
if(tmpvert->element == value)
return 0;
tmpvert=tmpvert->next ;
}
/*If we are here , then we HAVE to allocate memory and add to our graph.*/
tmpvert = (vertexT*)malloc(sizeof(vertexT));
if ( NULL == tmpvert )
return 0; /* malloc failure */
tmpvert->element = value;
tmpvert->visited = 0;
tmpvert->edges = NULL;
tmpvert->next = NULL;
if ( NULL == vert )
graph->vertices = tmpvert; /*Notice that I dont use virt=tmpvert */
else
vert->next = tmpvert; /*putting stuff in next is fine */
return 1;
/* Dont try printing vert->element here ..vert will be NULL first time */
/*return code for success is normally 0 others are error.
*That way you can have your printfs and error code
*handling outside this function.But its ok for a test code here */
}
Now for the main () snippet for testing :
int main (int argc , char* argv[]) {
graphADT graph ;
graph =(graphADT) malloc ( sizeof(struct graphCDT) );
graph->vertices = NULL;
while ( --argc >0)
{
int value = atoi(argv[argc]);
addVertex(graph,value);
}
}
I have the following struct:
struct cell {
int nmbr;
struct cell *p;
};
I have created a chain of linked structs from this type. Every struct is connected to its predecessor through *p. If I decide to print all nmbrs with a recursive algorithm as shown below, how do I define the stop condition?
void write(struct cell* l) {
/* The following if statement doesn't solve my problem,
but hopefully you see what I'm trying to do */
if (&l != 0x000000) {
printf("%d \t", l->nmbr);
write(l->p);
}
}
You want
if (l != 0)
or
if (l != NULL)
Of course, you also need to make sure that the tail of your linked list has p assigned NULL as well; otherwise it will be uninitialized and probably something not NULL but invalid anyway.
You should check against 'l' not being NULL rather than '&l' assuming that the linked list in NULL terminated.
Assuming the linked list is not cyclic, you just stop when you reach a null pointer.
Change
if (&l != 0x000000)
to
if (l != NULL)
As others have said, you should check l (el), not &l, against 0 or NULL. So, your function should look something like:
void write(CELL* l) {
if (l != 0x000000) {
printf("%d \t", l->nmbr);
write(l->p);
}
}
That said, it is easy to accomplish the same thing using a while, thus avoiding the overhead of recursion:
list_pos = list_head;
while (list_pos != NULL) {
printf("%d \t", list_pos->nmbr);
list_pos = list_pos -> p;
}
the print number up here, for the current node.
if(l->p!=NULL)
write(l->p);