I have a binary search tree (BST). I need to print it's values in such case: min, max, second min, second max...
For example: if the tree contains values 1,2,3,4,5,6,7, it should print: 1,7,2,6,3,5,4. I can't change the tree, or use another data structure like set or list. Also i need to do it in O(nlogn).
The nodes of BST are represented with struct:
struct nodeT{
int value;
nodeT * left;
nodeT * right;
};
void PrintTree(nodeT * head){
}
For the minimum you could have a function which recursively finds a value equal or larger than another value. You call this first with e.g. INT_MIN which means it will find the lowest value in the tree. Then you use the found value, add one, and call the same function again, and that will find the second smallest value, etc.
For the largest value, use the same basic algorithm, but check for equal or smaller than, and start with INT_MAX.
When the current "smallest" value found is larger than the current "largest" value, then you passed each other and end the searching.
Note 1: This works well with integer values, maybe not so well with floating point values.
Note 2: I have not put any regard to big-O, it's just something I thought of at the top of my head.
In pseudo code:
print_smallest_and_largest()
{
int current_smallest = INT_MIN;
int current_largest = INT_MAX;
// Infinite loop
for (;;)
{
current_smallest = find_equal_or_bigger_than(current_smallest);
current_largest = find_equal_or_smaller_than(current_largest);
if (current_smallest > current_largest)
break; // Got all values
printf("Smallest: %d\n", current_smallest);
printf("Largest: %d\n", current_largest);
++current_smallest;
--current_largest;
}
}
In BST, The left subtree of a node contains only nodes with keys less than the node's key. And the right subtree of a node contains only nodes with keys greater than the node's key. So after a BST has been constructed, you should notice that - the minimum element should be in leftmost node and the maximum element should be in the rightmost node.
So You can do these by traversing the tree in the leftmost or rightmost position to retrieve the minimum/maximum element in O(log n) time:
struct treeNode {
treeNode* left;
treeNode* right;
int key;
}*root;
template <class Object>
treeNode* BinarySearchTree <Object> :: minHelper(treeNode* &node) {
if(node -> left == NULL) return node;
else minHelper(node -> left);
}
template <class Object>
treeNode* BinarySearchTree <Object> :: min() {
return minHelper(root);
}
template <class Object>
treeNode* BinarySearchTree <Object> :: maxHelper(treeNode* &node) {
if(node -> right == NULL) return node;
else maxHelper(node -> right);
}
template <class Object>
treeNode* BinarySearchTree <Object> :: max() {
return maxHelper(root);
}
Here I wrote a complete program with all possible operations with BST
The minimum value is:
if the root has no left child then the root. otherwise the leftmost node, such that the node that has no right branch from root to itself.
The maximum value is: if the root has no right child then the root. otherwise the rightmost node that has no left branch from root to itself.
You can find these two using lg(N) operations in average case
So the complete algorithm becomes:
Find the minimum value. Print it and delete the node. Note that printing and deleting takes lg(N) time
If the tree is not empty, find the maximum value, print it and delete the node. Otherwise go to step 3.
If the tree is not empty, return to step 1. otherwise terminate.
Runtime analysis : By definition, deleting a node in BST takes lg(N) time in average case and we've shown earlier that finding the max or min value takes lg(N) time too(in average case). So the runtime is O(NlgN) in average case.
Is the restriction on no other data structures purely pedagogical (fine if it is)? It would make things simpler if you could use another data structure. In case it isn't obvious, here is a way to do it in O(N) time (but O(N) space)
Traverse the N-element BST and get back the sorted array S in O(N) time, O(N) space.
print out S[0], S[N-1], S[1], S[N-2], S[2], ...
The other, recursive, way is outlined by the other answers.
Related
Trying to find depth of binary search tree. Have done something with a bit of google search but my code is crashing:
int treeDepth(int depth) const
{
int l = this->left->treeDepth(depth);
int d = this->right->treeDepth(depth);
return depth + std::max(l, d);
}
calling this function with: root->treeDepth(1);
First of all, I think that you may be confusing the depth of a tree with the height of a tree. Refer to What is the difference between tree depth and height? for an explanation.
For example, following are the depth and height of the nodes in this (binary) tree ('0' being the root):
0 -- depth 0, height 2
/ \
1 2 -- depth 1, height 1 and 2, respectively, for nodes 1 and 2
/ \
3 4 -- depth 2, height 0
As you can see, that the depth of the tree root is '0', which is O(1) computation. The height of the tree, however, is more interesting with regards to recursion and can be computed using the following:
struct TreeNode {
T _value;
typedef TreeNode* node_ptr;
node_ptr _left, _right;
};
...
int treeHeight(Node* node)
{
return std::max(node->_left? 1 + height(node->_left) : 0,
node->_right? 1 + height(node->_right) : 0);
}
...
std::cout << treeHeight(root);
...
The fundamental idea is this:
During the recursive (depth-first) traversal, if a leaf node is reached, return a value of '0' (which is the height of every leaf node). Otherwise, compute the height of the tree rooted at the non-leaf node (which is the max of the heights of the left subtree and the right subtree of that node + 1 for the node itself). Start from the root of the tree.
The second aspect that I want to address in your question is regarding what I gather from your function signature:
int treeDepth(int depth) const
and the way you are calling it:
root->treeDepth(1);
The depth of a tree is not conducive to being a property of the root. Instead, it is a property of the tree, which is composed of tree nodes, one of which is the root node. So, I would define the class (C++ struct shown here) as follows:
template<class T>
struct BinaryTree {
struct TreeNode {
T _value;
typedef TreeNode* node_ptr;
node_ptr _left, _right;
};
TreeNode _root_node;
typedef typename TreeNode::node_ptr node_ptr;
node_ptr _root;
...
int height(node_ptr node) const;
...
};
Finally, finding the depth of a given node in a tree:
int depth(node_ptr node) const;
// returns depth of node if it is in the tree, else -1
is a problem where you may apply recursion. However, a recursive approach is not natural and a breadth-first (or level-order) traversal will be more suited for this.
there are actually many problems in your code.
Fist of all you need to implement a recursion termination, that is a condition needed to stop your function from calling its self forever
in your case you need to write something like
if(left==nullptr && rigth==nullptr)
return depth;
The recursion termination is VERY IMPORTANT! you always need it when you're writing a recursive function. Without you're 100% going into a never ending loop (in the best case).
then, when you re-call your function you need to change the depth value
i mean, if you're going down on another node of the tree it means that the tree is tall at least "depth+1" so you need to pass depth + 1 not just depth,
and at the and of the function just write
return std::max(l,d);
also,you're using pointers, always write a condition to check if you're really trying to access a well defined address, that is, before even trying to access an address (ex. this->left) you need to write a condition like if( this->left!=nullptr) (nullptr from c++ 11 otherwise NULL will as well get the work done)
You need to read up a little bit about recursion.
One of the fundamental tenets of recursion is that there must be a "stop condition" which will at some point terminate the recursion.
Otherwise, you have what is known as "runaway recursion", your stack fills up, and your program crashes and burns.
In your case, a "stop condition" would be reaching a this->left or this->right which happens to be NULL.
So, for the future readers (as suggested by Barmar in a comment)
int l = left == NULL? 0 : left->treeDepth(depth);
int d = right == NULL? 0 : right->treeDepth(depth);
try something like this:
int treeDepth() const
{
int l = left == NULL? 0 : left->treeDepth();
int d = right== NULL? 0 : right->treeDepth();
return 1 + std::max(l, d);
}
In this case you don't need additional paramether "depth"
I use the following method to traverse* a binary tree of 300 000 levels:
Node* find(int v){
if(value==v)
return this;
else if(right && value<v)
return right->find(v);
else if(left && value>v)
return left->find(v);
}
However I get a segmentation fault due to stack overflow.
Any ideas on how to traverse the deep tree without the overhead of recursive function calls?
*
By "traverse" I mean "search for a node with given value", not full tree traversal.
Yes! For a 300 000 level tree avoid recursion. Traverse your tree and find the value iteratively using a loop.
Binary Search Tree representation
25 // Level 1
20 36 // Level 2
10 22 30 40 // Level 3
.. .. .. .. .. .. ..
.. .. .. .. .. .. .. .. // Level n
Just to clarify the problem further. Your tree has a depth of n = 300.000 levels. Thus, in the worst case scenario a Binary Search Tree (BST) will have to visit ALL of the tree's nodes. This is bad news because that worst case has an algorithmic O(n) time complexity. Such a tree can have:
2ˆ300.000 nodes = 9.9701e+90308 nodes (approximately).
9.9701e+90308 nodes is an exponentially massive number of nodes to visit. With these numbers it becomes so clear why the call stack overflows.
Solution (iterative way):
I'm assuming your Node class/struct declaration is a classic standard integer BST one. Then you could adapt it and it will work:
struct Node {
int data;
Node* right;
Node* left;
};
Node* find(int v) {
Node* temp = root; // temp Node* value copy to not mess up tree structure by changing the root
while (temp != nullptr) {
if (temp->data == v) {
return temp;
}
if (v > temp->data) {
temp = temp->right;
}
else {
temp = temp->left;
}
}
return nullptr;
}
Taking this iterative approach avoids recursion, hence saving you the hassle of having to recursively find the value in a tree so large with your program call stack.
A simple loop where you have a variable of type Node* which you set to the next node, then loop again ...
Don't forget the case that the value you are searching for does not exist!
You could implement the recursion by not using the call stack but a user-defined stack or something similar; this could be done via the existing stack template. The approach would be to have a while loop which iterates until the stack is empty; as the existing implementaion uses depth-first search, elimination of the recursive calls can be found here.
When the tree that you have is a Binary Search Tree, and all you want to do is search for a node in it that has a specific value, then things are simple: no recursion is necessary, you can do it using a simple loop as others have pointed out.
In the more general case of having a tree which is not necessarily a Binary Search Tree, and wanting to perform a full traversal of it, the simplest way is using recursion, but as you already understand, if the tree is very deep, then recursion will not work.
So, in order to avoid recursion, you have to implement a stack on the C++ heap. You need to declare a new StackElement class that will contain one member for each local variable that your original recursive function had, and one member for each parameter that your original recursive function accepted. (You might be able to get away with fewer member variables, you can worry about that after you have gotten your code to work.)
You can store instances of StackElement in a stack collection, or you can simply have each one of them contain a pointer to its parent, thus fully implementing the stack by yourself.
So, instead of your function recursively calling itself, it will simply consist of a loop. Your function enters the loop with the current StackElement being initialized with information about the root node of your tree. Its parent pointer will be null, which is another way of saying that the stack will be empty.
In every place where the recursive version of your function was calling itself, your new function will be allocating a new instance of StackElement, initializing it, and repeating the loop using this new instance as the current element.
In every place where the recursive version of your function was returning, your new function will be releasing the current StackElement, popping the one that was sitting on the top of the stack, making it the new current element, and repeating the loop.
When you find the node you were looking for, you simply break from the loop.
Alternatively, if the node of your existing tree supports a) a link to its "parent" node and b) user data (where you can store a "visited" flag) then you don't need to implement your own stack, you can just traverse the tree in-place: in each iteration of your loop you first check if the current node is the node you were looking for; if not, then you enumerate through children until you find one which has not been visited yet, and then you visit it; when you reach a leaf, or a node whose children have all been visited, then you back-track by following the link to the parent. Also, if you have the freedom to destroy the tree as you are traversing it, then you do not even need the concept of "user data": once you are done with a child node, you free it and make it null.
Well, it can be made tail recursive at the cost of a single additional local variable and a few comparisons:
Node* find(int v){
if(value==v)
return this;
else if(!right && value<v)
return NULL;
else if(!left && value>v)
return NULL;
else {
Node *tmp = NULL;
if(value<v)
tmp = right;
else if(value>v)
tmp = left;
return tmp->find(v);
}
}
Walking through a binary tree is a recursive process, where you'll keep walking until you find that the node you're at currently points nowhere.
It is that you need an appropriate base condition. Something which looks like:
if (treeNode == NULL)
return NULL;
In general, traversing a tree is accomplished this way (in C):
void traverse(treeNode *pTree){
if (pTree==0)
return;
printf("%d\n",pTree->nodeData);
traverse(pTree->leftChild);
traverse(pTree->rightChild);
}
The following code is used to find all the root-to-leaf paths which equal a particular sum, given a binary tree and a sum.
class Solution {
public:
void buildResult(std::vector< std::vector< int > >& result, std::vector< int >& ans, TreeNode* root, int sum) {
if(!root)
return;
ans.push_back(root->val);
if(root->val==sum && !root->left && !root->right)
result.push_back(ans);
buildResult(result, ans, root->left, sum-(root->val));
buildResult(result, ans, root->right, sum-(root->val));
ans.pop_back();
}
vector<vector<int>> pathSum(TreeNode* root, int sum) {
std::vector< vector< int > > result;
std::vector< int > ans;
if(!root)
return result;
buildResult(result, ans, root, sum);
return result;
}
};
The above code works and generates the expected output. However, I do not understand the use of the statement ans.pop_back(); - I understand it is for backtracking, but when exactly is this backtracking carried out? The values are being inserted into the vector ans even before checking if they lie on a valid path. Besides, the number of pop_back()s should be many depending upon how many numbers leading to an incorrect sum have been inserted. Could someone please explain this working to me?
Thanks!
The backtracking gets carried out by the virtue of the recursive function returning.
The recursive function is recording its path, as it traverses the tree, by pushing the current node into ans. When it reaches the leaf it checks the leaf's value, and if it matches it records the path that it just built, by pushing ans into result.
You can think of ans as a bread-crumb trail to the current node. Each time the recursive function enters a node in the tree, it pushes the current node's value into ans, and when the right leaf is reached, the tail of breadcrumbs is the path to the leaf node.
Consequently, the recursive function leaves the current node it's visiting by returning. By returning from the recursive call, the recursive function returns to its parent. But not before removing the very last breadcrumb from the current tail of breadcrumbs, restoring the trail of breadcrumbs to contain just the path to its parent, which is precisely where it is returning to.
I have implement a link-based BST (binary search tree) in C++ for one of my assignment. I have written my whole class and everything works good, but my assignment asks me to plot the run-times for:
a. A sorted list of 50000, 75000, and 100000 items
b. A random list of 50000, 75000, and 100000 items
That's fine, I can insert the numbers but it also asks me to call the FindHeight() and CountLeaves() methods on the tree. My problem is that I've implemented the two functions using recursion. Since I have a such a big list of numbers I'm getting getting a stackoverflow exception.
Here's my class definition:
template <class TItem>
class BinarySearchTree
{
public:
struct BinarySearchTreeNode
{
public:
TItem Data;
BinarySearchTreeNode* LeftChild;
BinarySearchTreeNode* RightChild;
};
BinarySearchTreeNode* RootNode;
BinarySearchTree();
~BinarySearchTree();
void InsertItem(TItem);
void PrintTree();
void PrintTree(BinarySearchTreeNode*);
void DeleteTree();
void DeleteTree(BinarySearchTreeNode*&);
int CountLeaves();
int CountLeaves(BinarySearchTreeNode*);
int FindHeight();
int FindHeight(BinarySearchTreeNode*);
int SingleParents();
int SingleParents(BinarySearchTreeNode*);
TItem FindMin();
TItem FindMin(BinarySearchTreeNode*);
TItem FindMax();
TItem FindMax(BinarySearchTreeNode*);
};
FindHeight() Implementation
template <class TItem>
int BinarySearchTree<TItem>::FindHeight()
{
return FindHeight(RootNode);
}
template <class TItem>
int BinarySearchTree<TItem>::FindHeight(BinarySearchTreeNode* Node)
{
if(Node == NULL)
return 0;
return 1 + max(FindHeight(Node->LeftChild), FindHeight(Node->RightChild));
}
CountLeaves() implementation
template <class TItem>
int BinarySearchTree<TItem>::CountLeaves()
{
return CountLeaves(RootNode);
}
template <class TItem>
int BinarySearchTree<TItem>::CountLeaves(BinarySearchTreeNode* Node)
{
if(Node == NULL)
return 0;
else if(Node->LeftChild == NULL && Node->RightChild == NULL)
return 1;
else
return CountLeaves(Node->LeftChild) + CountLeaves(Node->RightChild);
}
I tried to think of how I can implement the two methods without recursion but I'm completely stumped. Anyone have any ideas?
Recursion on a tree with 100,000 nodes should not be a problem if it is balanced. The depth would only be maybe 17, which would not use very much stack in the implementations shown. (log2(100,000) = 16.61). So it seems that maybe the code that is building the tree is not balancing it correctly.
I found this page very enlightening because it talks about the mechanics of converting a function that uses recursion to one that uses iteration.
It has examples showing code as well.
May be you need to calculate this while doing the insert. Store the heights of nodes, i.e add an integer field like height in the Node object. Also have counters height and leaves for the tree. When you insert a node, if its parent is (was) a leaf, the leaf count doesnt change, but if not, increase leaf count by 1. Also the height of the new node is parent's height + 1, hence if that is greater than the current height of the tree, then update it. Its a homework, so i wont help with the actual code
Balance your tree occasionally. If your tree is getting stackoverflow on FindHeight(), that means your tree is way unbalanced. If the tree is balanced it should only have a depth of about 20 nodes for 100000 elements.
The easiest (but fairly slow) way of re-balancing unbalanced binary tree is to allocate an array of TItem big enough to hold all of the data in the tree, insert all of your data into it in sorted order, and delete all of the nodes. Then rebuild the tree from the array recursively. The root is the node in the middle. root->left is the middle of the left half, root->right is the middle of the right half. Repeat recursively. This is the easiest way to rebalance, but it is slowish and takes lots of memory temporarily. On the other hand, you only have to do this when you detect that the tree is very unbalanced, (depth on insert is more than 100).
The other (better) option is to balance during inserts. The most intuitive way to do this is to keep track of how many nodes are beneath the current node. If the right child has more than twice as many "child" nodes as the left child, "rotate" left. And vice-versa. There's instrcutions on how to do tree rotates all over the internet. This makes inserts slightly slower, but then you don't have occassional massive stalls that the first option creates. On the other hand, you have to constantly update all of the "children" counts as you do the rotates, which isn't trivial.
In order to count the leaves without recursion, use the concept of an iterator like the STL uses for the RB-tree underlying std::set and std::map ... Create a begin() and end() function for you tree that indentifies the ordered first and last node (in this case the left-most node and then the right-most node). Then create a function called
BinarySearchTreeNode* increment(const BinarySearchTreeNode* current_node)
that for a given current_node, will return a pointer to the next node in the tree. Keep in mind for this implementation to work, you will need an extra parent pointer in your node type to aid in the iteration process.
Your algorithm for increment() would look something like the following:
Check to see if there is a right-child to the current node.
If there is a right-child, use a while-loop to find the left-most node of that right subtree. This will be the "next" node. Otherwise go to step #3.
If there is no right-child on the current node, then check to see if the current node is the left-child of its parent node.
If step #3 is true, then the "next" node is the parent node, so you can stop at this point, otherwise go the next step.
If the step #3 was false, then the current node is the right-child of the parent. Thus you will need to keep moving up to the next parent node using a while loop until you come across a node that is a left-child of its parent node. The parent of this left-child node will then be the "next" node, and you can stop.
Finally, if step #5 returns you to the root, then the current node is the last node in the tree, and the iterator has reached the end of the tree.
Finally you'll need a bool leaf(const BinarySearchTreeNode* current_node) function that will test whether a given node is a leaf node. Thus you counter function can simply iterate though the tree and find all the leaf nodes, returning a final count once it's done.
If you want to measure the maximum depth of an unbalanced tree without recursion, you will, in your tree's insert() function, need to keep track of the depth that a node was inserted at. This can simply be a variable in your node type that is set when the node is inserted in the tree. You can then iterate through the three, and find the maximum depth of a leaf-node.
BTW, the complexity of this method is unfortunately going to be O(N) ... nowhere near as nice as O(log N).
int findLargest (ListNode *p)
// --------------------------------------------------------------------------
// Preconditions: list head pointer is passed as a parameter.
// Postconditions: returns the largest value in the linked list.
// --------------------------------------------------------------------------
{
if (p->item != NULL)
{
int largest = p->item;
if (largest > p->next->item)
...
}
...
}
Is it possible to write this recursive function passing only a pointer as a parameter? I can't figure out how to do this without adding more parameters. Any ideas? I am only using sequential search. Nothing fancy.
Here is the portion of class List that will be needed:
struct ListNode
{
ListItemType item; // A data item on the list.
ListNode *next; // Pointer to next node
}; // end ListNode
ListNode *head; // Pointer to linked list of items.
I am mainly worried about the feasibility of the problem. Can this be done with only a pointer as a parameter?
Though tail-recursion optimization isn't required by C, if you can transform it into tail-recursion (and you can without a lot of work here), then, when that optimization is applied, you can maintain the readability, clarity, and conciseness of recursion with the same performance (time and space) as the best non-recursive solution.
I've slightly modified the function's conditions so it can work on an empty list with no nodes (where p is null) and will return null in that case. This is tail-recursive, and does require another parameter:
ListNode* findLargestRecurse(ListNode* p, ListNode* largest) {
// this is an implementation detail, and would not be called directly
// requires that largest is not null, but p may be null
if (!p) return largest;
if (p->item > largest->item) largest = p;
return findLargestRecurse(p->next, largest);
}
// preconditions: list head pointer is passed as a parameter, and may be null
// postcondition: returns the node with the largest value, or null
ListNode* findLargest(ListNode* p) {
if (!p) return 0; // mark A, see below
return findLargestRecurse(p->next, p);
}
Notice you use the main entry, findLargest, to setup the initial conditions/context for the actual recursion, findLargestRecurse.
However, I'd write that tail-recursion as an iterative while loop to rely less on what's currently both very compiler-specific and generally unfamiliar in C, which is not hard to do:
// preconditions: list head pointer is passed as a parameter, and may be null
// postcondition: returns the node with the largest value, or null
ListNode* findLargest(ListNode* p) {
ListNode* largest = 0;
for (; p; p = p->next) {
if (!largest || p->item > largest->item) {
largest = p;
}
}
return largest;
}
You can separate out the !largest condition from the loop by doing it beforehand, by checking a base case just like the first findLargest does ("mark A").
And looking at this now, you may wonder why I called the recursive version more concise: it really isn't for this trivial example. That's partially because C is designed to favor iteration (notice the for loop in particular), somewhat because I tried to be verbose in the recursion instead of squashing it down as I would normally (so you could understand it easier), and the rest is because this is just a simple problem.
I find that most recursive problems can be solved using the framework/template of thinking about:
What information do I have at hand right now?
What information will I get if I make a recursive call?
How can I combine those two to make a final result?
(Also, be sure to be clear about the 'base case'.)
In this case, the answers are
the value in the current node
the largest element in the 'suffix' of the list that comes after this node
hmmm, that's for you to figure out
(What should you return for the empty list? Does the homework say?)
Have fun. :)
I have seen some code posted and could not refrain to add my own... because I really think it could be done more simply :)
I suppose that item is of a numeric type.
#include <algorithm> // std::max
#include <limits> // std::numeric_limits
ListItemType findLargest(const ListNode* p)
{
if (p == 0)
return std::numeric_limits<ListItemType>::min();
else
return std::max(p->item, findLargest(p->next));
}
As you can see, much simpler, and I took the liberty to add a const since we're certainly not going to have to modify the list itself!
This is definitely feasible, although I agree that recursion is not the best solution to solve this problem. In this case, non-recursive code would be easier to read (recursion), faster (overhead of function call), and more memory efficient (obviously more stack frames).
Each recursive call returns the greater of either it's value or the value from the rest of the list.
int findLargest (ListNode *p) {
int current = p->item;
int next;
if (p->next == NULL) {
//The value at this node is obviously larger than a non-existent value
return current;
} else {
//Recur to find the highest value from the rest of the LinkedList
next = findLargest(p->next);
}
//Return the highest value between this node and the end of the list
if (current > next) {
return current;
} else {
return next;
}
}
Recursion stops when the next item is null.
Java Version
return max(head, head.value);
int max(Node node, int currentMax)
{
if(node==null)
return currentMax;
if(node.value>currentMax)
return max(node.next, node.value);
else
return max(node.next, currentMax);
}
If you are looking to return just the largest value, then yes you pretty much already have it written.
int FindLargest(ListNode* node){
if (node != NULL){
int downTheListLargest = FindLargest(node->next);
if (downTheListLargest > node->item){
return downTheListLargest;
}
return node->item;
}
return //?? some max negative value
}
If you are looking to return a pointer to the largest node, then the parameter needs to be a double pointer (**), or the function needs to return a pointer.
ListNode* FindLargest(ListNode* node){
if (node == NULL){
return NULL;
}
ListNode* downTheListLargestNode = FindLargest(node->next);
if (downTheListLargestNode && downTheListLargestNode->item > node->item){
return downTheListLargestNode;
}
return node;
}
Really there is no reason to do this recursively. I assume this is just an exercise to learn about recursion, but I feel it is a poor learning example.
Here’s another idiomatic recursive solution, similar to Matthieu’s. Unlike his solution, this one requires an empty list – arguably, taking the smallest item of an empty list isn’t a meaningful operation:
// Precondition: list is non-empty.
int find_largest(ListNode const* n) {
assert(n != 0 && "find_largest requires non-empty list.");
return n->next == 0 ? n->item
: max(n->item, find_largest(n->next));
}
This one reads much like a mathematical definition, using the “cases” notation:
{ item(i), if i is the last node
largest(i) = {
{ max{item(i), largest(i+1)} else.
No need for recursion, and your example isn't recursion (it would have to call itself).
It is possible to do this with only a pointer as a parameter.
Hint: Assign p to p->next to advance through the list.
Always break recursion problems into two steps: the stop condition and "the rest of the problem". Start by thinking about the stop condition. In linked lists it's usually the null node. But in your case, think what happens when the given node is null. What would you return? The truth is that you have to return the max value no matter what, and when there are no elements in the list there is no max element. In this case maybe you can just assume that a list must therefore have at least one element.
So what's the stop condition? The stop condition is when there's a single element in the list; and in this case the max value is that node's value.
The next step is the recursive step. Suppose you have an element linked to a list. And pay attention how I describe a linked list: a node linked to a linked list. The max value is the value of that node if it's larger than the largest value of the list, or the largest value of the list otherwise.