2

I know people usually ask this question the other way round, but I have the following problem: I have this iterative function which counts all the nodes in a circular doubly link list containing the data value 20. Now, how do I make this recursive, what will be the base case (terminating case) for the recursive function? Any help is appreciated:

int count(node *start)
{
    int c;
    c = 0;
    if(start == NULL)
        return 0;
    if((start->roll_no) == 20)
        c = 1;
    node *current = start;
    while(current->next != start)
    {
        if((current->next->roll_no) == 20){
        c++;
        }
        current = current->next;
    }

    return c;
}
user1017072
  • 81
  • 1
  • 3
  • 11
  • no, I just want to understand iterative to recursion conversion. I see lots of documents the other way round, but I tend to think in iterative manner, so just wanted to know if there is a fixed way to go about it and if yes then how to handle the base case. Thanks – user1017072 Oct 29 '11 at 14:34
  • @user1017072: you're already handling the base case in the iterative version. – Mat Oct 29 '11 at 14:39
  • yes Mat, but I want to see its recursive version to understand the overhead. Thanks – user1017072 Oct 29 '11 at 14:42

5 Answers5

4

I think this should work (but note that it requires an extra argument for tracking start):

int count(node *start)
{
    return count_helper(start, start);
}
int count_helper(node *current, node *start)
{
    int c;
    c = 0;
    if(current == NULL)
        return 0;
    if((current->roll_no) == 20)
        c = 1;
    if(current->next == start) return c;
    return (c + count_helper(current->next, start));
}
Ben Hocking
  • 7,790
  • 5
  • 37
  • 52
  • Hi Ben thank you for the reply, I am sorry to bother you, but could you help me in understanding what the last two lines are doing in the code: if(current->next == start) return c; return (c + count_helper(current->next, start)); Thank you very much for your help. – user1017072 Oct 29 '11 at 14:49
  • `if (current->next == start) return c;` is the terminating condition for when you hit the loop re-entry point. – Ben Hocking Oct 29 '11 at 14:54
  • `return (c + count_helper(current->next, start));` takes the current value of `c` and performs a tail recursion on the remaining part of the loop. Try it out with a small loop and step through it. – Ben Hocking Oct 29 '11 at 14:55
  • 2
    @BenHocking: I don't think `c + count_helper(current->next, start)` is technically tail recursive. It has to store `c` in each stack frame (unless the optimiser is smart enough to deal with that, which it likely is). – mange Oct 29 '11 at 15:00
  • Thank you very much for all the help Ben and Mange. – user1017072 Oct 29 '11 at 15:15
1
int count_recursive (node* current, node* start) {
  if (current == NULL)
    return 0;
  if (current->next == start)
    return (current->roll_no == 20 ? 1 : 0);
  if (current->roll_no == 20)
    return count_recursive(current->next, start) + 1;
  else
    return count_recursive(current->next, start);
}
int count(node* start) {
  return count_recursive(start, start);
}

The base case is "the list is empty" or "we are at the end of the list". Then you recurse by taking the rest of the list (without the item we just looked at) and doing the exact same thing.

Note that this is not tail recursive (although it may get optimised into a tail recursive option), so it will grow the stack and may explode.

Tail recursively:

int count_recursive (node* current, node* start, int c) {
  if (current == NULL)
    return c;
  if (current->next == start)
    return (current->roll_no == 20 ? 1 : 0) + c;
  if (current->roll_no == 20)
    return count_recursive(current->next, start, c+1);
  else
    return count_recursive(current->next, start, c);
}
int count(node* start) {
  return count_recursive(start, start, 0);
}

The tail recursive option is more likely to be optimised into a loop by the compiler, but a sufficiently intelligent compiler should turn them both into a loop.

mange
  • 3,172
  • 18
  • 27
  • Hi mange, I was just going through your tail recursive solution to understand the benefits. The program seems to return 0, no matter how many nodes have 20 in them. I am guessing it has some issue with parameter c. Thanks – user1017072 Oct 30 '11 at 07:56
  • Did you put the `c+1` in the `if (current->roll_no == 20)` instead of just `c`? The general idea is that instead of using named variables you pass through the variables as arguments to the next function, so instead of `c = c+1`, you pass `c+1` as the `c` argument of the next function call. – mange Oct 30 '11 at 13:35
  • Ah, I've just realised that this actually doesn't work at all because I'm stupid. Fixing it now. (I wasn't testing the start properly, so it terminated immediately.) – mange Oct 30 '11 at 13:57
1
int count(struct node * ptr)
{
    return ptr==NULL ? 0 : (ptr->roll_no == 20 ? 1:0) + count(ptr->next);
}

UPDATE: it appears the list is circular.

int count(struct node * start, struct node * ptr)
{
    return ptr==NULL || ptr->next == start ? 0 
                                           : (ptr->roll_no == 20 ? 1:0) 
                                             + count(start, ptr->next);
}
/* to be called like: */
cnt = count (the_list, the_list);

