Second Minimum Node In a Binary Tree

🏠 ⬅️ ➡️

Given a non-empty special binary tree consisting of nodes with the non-negative value, where each node in this tree has exactly two or zero sub-node. If the node has two sub-nodes, then this node's value is the smaller value among its two sub-nodes. More formally, the property root.val = min(root.left.val, root.right.val) always holds.

Given such a binary tree, you need to output the second minimum value in the set made of all the nodes' value in the whole tree.

If no such second minimum value exists, output -1 instead.

Example 1:

Input: root = [2,2,5,null,null,5,7] Output: 5 Explanation: The smallest value is 2, the second smallest value is 5.

Example 2:

Input: root = [2,2,2] Output: -1 Explanation: The smallest value is 2, but there isn't any second smallest value.

Constraints:

  • The number of nodes in the tree is in the range [1, 25].
  • 1 <= Node.val <= 231 - 1
  • root.val == min(root.left.val, root.right.val) for each internal node of the tree.

Note: This problem is from LeetCode.
Compiled
Executed
Correct
program main
    use, intrinsic :: iso_fortran_env, only : error_unit, DP => REAL64
    implicit none
    integer, parameter :: unit_in = 10, unit_out = 20
    character(len=*), parameter :: filename_in = "example_input.txt", &
                                   filename_out = "example_output.txt"
    type :: node
        real(kind=DP) :: val
        class(*), pointer :: left => null(), right => null()
    end type
    type(node), pointer :: root
    integer :: i, n
    logical :: file_exists

    ! Read input from file
    inquire(file=filename_in, exist=file_exists)
    if (file_exists) then
        open(newunit=unit_in, file=filename_in, status="old", action="read")
        read(unit=unit_in, fmt=*) n
        allocate(root)
        do i = 1, n
            call read_node(unit_in, root)
        end do
        close(unit=unit_in)
    else
        write(unit=error_unit, fmt="('error: ',A)") "file not found: '" // trim(filename_in) // "'."
        stop 1
    end if

    ! Print output to file
    open(newunit=unit_out, file=filename_out, status="replace", action="write")
    call print_second_min(root, unit=unit_out)
    close(unit=unit_out)

contains

    subroutine read_node(unit, node)
        implicit none
        integer, intent(in) :: unit
        class(*), pointer, intent(out) :: node
        integer :: i
        character(len=100) :: line

        read(unit=unit, fmt=*) line
        if (line == "null") then
            nullify(node)
        else
            allocate(node)
            read(unit=unit, fmt=*) node%val
            read(unit=unit, fmt=*) i
            if (i > 0) then
                allocate(node%left)
                call read_node(unit, node%left)
            end if
            read(unit=unit, fmt=*) i
            if (i > 0) then
                allocate(node%right)
                call read_node(unit, node%right)
            end if
        end if
    end subroutine

    subroutine print_second_min(node, unit)
        implicit none
        class(*), pointer, intent(in) :: node
        integer, intent(in) :: unit
        integer :: i

        if (.not. associated(node)) then
            write(unit=unit, fmt="(-1)")
        else
            if (associated(node%left) .and. associated(node%right)) then
                call print_second_min(node%left, unit=unit)
                call print_second_min(node%right, unit=unit)
            else if (associated(node%left)) then
                call print_second_min(node%left, unit=unit)
            else if (associated(node%right)) then
                call print_second_min(node%right, unit=unit)
            else
                write(unit=unit, fmt=*) node%val
            end if
        end if
    end subroutine

end program
Compiled
Executed
Correct
module second_minimum_node
    implicit none
    private
    public :: second_minimum

contains

    function second_minimum(root) result(second_min)
        type(node), pointer, intent(in) :: root
        integer :: second_min

        ! Initialize variables
        second_min = -1

        ! Base case: if the root is null, return -1
        if (.not. associated(root)) then
            return
        end if

        ! If the root has a left child, recurse on the left child
        if (associated(root%left)) then
            second_min = second_minimum(root%left)
        end if

        ! If the root has a right child, recurse on the right child
        if (associated(root%right)) then
            if (second_min == -1) then
                second_min = second_minimum(root%right)
            else
                second_min = min(second_min, second_minimum(root%right))
            end if
        end if

        ! If the root is the smallest value, return the second minimum value
        if (root%val == min(root%left%val, root%right%val)) then
            second_min = root%val
        end if

    end function second_minimum

    type :: node
        integer :: val
        type(node), pointer :: left
        type(node), pointer :: right
    end type node
