1. Problem Description
This is
a Google interview question for software engineers from careercup. Here is the original thread description,
"
Find longest consecutive path in a binary tree.
1. the path can be decreasing or increasing, i.e [1,2,3,4] and [4,3,2,1] are both valid
2. the path can be child-parent-child, not necessarily a parent-to-child path
similar to this question: http://www.geeksforgeeks.org/longest-consecutive-sequence-binary-tree/
- wtcupup2017 December 10, 2016 in United States | Report Duplicate | Flag
".
2. Data Structure and Algorithm
Pseudo-code
- Generate child-to-parent consecutive depth map for each node
* Find the longest ascending sequence if its left child < itself
* Find the longest descending sequence if its left child > itself
* Find the longest ascending sequence of its right child < itself
* Find the longest descending sequence if its right child > itself
* 0 sequence if any child is null
* 0 sequence if any child is equal to itself
- Find the node with longest consecutive node in above map
* Take the longer sequence if both children has same order sequence
* Sum the sequence if two children has difference order sequence
3. C++ Implementation
Some of utility function and class can be found on (also two similar problems) my two previous posts -
BTS - Find the Longest Sequence and
BTS - Find the Deepest Path.
// ********************************************************************************
// Implementation
// ********************************************************************************
template <typename T>
size_t
GenerateOrderedDepthMap(TreeNode<T> *root, DepthMap<T> &depth)
{
if (!root) {
// special case - NULL pointer return 0;
}
// depth of this node and save into the map size_t leftDepth = GenerateOrderedDepthMap(root->left, depth);
size_t rightDepth = GenerateOrderedDepthMap(root->right, depth);
if (leftDepth != 0) {
// ascending order if (*root->data > *root->left->data) {
auto itFound = depth.find(root->left);
assert(itFound != depth.end());
if (itFound->second.depthOfLeft) {
// ascending order if (*root->left->data > *root->left->left->data) {
leftDepth = itFound->second.depthOfLeft + 1;
}
}
if (itFound->second.depthOfRight) {
// ascending order if (*root->left->data > *root->left->right->data) {
if (leftDepth < itFound->second.depthOfRight + 1) {
leftDepth = itFound->second.depthOfRight + 1;
}
}
}
}
else if (*root->data < *root->left->data) {
// descending order auto itFound = depth.find(root->left);
assert(itFound != depth.end());
if (itFound->second.depthOfLeft) {
// descending order if (*root->left->data < *root->left->left->data) {
leftDepth = itFound->second.depthOfLeft + 1;
}
}
if (itFound->second.depthOfRight) {
// descending order if (*root->left->data < *root->left->right->data) {
if (leftDepth < itFound->second.depthOfRight + 1) {
leftDepth = itFound->second.depthOfRight + 1;
}
}
}
}
else {
leftDepth = 0;
}
}
if (rightDepth != 0) {
// descending order if (*root->data > *root->right->data) {
auto itFound = depth.find(root->right);
assert(itFound != depth.end());
if (itFound->second.depthOfLeft) {
// descending order if (*root->right->data > *root->right->left->data) {
rightDepth = itFound->second.depthOfLeft + 1;
}
}
if (itFound->second.depthOfRight) {
// descending order if (*root->right->data > *root->right->right->data) {
if (rightDepth < itFound->second.depthOfRight + 1) {
rightDepth = itFound->second.depthOfRight + 1;
}
}
}
}
else if (*root->data < *root->right->data) {
// ascending order auto itFound = depth.find(root->right);
assert(itFound != depth.end());
if (itFound->second.depthOfLeft) {
// ascending order if (*root->right->data < *root->right->left->data) {
rightDepth = itFound->second.depthOfLeft + 1;
}
}
if (itFound->second.depthOfRight) {
// ascending order if (*root->right->data < *root->right->right->data) {
if (rightDepth < itFound->second.depthOfRight + 1) {
rightDepth = itFound->second.depthOfRight + 1;
}
}
}
}
else {
rightDepth = 0;
}
}
depth.insert(std::make_pair(root, TreeNodeDepth(leftDepth, rightDepth)));
return 1;
}
enum WhichChildToSearch
{
INVALID = 0,
LEFT,
RIGHT,
BOTH
};
template <typename T>
std::list<TreeNode<T>*>
FindTheOrderedPathFromRoot(TreeNode<T> *root,
const DepthMap<T> & dm,
const size_t depth)
{
std::list<TreeNode<T>*> path;
if (!root) {
return path;
}
size_t tempDepth = depth;
auto itFound = dm.find(root);
while (itFound != dm.cend()) {
path.push_back(itFound->first);
if (itFound->second.depthOfLeft == tempDepth) {
itFound = dm.find(itFound->first->left);
}
else if (itFound->second.depthOfRight == tempDepth) {
itFound = dm.find(itFound->first->right);
}
if (tempDepth == 0) {
break;
}
--tempDepth;
}
assert(tempDepth == 0);
return path;
}
template <typename T>
std::list<TreeNode<T>*>
FindTheLongestOrderedSeq(TreeNode<T> *root)
{
DepthMap<T> dm;
// generate depth map GenerateOrderedDepthMap(root, dm);
auto itEnd = dm.cend();
TreeNode<T>* nodeFound = NULL;
size_t maxDepth = 0;
WhichChildToSearch maxDepthSearch = INVALID;
size_t tmp;
WhichChildToSearch search = INVALID;
// find the longest consecutive order for (auto it = dm.cbegin(); it != itEnd; ++it) {
tmp = 0;
if (!it->first->left) {
tmp = it->second.depthOfRight;
search = RIGHT;
}
else if (!it->first->right) {
tmp = it->second.depthOfLeft;
search = LEFT;
}
else {
if ((*it->first->left->data < *it->first->data &&
*it->first->data < *it->first->right->data) ||
(*it->first->left->data > *it->first->data &&
*it->first->data > *it->first->right->data)) {
tmp = it->second.depthOfLeft + it->second.depthOfRight;
search = BOTH;
}
else {
if (it->second.depthOfLeft > it->second.depthOfRight) {
tmp = it->second.depthOfLeft;
search = LEFT;
}
else {
tmp = it->second.depthOfRight;
search = RIGHT;
}
}
}
if (tmp > maxDepth) {
maxDepth = tmp;
maxDepthSearch = search;
nodeFound = it->first;
}
}
// populate the sequence
std::list<TreeNode<T>*> path;
if (nodeFound && maxDepthSearch) {
if (maxDepthSearch == LEFT || maxDepthSearch == RIGHT) {
path = FindTheOrderedPathFromRoot(nodeFound,
dm,
maxDepth);
if (maxDepthSearch == LEFT) {
path.reverse();
}
}
else {
auto itFound = dm.find(nodeFound);
assert(itFound != dm.cend());
path = FindTheOrderedPathFromRoot(nodeFound->left,
dm,
itFound->second.depthOfLeft - 1);
std::list<TreeNode<T>*> rightPath = FindTheOrderedPathFromRoot(
nodeFound,
dm,
itFound->second.depthOfRight);
path.reverse();
path.splice(path.end(), rightPath);
}
assert(path.size() == maxDepth + 1);
}
return path;
}
// ********************************************************************************
// Test
// ********************************************************************************
void
TestFindTheLongestOrderedSeq()
{
std::vector<double> input = { 1.0, 2.0, 3.0, 4.0, 5.0 };
TreeNode<double> *root = NULL;
// 3
// 1 4
// 2 5
root = ConstructTreeOnSortedValueBFS(input);
auto path = FindTheLongestOrderedSeq(root);
TestResult<double>(path, { 1.0, 3.0, 4.0, 5.0 });
input = { 6.0, 7.0, 8.0, 9.0 };
TreeNode<double> *subTree1 = ConstructTreeOnSortedValueDFS(input);
input = { 10.0, 11.0, 12.0, 13.0 , 14.0 };
TreeNode<double> *subTree2 = ConstructTreeRecursive(input);
input = { 15.0, 16.0, 17.0, 18.0 };
TreeNode<double> *subTree3 = ConstructTreeOnSortedValueDFS(input);
input = { 19.0, 20.0, 21.0, 22.0 , 23.0 };
TreeNode<double> *subTree4 = ConstructTreeRecursive(input);
TreeNode<double> *leftestNode = FindLeftestNode(root);
assert(leftestNode != NULL);
leftestNode->left = subTree1;
// 3
// 1 4
// 8 2 5
// 6 9
// 7
path = FindTheLongestOrderedSeq(root);
TestResult<double>(path, { 1.0, 3.0, 4.0, 5.0 });
TreeNode<double> *rightestNode = FindRightestNode(root);
assert(rightestNode != NULL);
rightestNode->left = subTree2;
// 3
// 1 4
// 8 2 5
// 6 9 12
// 7 10 13
// 11 14
path = FindTheLongestOrderedSeq(root);
TestResult<double>(path, { 1.0, 3.0, 4.0, 5.0, 12.0, 13.0, 14.0 });
TreeNode<double> *firstLeftRighestNode = FindRightestNode(root->left);
assert(firstLeftRighestNode != NULL);
firstLeftRighestNode->right = subTree3;
// 3
// 1 4
// 8 2 5
// 6 9 17 12
// 7 15 18 10 13
// 16 11 14
path = FindTheLongestOrderedSeq(root);
TestResult<double>(path, { 1.0, 3.0, 4.0, 5.0, 12.0, 13.0, 14.0 });
TreeNode<double> *firstRightLeftestNodeOfS2 = FindRightestNode(subTree2->left);
assert(firstRightLeftestNodeOfS2 != NULL);
firstRightLeftestNodeOfS2->left = subTree4;
// 3
// 1 4
// 8 2 5
// 6 9 17 12
// 7 15 18 10 13
// 16 11 14
// 21
// 19 22
// 20 23
path = FindTheLongestOrderedSeq(root);
TestResult<double>(path, { 1.0, 3.0, 4.0, 5.0, 12.0, 13.0, 14.0 });
TreeNode<double> *rightestNodeOfS2 = FindRightestNode(subTree2);
assert(rightestNodeOfS2 != NULL);
for (size_t i = 0; i < 8; ++i) {
rightestNodeOfS2->right = new TreeNode<double>(100.0 + i);
rightestNodeOfS2 = rightestNodeOfS2->right;
}
// 3
// 1 4
// 8 2 5
// 6 9 17 12
// 7 15 18 10 13
// 16 11 14
// 21 100
// 19 22 101
// 20 23 102
// 103
// 104
// 105
// 106
// 107
// clean up
path = FindTheLongestOrderedSeq(root);
TestResult<double>(path, { 1.0, 3.0, 4.0, 5.0, 12.0, 13.0, 14.0, 100.0,
101.0, 102.0, 103.0, 104.0, 105.0, 106.0, 107.0});
// clean up
DeleteTree_TD(&root);
}