UPDATE 2: (failure to count the last node)

int count(struct node * start, struct node * ptr)
{
    return ptr==NULL  ? 0 
                      : (ptr->roll_no == 20 ? 1:0) 
                        + ptr->next == start ? 0
                                             : count(start, ptr->next);
}

UPDATE3: it did need an extra pair of parentheses...

#include <stdio.h>

struct node {
        struct node *next;
        int roll_no;
        };

struct node nodes[8] =
{{ nodes+1, 20} ,{ nodes+2, 0}
,{ nodes+3, 20} ,{ nodes+4, 0}
,{ nodes+5, 20} ,{ nodes+6, 0}
,{ nodes+7, 20} ,{ nodes+0, 0}
};

unsigned count(struct node * start, struct node * ptr)
{
    return ptr==NULL
              ? 0
              : (ptr->roll_no == 20 ? 1:0)
                + (ptr->next == start
                    ? 0
                    : count(start, ptr->next)
                  )
              ;
}

#define COUNT(p) count(p,p)

int main (void)
{
unsigned cnt,idx;

for (idx = 0; idx < 8 ; idx++) {
    cnt = COUNT (nodes+idx);
    printf ("count@%u = %u\n", idx, cnt);
    }

return 0;
}
wildplasser
  • 43,142
  • 8
  • 66
  • 109
  • The list is circular (I made the same mistake at first). – mange Oct 29 '11 at 15:37
  • Well: don't make it circular, than ;-) (why is this not in the title?) I'll try to update my snippet... BTW: it is a double linked list, too. – wildplasser Oct 29 '11 at 15:41
  • @wildplasser, sorry for not mentioning circular and doubly linked list in the title. For some reason, the code leaves out counting the first node if it has 20. – user1017072 Oct 29 '11 at 16:35
  • No, it does recurse if ptr->roll_no == 20; there are prentheses. But it fails to count the last node. I'll update. again. – wildplasser Oct 29 '11 at 17:06
  • Thank you wildplasser, I have one more query, sorry to trouble you. Can this be made efficient by using other data structures like Hash table to store the index of linked list? or am I wrong in my assumption? Thanks – user1017072 Oct 29 '11 at 19:09
  • 1
    Off course you can. It all depends on your typical usage of the datastructure. You could even maintain a counter that is incremented every time a node with (ptr->roll_no == 20) is inserted (and decremented, etc) Or you could keep two lists, or a tree or whatever. It all depends on what you want to do. BTW a LL is a rather unpractical datastructure. A cyclic LL is seen very rarely. I think I never saw one in my 20+ years of programming (and reading programs). – wildplasser Oct 29 '11 at 19:40
  • Also: my code fragment is not intended to be used, only to show that the tail recursion can be invoked by a one line program. Also: the (nested) usage of the ternary operator can easily cause precedence errors. I did not get it right the first time. Since this is homework, I advise you to try to resolve the precedence of the fragment _without_ the extra parentheses. – wildplasser Oct 29 '11 at 19:46
  • That is not true tail recursion and is relying on the compiler to perform a non-trivial optimisation, just to be clear. Also, never ever write code like that in real projects: it is far harder to read than is necessary. – mange Oct 29 '11 at 23:54
  • I have one more quick question, can tortoise and hare algorithm help in any way in improving the efficiency of this program? Thank you – user1017072 Nov 01 '11 at 03:10
0

There is no making this recursive, because there is no recursion in the structure, it is already a list.

Usually, because recursion has a lot of overhead, you can rewrite such a function to iterative, by creating a list (or stack) of 'levels to do'. Instead of recursively calling the function, you can push the item on the stack, and loop your routine until the stack is empty.

An example is listing a file tree. Instead of calling the function recursively for each found directory, you can also push the directory name on a stack of directories to process. That way, you don't have a large number of directory handles or iterators or whatever you're using, when you have a deeply nested tree.

But none of that applies here, since you don't have a tree at all.

It can be done, though: You could replace the while with a recursive call, but you would have to pass the original start, or make it global, or else you wouldn't know when to break the recursion. Also, though possible, you will get it trouble soon if you got a long list.

GolezTrol
  • 114,394
  • 18
  • 182
  • 210
0

How about this :

int count(node *start){  
   if(!start) return 0;  
   int countValue = 0; 
   return count(start,start,&countValue);
}


int count(node *start, node *next, int *count){  
    if(start == next)//circular list so this is your base
       return *count;
    if(next->next->roll_no) == 20){  
         *count++;
    }  
    return count(start,next->next,count);  
}

It is not custom for recursive functions to modify input params though.
Nor to use use global variable.
This is the first approach I could come up

BenMorel
  • 34,448
  • 50
  • 182
  • 322
Cratylus
  • 52,998
  • 69
  • 209
  • 339