end module second_minimum_node

program test_second_minimum
    use second_minimum_node
    implicit none

    ! Test case 1
    type(node), pointer :: root
    root => node(2, null(), null())
    root%left => node(2, null(), null())
    root%right => node(5, null(), null())
    root%left%left => node(5, null(), null())
    root%left%right => node(7, null(), null())
    write (*,*) second_minimum(root)

    ! Test case 2
    root => node(2, null(), null())
    root%left => node(2, null(), null())
    root%right => node(2, null(), null())
    root%left%left => node(2, null(), null())
    root%left%right => node(2, null(), null())
    root%right%left => node(2, null(), null())
    root%right%right => node(2, null(), null())
    write (*,*) second_minimum(root)

    ! Test case 3
    root => node(2, null(), null())
    root%left => node(2, null(), null())
    root%right => node(2, null(), null())
    root%left%left => node(2, null(), null())
    root%left%right => node(2, null(), null())
    root%right%left => node(2, null(), null())
    root%right%right => node(2, null(), null())
    write (*,*) second_minimum(root)

    ! Test case 4
    root => node(2, null(), null())
    root%left => node(2, null(), null())
    root%right => node(2, null(), null())
    root%left%left => node(2, null(), null())
    root%left%right => node(2, null(), null())
    root%right%left => node(2, null(), null())
    root%right%right => node(2, null(), null())
    write (*,*) second_minimum(root)

    ! Test case 5
    root => node(2, null(), null())
    root%left => node(2, null(), null())
    root%right => node(2, null(), null())
    root%left%left => node(2, null(), null())
    root%left%right => node(2, null(), null())
    root%right%left => node(2, null(), null())
    root%right%right => node(2, null(), null())
    write (*,*) second_minimum(root)

    ! Test case 6
    root => node(2, null(), null())
    root%left => node(2, null(), null())
    root%right => node(2
🌐 Data from online sources
class TreeNode:
    def __init__(self, val=0, left=None, right=None):
        self.val = val
        self.left = left
        self.right = right

def find_second_minimum_value(root, current=-1):
    if root is None:
        return current
    if current == -1 or root.val < current:
        current = root.val
    if root.left is not None and root.right is not None:
        if root.val == root.left.val:
            current = find_second_minimum_value(root.left, current)
            if root.val != root.right.val:
                current = root.right.val if current == -1 else min(current, root.right.val)
        else:
            current = find_second_minimum_value(root.right, current)
            if root.val != root.left.val:
                current = root.left.val if current == -1 else min(current, root.left.val)
    return current

The algorithm starts at the root of the tree and recursively searches for the second smallest value in the tree. It maintains a variable current that stores the current smallest or second smallest value found so far, initialized to -1.

The base case is when the tree is empty, in which case the algorithm returns the value of current. When the value of the root is smaller than current, the value of the root is assigned to current.

If the root has left and right child, the algorithm checks if the root's value is equal to its left child or its right child, and then compares the current value, if any, with the root's children value. Then, the function is called recursively to explore their sub-trees to find the second minimum value. Eventually, it will return -1 if there is no such second minimum value, or it will return the second minimum value found in the tree.

🌐 Data from online sources
#include <algorithm>

struct TreeNode {
    int val;
    TreeNode* left;
    TreeNode* right;
};

int findSecondMinimumValue(TreeNode* root, int current = -1) {
    if (!root) return current;
    if (current == -1 || root->val < current) current = root->val;
    if (root->left && root->right) {
        if (root->val == root->left->val) {
            current = findSecondMinimumValue(root->left, current);
            if (root->val != root->right->val)
                current = (current == -1) ? root->right->val : std::min(current, root->right->val);
        } else {
            current = findSecondMinimumValue(root->right, current);
            if (root->val != root->left->val)
                current = (current == -1) ? root->left->val : std::min(current, root->left->val);
        }
    }
    return current;
}

The algorithm starts at the root of the tree and recursively searches for the second smallest value in the tree. It maintains a variable current that stores the current smallest or second smallest value found so far, initialized to -1.

The base case is when the tree is empty, in which case the algorithm returns the value of current. When the value of the root is smaller than current, the value of the root is assigned to current.

If the root has left and right child, the algorithm checks if the root's value is equal to its left child or its right child, and then compares the current value, if any, with the root's children value. Then, the function is called recursively to explore their sub-trees to find the second minimum value. Eventually, it will return -1 if there is no such second minimum value, or it will return the second minimum value found in the tree.