Recall that a full binary tree is one in which each node is either a leaf node, or has two children. Given a binary tree, convert it to a full one by removing nodes with only one child.
Input: Tree as shown above
0
/ \
1 2
/ \
3 4
\ / \
5 6 7
Output:
0
/ \
5 4
/ \
6 7
Explanation: Nodes 1, 2, and 3 have only one child each, so they are removed.
The key insight is to use post-order traversal (process children before parent) to ensure we handle the deepest nodes first. When we encounter a node with only one child, we replace it with its single child and continue the process.
The algorithm works as follows:
Recursively process left and right subtrees first
If current node has only one child, replace it with that child
If current node has two children or is a leaf, keep it as is
#include<iostream>#include<queue>#include<vector>structTreeNode {
int val;
TreeNode* left;
TreeNode* right;
TreeNode() : val(0), left(nullptr), right(nullptr) {}
TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
TreeNode(int x, TreeNode* left, TreeNode* right) : val(x), left(left), right(right) {}
};
classSolution {
public: TreeNode* convertToFullBinaryTree(TreeNode* root) {
if (!root) returnnullptr;
// Process left and right subtrees first (post-order)
root->left = convertToFullBinaryTree(root->left);
root->right = convertToFullBinaryTree(root->right);
// If current node has only one child, replace it with that child
if (!root->left && root->right) {
// Only right child exists
return root->right;
} elseif (root->left &&!root->right) {
// Only left child exists
return root->left;
}
// Node has either two children or no children (leaf)
return root;
}
// Helper function to print tree level by level
voidprintLevelOrder(TreeNode* root) {
if (!root) {
std::cout <<"Empty tree"<< std::endl;
return;
}
std::queue<TreeNode*> q;
q.push(root);
while (!q.empty()) {
int levelSize = q.size();
for (int i =0; i < levelSize; i++) {
TreeNode* node = q.front();
q.pop();
if (node) {
std::cout << node->val <<" ";
q.push(node->left);
q.push(node->right);
} else {
std::cout <<"null ";
}
}
std::cout << std::endl;
}
}
// Helper function to create the example tree
TreeNode*createExampleTree() {
TreeNode* root =new TreeNode(0);
root->left =new TreeNode(1);
root->right =new TreeNode(2);
root->left->left =new TreeNode(3);
root->right->right =new TreeNode(4);
root->left->left->right =new TreeNode(5);
root->right->right->left =new TreeNode(6);
root->right->right->right =new TreeNode(7);
return root;
}
};
packagemainimport (
"fmt")
typeTreeNodestruct {
ValintLeft*TreeNodeRight*TreeNode}
funcconvertToFullBinaryTree(root*TreeNode) *TreeNode {
ifroot==nil {
returnnil }
// Process left and right subtrees first (post-order)root.Left = convertToFullBinaryTree(root.Left)
root.Right = convertToFullBinaryTree(root.Right)
// If current node has only one child, replace it with that childifroot.Left==nil&&root.Right!=nil {
// Only right child existsreturnroot.Right } elseifroot.Left!=nil&&root.Right==nil {
// Only left child existsreturnroot.Left }
// Node has either two children or no children (leaf)returnroot}
funcprintInOrder(root*TreeNode) {
ifroot==nil {
return }
printInOrder(root.Left)
fmt.Printf("%d ", root.Val)
printInOrder(root.Right)
}
funccreateExampleTree() *TreeNode {
root:=&TreeNode{Val: 0}
root.Left = &TreeNode{Val: 1}
root.Right = &TreeNode{Val: 2}
root.Left.Left = &TreeNode{Val: 3}
root.Right.Right = &TreeNode{Val: 4}
root.Left.Left.Right = &TreeNode{Val: 5}
root.Right.Right.Left = &TreeNode{Val: 6}
root.Right.Right.Right = &TreeNode{Val: 7}
returnroot}
funcmain() {
root:=createExampleTree()
fmt.Print("Original tree (in-order): ")
printInOrder(root)
fmt.Println()
fullTree:=convertToFullBinaryTree(root)
fmt.Print("Full binary tree (in-order): ")
printInOrder(fullTree)
fmt.Println()
}
classTreeNode(var `val`: Int) {
var left: TreeNode? = nullvar right: TreeNode? = null}
classSolution {
funconvertToFullBinaryTree(root: TreeNode?): TreeNode? {
if (root ==null) returnnull// Process left and right subtrees first (post-order)
root.left = convertToFullBinaryTree(root.left)
root.right = convertToFullBinaryTree(root.right)
// If current node has only one child, replace it with that child
returnwhen {
root.left ==null&& root.right !=null-> {
// Only right child exists
root.right
}
root.left !=null&& root.right ==null-> {
// Only left child exists
root.left
}
else-> {
// Node has either two children or no children (leaf)
root
}
}
}
funprintInOrder(root: TreeNode?) {
if (root ==null) return printInOrder(root.left)
print("${root.`val`} ")
printInOrder(root.right)
}
funcreateExampleTree(): TreeNode {
val root = TreeNode(0)
root.left = TreeNode(1)
root.right = TreeNode(2)
root.left!!.left = TreeNode(3)
root.right!!.right = TreeNode(4)
root.left!!.left!!.right = TreeNode(5)
root.right!!.right!!.left = TreeNode(6)
root.right!!.right!!.right = TreeNode(7)
return root
}
}
funmain() {
val sol = Solution()
val root = sol.createExampleTree()
print("Original tree (in-order): ")
sol.printInOrder(root)
println()
val fullTree = sol.convertToFullBinaryTree(root)
print("Full binary tree (in-order): ")
sol.printInOrder(fullTree)
println()
}
from typing import Optional
from collections import deque
classTreeNode:
def__init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
classSolution:
defconvert_to_full_binary_tree(self, root: Optional[TreeNode]) -> Optional[TreeNode]:
"""
Convert binary tree to full binary tree by removing nodes with only one child.
Args:
root: Root of the binary tree
Returns:
Root of the converted full binary tree
"""ifnot root:
returnNone# Process left and right subtrees first (post-order) root.left = self.convert_to_full_binary_tree(root.left)
root.right = self.convert_to_full_binary_tree(root.right)
# If current node has only one child, replace it with that childifnot root.left and root.right:
# Only right child existsreturn root.right
elif root.left andnot root.right:
# Only left child existsreturn root.left
# Node has either two children or no children (leaf)return root
defprint_level_order(self, root: Optional[TreeNode]) ->None:
"""Print tree in level order for visualization."""ifnot root:
print("Empty tree")
return queue = deque([root])
while queue:
level_size = len(queue)
for _ in range(level_size):
node = queue.popleft()
if node:
print(node.val, end=" ")
queue.append(node.left)
queue.append(node.right)
else:
print("null", end=" ")
print()
defprint_in_order(self, root: Optional[TreeNode]) ->None:
"""Print tree in-order traversal."""ifnot root:
return self.print_in_order(root.left)
print(root.val, end=" ")
self.print_in_order(root.right)
defcreate_example_tree(self) -> TreeNode:
"""Create the example tree from the problem.""" root = TreeNode(0)
root.left = TreeNode(1)
root.right = TreeNode(2)
root.left.left = TreeNode(3)
root.right.right = TreeNode(4)
root.left.left.right = TreeNode(5)
root.right.right.left = TreeNode(6)
root.right.right.right = TreeNode(7)
return root
deftree_to_list(self, root: Optional[TreeNode]) -> list:
"""Convert tree to list representation for easy comparison."""ifnot root:
return []
result = []
queue = deque([root])
while queue:
node = queue.popleft()
if node:
result.append(node.val)
queue.append(node.left)
queue.append(node.right)
else:
result.append(None)
# Remove trailing None valueswhile result and result[-1] isNone:
result.pop()
return result
deftest_convert_to_full_binary_tree():
"""Test the solution with various test cases.""" sol = Solution()
# Test case 1: Original example print("Test Case 1: Original Example")
root1 = sol.create_example_tree()
print("Original tree (in-order): ", end="")
sol.print_in_order(root1)
print()
full_tree1 = sol.convert_to_full_binary_tree(root1)
print("Full binary tree (in-order): ", end="")
sol.print_in_order(full_tree1)
print("\n")
# Test case 2: Chain of single children print("Test Case 2: Chain Tree")
root2 = TreeNode(1)
root2.left = TreeNode(2)
root2.left.right = TreeNode(3)
print("Original chain (in-order): ", end="")
sol.print_in_order(root2)
print()
full_tree2 = sol.convert_to_full_binary_tree(root2)
print("Result (in-order): ", end="")
sol.print_in_order(full_tree2)
print("\n")
# Test case 3: Already full binary tree print("Test Case 3: Already Full Tree")
root3 = TreeNode(1)
root3.left = TreeNode(2)
root3.right = TreeNode(3)
root3.left.left = TreeNode(4)
root3.left.right = TreeNode(5)
print("Original tree (in-order): ", end="")
sol.print_in_order(root3)
print()
full_tree3 = sol.convert_to_full_binary_tree(root3)
print("Result (in-order): ", end="")
sol.print_in_order(full_tree3)
print("\n")
# Test case 4: Single node print("Test Case 4: Single Node")
root4 = TreeNode(42)
print("Original tree (in-order): ", end="")
sol.print_in_order(root4)
print()
full_tree4 = sol.convert_to_full_binary_tree(root4)
print("Result (in-order): ", end="")
sol.print_in_order(full_tree4)
print("\n")
# Test case 5: Empty tree print("Test Case 5: Empty Tree")
root5 =None full_tree5 = sol.convert_to_full_binary_tree(root5)
print(f"Result: {full_tree5}")
if __name__ =="__main__":
test_convert_to_full_binary_tree()
use std::rc::Rc;
use std::cell::RefCell;
use std::collections::VecDeque;
#[derive(Debug, PartialEq, Eq)]pubstructTreeNode {
pub val: i32,
pub left: Option<Rc<RefCell<TreeNode>>>,
pub right: Option<Rc<RefCell<TreeNode>>>,
}
impl TreeNode {
#[inline]pubfnnew(val: i32) -> Self {
TreeNode {
val,
left: None,
right: None,
}
}
}
typeOptNode= Option<Rc<RefCell<TreeNode>>>;
fnconvert_to_full_binary_tree(root: OptNode) -> OptNode {
match root {
None => None,
Some(node) => {
letmut node_ref = node.borrow_mut();
// Process left and right subtrees first (post-order)
let left = convert_to_full_binary_tree(node_ref.left.take());
let right = convert_to_full_binary_tree(node_ref.right.take());
node_ref.left = left.clone();
node_ref.right = right.clone();
// If current node has only one child, replace it with that child
match (&left, &right) {
(None, Some(_)) => {
// Only right child exists
right
}
(Some(_), None) => {
// Only left child exists
left
}
_ => {
// Node has either two children or no children (leaf)
drop(node_ref);
Some(node)
}
}
}
}
}
fnprint_in_order(root: &OptNode) {
iflet Some(node) = root {
let node_ref = node.borrow();
print_in_order(&node_ref.left);
print!("{} ", node_ref.val);
print_in_order(&node_ref.right);
}
}
fncreate_example_tree() -> OptNode {
let root = Rc::new(RefCell::new(TreeNode::new(0)));
let node1 = Rc::new(RefCell::new(TreeNode::new(1)));
let node2 = Rc::new(RefCell::new(TreeNode::new(2)));
let node3 = Rc::new(RefCell::new(TreeNode::new(3)));
let node4 = Rc::new(RefCell::new(TreeNode::new(4)));
let node5 = Rc::new(RefCell::new(TreeNode::new(5)));
let node6 = Rc::new(RefCell::new(TreeNode::new(6)));
let node7 = Rc::new(RefCell::new(TreeNode::new(7)));
root.borrow_mut().left = Some(node1.clone());
root.borrow_mut().right = Some(node2.clone());
node1.borrow_mut().left = Some(node3.clone());
node2.borrow_mut().right = Some(node4.clone());
node3.borrow_mut().right = Some(node5);
node4.borrow_mut().left = Some(node6);
node4.borrow_mut().right = Some(node7);
Some(root)
}
fnmain() {
println!("Test Case 1: Original Example");
let root = create_example_tree();
print!("Original tree (in-order): ");
print_in_order(&root);
println!();
let full_tree = convert_to_full_binary_tree(root);
print!("Full binary tree (in-order): ");
print_in_order(&full_tree);
println!();
// Test case 2: Chain of single children
println!("\nTest Case 2: Chain Tree");
let root2 = Some(Rc::new(RefCell::new(TreeNode::new(1))));
iflet Some(ref node) = root2 {
let node2 = Rc::new(RefCell::new(TreeNode::new(2)));
let node3 = Rc::new(RefCell::new(TreeNode::new(3)));
node.borrow_mut().left = Some(node2.clone());
node2.borrow_mut().right = Some(node3);
}
print!("Original chain (in-order): ");
print_in_order(&root2);
println!();
let full_tree2 = convert_to_full_binary_tree(root2);
print!("Result (in-order): ");
print_in_order(&full_tree2);
println!();
}
classTreeNode {
val: number;
left: TreeNode|null;
right: TreeNode|null;
constructor(val?: number, left?: TreeNode|null, right?: TreeNode|null) {
this.val=val??0;
this.left=left??null;
this.right=right??null;
}
}
functionconvertToFullBinaryTree(root: TreeNode|null):TreeNode|null {
if (!root) returnnull;
// Process left and right subtrees first (post-order)
root.left=convertToFullBinaryTree(root.left);
root.right=convertToFullBinaryTree(root.right);
// If current node has only one child, replace it with that child
if (!root.left&&root.right) {
// Only right child exists
returnroot.right;
} elseif (root.left&&!root.right) {
// Only left child exists
returnroot.left;
}
// Node has either two children or no children (leaf)
returnroot;
}
functionprintInOrder(root: TreeNode|null):void {
if (!root) return;
printInOrder(root.left);
process.stdout.write(`${root.val} `);
printInOrder(root.right);
}
functionprintLevelOrder(root: TreeNode|null):void {
if (!root) {
console.log("Empty tree");
return;
}
constqueue: (TreeNode|null)[] = [root];
while (queue.length>0) {
constlevelSize=queue.length;
for (leti=0; i<levelSize; i++) {
constnode=queue.shift();
if (node) {
process.stdout.write(`${node.val} `);
queue.push(node.left);
queue.push(node.right);
} else {
process.stdout.write("null ");
}
}
console.log();
}
}
functioncreateExampleTree():TreeNode {
constroot=newTreeNode(0);
root.left=newTreeNode(1);
root.right=newTreeNode(2);
root.left.left=newTreeNode(3);
root.right.right=newTreeNode(4);
root.left.left.right=newTreeNode(5);
root.right.right.left=newTreeNode(6);
root.right.right.right=newTreeNode(7);
returnroot;
}
// Test cases
functiontestConvertToFullBinaryTree():void {
console.log("Test Case 1: Original Example");
constroot1=createExampleTree();
process.stdout.write("Original tree (in-order): ");
printInOrder(root1);
console.log();
constfullTree1=convertToFullBinaryTree(root1);
process.stdout.write("Full binary tree (in-order): ");
printInOrder(fullTree1);
console.log("\n");
// Test case 2: Chain of single children
console.log("Test Case 2: Chain Tree");
constroot2=newTreeNode(1);
root2.left=newTreeNode(2);
root2.left.right=newTreeNode(3);
process.stdout.write("Original chain (in-order): ");
printInOrder(root2);
console.log();
constfullTree2=convertToFullBinaryTree(root2);
process.stdout.write("Result (in-order): ");
printInOrder(fullTree2);
console.log("\n");
// Test case 3: Single node
console.log("Test Case 3: Single Node");
constroot3=newTreeNode(42);
process.stdout.write("Original tree (in-order): ");
printInOrder(root3);
console.log();
constfullTree3=convertToFullBinaryTree(root3);
process.stdout.write("Result (in-order): ");
printInOrder(fullTree3);
console.log();
}
// Run tests
testConvertToFullBinaryTree();
🧺 Space: O(h) - recursion stack depth equals tree height
Properties:
Best case: O(log n) space for balanced tree
Worst case: O(n) space for skewed tree
Memory efficient: Only uses recursion stack, no additional data structures
The recursive post-order approach is optimal for this problem, providing both simplicity and efficiency while naturally handling the tree transformation requirements.