we can traverse the binary search tree through recursion like:
void traverse1(node t) {
if( t != NULL) {
visit(t);
traverse1(t->left);
traverse1(t->right);
}
}
and also through loop( with stack) like:
void traverse2(node root) {
stack.push(root);
while (stack.notEmpty()) {
node next = stack.pop();
visit(next);
if (next->right != NULL)
stack.push(next->right);
if (next->left != NUll)
stack.push(next->left)
}
}
Question
Which one is more efficiency? why?
I think these two method time complexity is both O(n). so all the differences are all in the space complexity or ..?
It will depend on how you define efficiency? It is in runtime, amount of code, size of executable, how much memory/stack space is used, or how easy it is to understand the code?
The recursion is very easy to code, hopefully easy to understand, and is less code. Looping may be a bit more complex (depending on how you view complexity) and code. Recursion may be easier to understand and will be less in the amount of code and in executable size. Recursion will use more stack space assuming you have a few items to transverse.
Looping will have a larger amount of code (as your above example shows), and could possibly be considered a bit more complex. But the transverse is just one call to be place on the stack, rather than several. So if you have a lot of items to transverse, loop will be faster as you don't have the time to push items on the stack and the pop them off, which is what will occur when using recursion.
Apart from efficiency, if your tree is too deep or if your stack space is limited, you may run into overflow - stack overflow!!
With iterative approach, you can use the much larger heap space to place the allocated stack. With recursion, you don't have a choice as the stack frames are pushed and popped for you.
I know that such constrained stack environments may be a bit rare; nevertheless, one needs to be aware of it.
Both versions have the same space and time complexity.
The recursion implicitly uses the stack (memory location) for storing call context, and the second uses a stack abstract data type effectively emulating the first version, using stack explicitly.
The difference is that with the first version, you risk stack overflow with deep, unbalanced trees, however it's simpler conceptually (less opportunities for bugs). The second uses dynamic allocation for storing the pointers to parent nodes.
You will have to measure the difference to know for sure. I personally have a feeling that the recursive formulation will beat the one with an explicit stack in this particular instance.
What the non-recursive version has going for it is that it eliminates the calls. On the other hand - depending on the exact library implementation - the pushes and the pop might also resolve to function calls.
Any decent compiler will actually encode your recursive function in a way similar to the following pseudo-code:
void traverse1(node t) {
1:
if( t != NULL) {
visit(t);
traverse1(t->left);
t = t->right;
goto 1;
}
}
Thus eliminating one of the recursive calls. This is known as tail call elimination.
The time and space complexity are the same. The only difference is that traverse2 doesn't call itself recursively. This should make it slightly faster, as pushing/popping from a stack is a cheaper operation than calling a function.
That said, I think the recursive version is "cleaner", so I'd personally use that, unless it turns out to be too slow in practice.
Related
I have an option to either create and destroy a vector on every call to func() and push elements in each iteration, as shown in Example A OR fixed the initialization and only overwrite old values in each iteration, as shown in Example B.
Example A:
void func ()
{
std::vector<double> my_vec(5, 0.0);
for ( int i = 0; i < my_vec.size(); i++) {
my_vec.push_back(i);
// do something
}
}
while (condition) {
func();
}
Example B:
void func (std::vector<double>& my_vec)
{
for ( int i = 0; i < my_vec.size(); i++) {
my_vec[i] = i;
// do something
}
}
while (condition) {
std::vector<double> my_vec(5, 0.0);
func(myVec);
}
Which of the two would be computationally inexpensive. The size of the array won't be more than 10.
I still suspect that the question that was asked is not the question that was intended, but it occurred to me that the main point of my answer would likely not change. If the question gets updated, I can always edit this answer to match (or delete it, if it turns out to be inapplicable).
De-prioritize optimizations
There are various factors that should affect how you write your code. Among the desirable goals are space optimization, time optimization, data encapsulation, logic encapsulation, readability, robustness, and correct functionality. Ideally, all of these goals would be achievable in every piece of code, but that is not particularly realistic. Much more likely is a situation where one or more of these goals must be sacrificed in favor of the others. When that happens, optimizations should typically yield to everything else.
That is not to say that optimizations should be ignored. There are plenty of optimizations that rarely obstruct the higher-priority goals. These range from the small, such as passing by const reference instead of by value, to the large, such as choosing the logarithmic algorithm instead of the exponential one. However, the optimizations that do interfere with the other goals should be postponed until after your code is reasonably complete and functioning correctly. At that point, a profiler should be used to determine where the bottlenecks actually are. Those bottlenecks are the only places where other goals should yield to optimizations, and only if the profiler confirms that the optimizations achieved their goals.
For the question being asked, this means that the main concern should not be computational expense, but encapsulation. Why should the caller of func() need to allocate space for func() to work with? It should not, unless a profiler identified this as a performance bottleneck. And if a profiler did that, it would be much easier (and more reliable!) to ask the profiler if the change helps than to ask Stack Overflow.
Why?
I can think of two major reasons to de-prioritize optimizations. First, the "sniff test" is unreliable. While there might be a few people who can identify bottlenecks by looking at code, there are many, many more who merely think they can. Second, that's why we have optimizing compilers. It is not unheard of for someone to come up with this super-clever optimization trick only to discover that the compiler was already doing it. Keep your code clean and let the compiler handle the routine optimizations. Only step in when the task demonstrably exceeds the compiler's capabilities.
See also: premature-optimization
Choosing an optimization
OK, suppose the profiler did identify construction of this small, 10-element array as a bottleneck. The next step is to test an alternative, right? Almost. First you need an alternative, and I would consider a review of the theoretical benefits of various alternatives to be useful. Just keep in mind that this is theoretical and that the profiler gets the final say. So I'll go into the pros and cons of the alternatives from the question, as well as some other alternatives that might bear consideration. Let's start from the worst options, working our way to the better ones.
Example A
In Example A, a vector is created with 5 elements, then elements are pushed onto the vector until i meets or exceeds the vector's size. Seeing how i and the vector's size are both increased by one each iteration (and i starts smaller than the size), this loop will run until the vector grows large enough to crash the program. That means probably billions of iterations (despite the question's claim that the size will not exceed 10).
Easily the most computationally expensive option. Don't do this.
Example B
In example B, a vector is created for each iteration of the outer while loop, which is then accessed by reference from within func(). The performance cons here include passing a parameter to func() and having func() access the vector indirectly through a reference. There are no performance pros as this does everything the baseline (see below) would do, plus some extra steps.
Even though a compiler might be able to compensate for the cons, I see no reason to try this approach.
Baseline
The baseline I'm using is a fix to Example A's infinite loop. Specifically, replace "my_vec.push_back(i);" with Example B's "my_vec[i] = i;". This simple approach is along the lines of what I would expect for the initial assessment by the profiler. If you cannot beat simple, stick with it.
Example B*
The text of the question presents an inaccurate assessment of Example B. Interestingly, the assessment describes an approach that has the potential to improve on the baseline. To get code that matches the textual description, move Example B's "std::vector<double> my_vec(5, 0.0);" to the line immediately before the while statement. This has the effect of constructing the vector only once, rather than constructing it with each iteration.
The cons of this approach are the same as those of Example B as originally coded. However, we now pick up a gain in that the vector's constructor is called only once. If construction is more expensive than the indirection costs, the result should be a net improvement once the while loop iterates often enough. (Beware these conditions: that's a significant "if" and there is no a priori guess as to how many iterations is "enough".) It would be reasonable to try this and see what the profiler says.
Get some static
A variant on Example B* that helps preserve encapsulation is to use the baseline (the fixed Example A), but precede the declaration of the vector with the keyword static. This brings in the benefit of constructing the vector only once, but without the overhead associated with making the vector a parameter. In fact, the benefit could be greater than in Example B* since construction happens only once per program execution, rather than each time the while loop is started. The more times the while loop is started, the greater this benefit.
The main con here is that the vector will occupy memory throughout the program's execution. Unlike Example B*, it will not release its memory when the block containing the while loop ends. Using this approach in too many places would lead to memory bloat. So while it is reasonable to profile this approach, you might want to consider other options. (Of course if the profiler calls this out as the bottleneck, dwarfing all others, the cost is small enough to pay.)
Fix the size
My personal choice for what optimization to try here would be to start from the baseline and switch the vector to std::array<10,double>. My main motivation is that the needed size won't be more than 10. Also relevant is that the construction of a double is trivial. Construction of the array should be on par with declaring 10 variables of type double, which I would expect to be negligible. So no need for fancy optimization tricks. Just let the compiler do its thing.
The expected possible benefit of this approach is that a vector allocates space on the heap for its storage, which has an overhead cost. The local array would not have this cost. However, this is only a possible benefit. A vector implementation might already take advantage of this performance consideration for small vectors. (Maybe it does not use the heap until the capacity needs to exceed some magic number, perhaps more than 10.) I would refer you back to earlier when I mentioned "super-clever" and "compiler was already doing it".
I'd run this through the profiler. If there's no benefit, there is likely no benefit from the other approaches. Give them a try, sure, since they're easy enough, but it would probably be a better use of your time to look at other aspects to optimize.
For example if we are traversaling a rather big tree by following function, it is possible that we get stack overflow.
void inorder(node* n)
{
if(n == null) return;
inorder(n->l);
n->print();
inorder(n->r);
}
How to add a condition or something into the function to prevent such overflow from happening?
consider iteration over recursion , if that is really a concern.
http://en.wikipedia.org/wiki/Tree_traversal
see the psedo code there for iteration
iterativeInorder
iterativePreorder
iterativePostorder
Basdically use your own list as stack data structure in a while loop , You can effectively replace function recursion.
There's no portable solution other than by replacing recursion
with explicit management of the stack (using
std::vector<Node*>). Non-portably, you can keep track of the
depth using a static variable; if you know the maximum stack
size, and how much stack each recursion takes, then you can
check that the depth doesn't exceed that.
A lot of systems, like Linux and Solaris, you can't know the
maximum stack depth up front, since the stack is allocated
dynamically. At least under Linux and Solaris, however, once
memory has been allocated to the stack, it will remain allocated
and remain affected to the stack. So you can recurse fairly
deeply at the start of the program (possibly crashing, but
before having done anything), and then check against this value
later:
static char const* lowerBound = nullptr;
// At start-up...
void
preallocateStack( int currentCount ) {
{
char dummyToTakeSpace[1000];
-- currentCount;
if ( currentCount <= 0 ) {
lowerBound = dummyToTakeSpace;
} else {
preallocateStack( currentCount - 1 );
}
}
void
checkStack()
{
char dummyForAddress;
if ( &dummyForAddress < lowerBound ) {
throw std::bad_alloc(); // Or something more specific.
}
}
You'll note that there are a couple of cases of
undefined/unspecified behavior floating around in that code, but
I've used it successfully on a couple of occasions (under
Solaris on Sparc, but Linux on PC works exactly the same in this
regard). It will, in fact, work on almost any system where:
- the stack grows down, and
- local variables are allocated on the stack.
Thus, it will also work on Windows, but if it fails to
allocate the initial stack, you'll have to relink, rather than
just run the program at a moment when there's less activity on
the box (or change ulimits) (since the stack size on Windows
is fixed at link time).
EDIT:
One comment concerning the use of an explicit stack: some
systems (including Linux, by default) overcommit, which means
that you cannot reliably get an out of memory error when
extending an std::vector<>; the system will tell
std::vector<> that the memory is there, and then give the
program a segment violation when it attempts to access it.
The thing about recursion is that you can never guarantee that it will never overflow the stack, unless you can put some bounds on both the (minimum) size of memory and (maximum) size of the input. What you can do, however, is guarantee that it will overflow the stack if you have an infinite loop...
I see your "if() return;" terminating condition, so you should avoid infinite loops as long every branch of your tree ends in a null. So one possibility is malformed input where some branch of the tree never reaches a null. (This would occur, e.g., if you have a loop in your tree data structure.)
The only other possibility I see is that your tree data structure is simply too big for the amount of stack memory available. (N.B. this is virtual memory and swap space can be used, so it's not necessarily an issue of insufficient RAM.) If that's the case, you may need to come up with some other algorithmic approach that is not recursive. Although your function has a small memory footprint, so unless you've omitted some additional processing that it does, your tree would really have to be REALLY DEEP for this to be an issue. (N.B. it's maximum depth that's an issue here, not the total number of nodes.)
You could increase the stack size for your OS. This is normally configured through ulimit if you're on a Unix-like environment.
E.g. on Linux you can do ulimit -s unlimited which will set the size of the stack to 'unlimited' although IIRC there is a hard limit and you cannot dedicate your entire memory to one process (although one of the answers in the links below mentions an unlimited amount).
My suggestions would be to run ulimit -s which will give you the current size of the stack and if you're still getting a stack overflow double that limit until you're happy.
Have a look here, here and here for a more detailed discussion on the size of the stack and how to update it.
If you have a very large tree, and you are running into issues with overrunning your stack using recursive traversals, the problem is likely that you do not have a balanced tree. The first suggestion then is to try a balanced binary tree, such as red-black tree or AVL tree, or a tree with more than 2 children per node, such as a B+ tree. The C++ library provides std::map<> and std::set<> which are typically implemented using a balanced tree.
However, one simple way to avoid recursive in-order traversals is to modify your tree to be threaded. That is, use the right pointer of leaf nodes indicate the next node. The traversal of such a tree would look something like this:
n = find_first(n);
while (! is_null(n)) {
n->print();
if (n->is_leaf()) n = n->r;
else n = find_first(n->r);
}
You can add a static variable to keep track of the times the function is called. If it's getting close to what you think would crash your system, perform some routine to notify the user of the error.
A small prototype of the alterations that can be made by associating another int variable with the recursive function.You could pass the variable as an argument to the function starting with zero value by default at the root and decrement it as u go down the tree ...
drawback: this solution comes at the cost of an overhead of an int variable being allocated for every node.
void inorder(node* n,int counter)
{
if(counter<limit) //limit specified as per system limit
{
if(n == null) return;
inorder(n->l,counter-1);
n->print();
inorder(n->r,counter-1);
}
else
n->print();
}
consider for further research:
Although the problem may not be with traversal if only recursion is to be considered. And could be avoided with better tree creation and updation. check the concept of balanced trees if not already considered.
In order to give functions the option to modify the vector I can't do
curr = myvec.at( i );
doThis( curr );
doThat( curr );
doStuffWith( curr );
But I have to do:
doThis( myvec.at( i ) );
doThat( myvec.at( i ) );
doStuffWith( myvec.at( i ) );
(as the answers of my other question pointed out)
I'm going to make a hell lot of calls to myvec.at() then. How fast is it, compared to the first example using a variable to store the result?
Is there a different option for me? Can I somehow use pointers?
When it's getting serious there will be thousands of calls to myvec.at() per second. So every little performance-eater is important.
You can use a reference:
int &curr = myvec.at(i);
// do stuff with curr
The at member function does bounds checking to make sure the argument is within the size of the vector. Profiling is only way to know exactly how much slower it is compared to operator[]. Using a reference here allows you to do the lookup once and then use the result in other places. And you can make it a reference-to-const if you want to protect yourself from accidentally changing the value.
From my own tests with similar code (compiled under gcc and Linux), operator[] can be noticeably faster than at, not because of the bounds checking, but because of the overhead of exception handling. Replacing at (which throws an exception on out-of-bounds) with my own bounds checking that raised an assert on out-of-bounds gave a measurable improvement.
Using a reference, as Kristo said, lets you only incur the bounds checking overhead once.
Ignoring bounds checking and exception handling overhead, both operator[] and at should be optimized to equivalent to direct array access or direct access via pointer.
As Chris Becke said, though, there's no substitute for profiling.
When performance is an issue, there is no substitute for profiling. The optimization capabilities of compilers change from version to version, and tiny, insignificant alterations to source code can dramatically change the resulting performace.
No one can answer this question but yourself: Create a test harness, and throw several algorithms at it and see what you get.
ps. if performance really is an issue, well, i got a factor 10 speed increase out of a png decoder by removing the vectors and replacing them with raw arrays. Again, this was for Visual Studio 6. I am not claiming that a raw array substitution will give you a factor 10 improvement, but it is something to try.
The reason the first doesn't work is that you're not setting a pointer or iterator to the address of the ith variable. Instead you're setting curr equal to the value of the ith variable and then modifying curr. I'm assuming that doThis and doThat are references.
Do this:
MyObject& curr = myvec.at( i );
Operator[] might be faster than at, because it isn't required to do bounds checking.
You can make curr a reference to do what you want.
MyClass & curr = myvec.at(i);
You might also do some benchmarking before getting worried. Modern processors can handle thousands of operations per second quite easily.
Options that I see, in roughly inverse order of preference:
Store pointers in your container instead of the actual objects. This may be advisable anyway, if the objects are complex enough that copying them around is problematic.
Use the indexing operator [] instead of at().
Just call at() once, and save it into a reference (see Kristo's answer above).
Forget about it until you actually have a problem with excessive runtime. If that happens, profile your code first to make sure the bottleneck is here, and only then worry about doing one of the above to speed things up.
Honestly, what you should do is play with the four different approaches, and just use the one that produces the easiest to understand code. In most cases we are happy to sacrifice a few machine cycles for code that is easier for human beings to maintain.
The complexity of at() is constant, i.e., in practice this means that it must be designed to not have any relevant performance penalty.
You can use [], which is also constant complexity, but does not check bounds. This would be equivalent to using pointer arithmetic and, thus, potentially a bit faster than the former.
In any case, vector is specifically designed for constant performance access to any of its elements. So this should be the least of your worries.
Vectors are the most suited for access speed. Access to a random element in a vector is of complexity O(1) compared with O(n) for general linked-lists and O(log n) for link-trees.
However this question is mis-placed as the answer to your other question has lead you astray by not explaining how to fix your original problem by using a reference.
If it is the case, that you load up a vector, then process it without adding or deleting any more elements, then consider getting a pointer to the underlying array, and using array operations on that to 'avoid the vector overhead'.
If you are adding or deleting elements as part of your processing, then this is not safe to do, as the underlying array may be moved at any point by the vector itself.
As far as I assume, std::stack and all such 'handmade' stacks work much slower than stack which is applications one.
Maybe there's a good low-level 'bicycle' already? (Stack realization).
Or it's a good idea to create new thread and use it's own stack?
And how can I work directly with application stack? (asm {} only?)
std::stack is a collection of c++ objects that have stack semantics. It has nothing to do with a thread's stack or the push and pop intructions in assembler code.
Which one are you trying to do
The 'assembler' stack is usually maintained by the hardware and required by various calling conventions, so you have no choice about how to 'allocate' it or 'manage' it. Some architectures have highly configurable stacks but you dont say what arch you are on
If you want a collection with stack semantics and you are writing in c++ then std::stack is your choice unless you can prove that its not fast enough
The only way in which std::stack is significantly slower than the processor stack is that it has to allocate memory from the free store. By default, it uses std::deque for storage, which allocates memory in chunks as needed. As long as you don't keep destroying and recreating the stack, it will keep that memory and not need to allocate more unless it grows bigger than before. So structure code like this:
std::stack<int> stack;
for (int i = 0; i < HUGE_NUMBER; ++i)
do_lots_of_work(stack); // uses stack
rather than:
for (int i = 0; i < HUGE_NUMBER; ++i)
do_lots_of_work(); // creates its own stack
If, after profiling, you find that it's still spending too long allocating memory, then you could preallocate a large block so you only need a single allocation when your program starts up (assuming you can find an upper limit for the stack size). You need to get into the innards of the stack to do this, but it is possible by deriving your own stack type. Something like this (not tested):
class PreallocatedStack : public std::stack< int, std::vector<int> >
{
public:
explicit PreallocatedStack(size_t size) { c.reserve(size); }
};
EDIT: this is quite a gruesome hack, but it is supported by the C++ Standard. More tasteful would be to initialise a stack with a reserved vector, at the cost of an extra allocation. And don't try to use this class polymorphically - STL containers aren't designed for that.
Using the processor stack won't be portable, and on some platforms might make it impossible to use local variables after pushing something - you might end up having to code everything in assembly. (That is an option, if you really need to count every last cycle and don't need portability, but make sure you use a profiler to check that it really is worthwhile). There's no way to use another thread's stack that will be faster than a stack container.
MInner, are you sure that stack operations are/can be bottlenecks of our application? if not, and i can bet for it, just use std::stack and forget about it.
The basic idea that a "handmade" stack is necessarily slower than the one used for function calls is fundamentally flawed. The two work sufficiently similarly that they will typically be close to the same speed. The biggest point that favors the hardware stack is that it's used often enough that the data at or close to the top of that stack will almost always be in the cache. Another stack that you create usually won't be used as often, so there's a much better chance that any given reference will end up going to main memory instead of the cache.
In the other direction, you have a bit more flexibility in allocating memory for your stack. You can create a specialized allocator just for your stack. When the hardware stack overflows, it normally allocates memory using the kernel allocator. The kernel allocator is usually tuned quite carefully, so it's usually pretty efficient -- but it's also extremely general purpose. It can't be written just to do stack allocation really well; it has to be written to do any kind of allocation at least reasonably well. In the process, its ability to do one thing exceptionally well often suffers a bit.
It's certainly possible to create a stack that's arbitrarily slow, but there's no fundamental reason that your stack can't be just as fast (or possibly even faster) than the one provided by the (usual) hardware. I'll repeat though: the single biggest reason for it to be slower is cache allocation, which simply reflects usage.
It depends on your requirement.
If you want to push a userdefined data type on stack, you will need 'handmade' stacks
for others say, you want to push integers, chars, or pointers of objects you can use
asm
{
push
pop
}
but dont mess it up
If we consider recursive function in C/C++, are they useful in any way? Where exactly they are used mostly?
Are there any advantages in terms of memory by using recursive functions?
Edit: is the recursion better or using a while loop?
Recursive functions are primarily used for ease of designing algorithms. For example you need to traverse a directory tree recursively - its depth it limited, so you're quite likely to never face anything like too deep recursion and consequent stack overflow, but writing a tree traversal recursively is soo much easier, then doing the same in iterative manner.
In most cases recursive functions don't save memory compared to iterative solutions. Even worse they consume stack memory which is relatively scarse.
they have many uses and some things become very difficult to impossible without them. iterating trees for instance.
Recursion definitively has advantages at problems with a recursive nature. Other posters named some of them.
To use the capability of C for recursion definitively has advantages in memory management. When you try to avoid recursion, most of the time an own stack or other dynamic data type is used to break the problem. This involves dynamic memory management in C/C++. Dynamic memory management is costly and errorprone!
You can't beat the stack
On the other hand, when you just use the stack and use recursion with local variables -- the memory management is just simple and the stack is most of the time more time-efficient then all the memory management you can do by yourself or with plain C/C++ memory-management. The reason is that the system stack is such a simple and convenient data structure with low overhead and it is implemented using special processor operations that are optimized for this work. Believe me, you can't beat that, since compilers, operation systems and processors are optimized for stack manipulations for decades!
PS: Also the stack does not become fragmented, like heap memory does easily. This way it is also possible to save memory by using the stack / recursion.
Implement QuickSort with and without using recursion, then you can tell by yourself if it's useful or not.
I often find recursive algorithms easier to understand because they involve less mutable state. Consider the algorithm for determining the greatest common divisor of two numbers.
unsigned greatest_common_divisor_iter(unsigned x, unsigned y)
{
while (y != 0)
{
unsigned temp = x % y;
x = y;
y = temp;
}
return x;
}
unsigned greatest_common_divisor(unsigned x, unsigned y)
{
return y == 0 ? x : greatest_common_divisor(y, x % y);
}
There is too much renaming going on in the iterative version for my taste. In the recursive version, everything is immutable, so you could even make x and y const if you liked.
When using recursion, you can store data on the stack (effectively, in the calling contexts of all the functions above the current instance) that you would have instead to store in the heap with dynamic allocation if you were trying to do the same thing with a while loop.
Think of most divide-and-conquer algorithms where there are two things to do on each call (that is, one of the calls is not tail-recursive).
And with respect to Tom's interesting comment/subquestion, this advantage of recursive functions is maybe particularly noticeable in C where the management of dynamic memory is so basic. But that doesn't make it very specific to C.
Dynamic programming is a key area where recursion is crucial, though it goes beyond that (remembering sub-answers can give drastic performance improvements). Algorithms are where recursion is normally used, rather than typical day to day coding. It's more a computer-science concept than a programming one.
One thing that is worth mentioning is that in most functional languages (Scheme for example), you can take advantage of tail call optimizations, and thus you can use recursive functions without increasing the amount of memory in your stack.
Basically, complex recursive tail calls can runs flawlessly in Scheme while in C/C++ the same ones will create a stack overflow.
There are two reasons I see for using recursion:
an algorithm operates on recursive data structures (like e.g. a tree)
an algorithm is of recursive nature (often happens for mathematical problems as recursion often offers beautiful solutions)
Handle recursion with care as there is always the danger of infinite recursion.
Recursive functions make it easier to code solutions that have a recurrence relation.
For instance the factorial function has a recurrence relation:
factorial(0) = 1
factorial(n) = n * factorial(n-1)
Below I have implemented factorial using recursion and looping.
The recursive version and recurrence relation defined above look similar and is
hence easier to read.
Recursive:
double factorial ( int n )
{
return ( n ? n * factorial ( n-1 ) : 1 );
}
Looping:
double factorial ( int n )
{
double result = 1;
while ( n > 1 )
{
result = result * n;
n--;
}
return result;
}
One more thing:
The recursive version of factorial includes a tail call to itself and can be tail-call optimized. This brings the space complexity of recursive factorial down to the space complexity of the iterative factorial.