-2

I was faced with below interview question today and I came up with recursive and iterative solution as mentioned below but somehow interviewer was not happy. I am not sure why.

Given a binary search tree, find the kth smallest element in it.

Is there any better way to do this problem either in recursive or iterative way?

  /*****************************************************
   *
   * Kth Smallest Recursive
   *
   ******************************************************/
  public int kthSmallestRecursive(TreeNode root, int k) {
    int count = countNodes(root.left);
    if (k <= count) {
      return kthSmallestRecursive(root.left, k);
    } else if (k > count + 1) {
      return kthSmallestRecursive(root.right, k - 1 - count);
    }

    return root.data;
  }

  public int countNodes(TreeNode n) {
    if (n == null)
      return 0;

    return 1 + countNodes(n.left) + countNodes(n.right);
  }

  /*****************************************************
   *
   * Kth Smallest Iterative
   *
   ******************************************************/

  public int kthSmallestIterative(TreeNode root, int k) {
    Stack<TreeNode> st = new Stack<>();

    while (root != null) {
      st.push(root);
      root = root.left;
    }

    while (k != 0) {
      TreeNode n = st.pop();
      k--;
      if (k == 0)
        return n.data;
      TreeNode right = n.right;
      while (right != null) {
        st.push(right);
        right = right.left;
      }
    }

    return -1;
  }

I mentioned complexity on both the above solution as O(depth of node) which is O(log n).

My iterative version takes extra space. Is there any way to do it without any extra space?

john
  • 11,311
  • 40
  • 131
  • 251
  • You certainly can't do better than log n. Did they mention anything specific? – Mitch Nov 29 '18 at 00:32
  • HOw do you know if he is not happy? – SomeDude Nov 29 '18 at 00:33
  • 1
    How long will just one call of `countNodes` take? How long does it take to count all the nodes in a tree (or half the nodes in a tree)? It's not O(log n). Also, you're unnecessarily looking at the same nodes multiple times - you count them, then you recurse, then you count them again, then you recurse, etc. – Bernhard Barker Nov 29 '18 at 00:33
  • 2
    all you need is kth element in the inorder traversal. Your iterative version seems to be doing just that. The recursive version is not efficient. – SomeDude Nov 29 '18 at 00:36
  • @Dukeling so my iterative version is fine only recursive one is having issues right? Because I am checking same nodes multiple times Also can you help me understand why it's not O(log n). – john Nov 29 '18 at 00:39
  • @SomeDude can you tell me why recursive one is not efficient? It is what Dukeling said? If yes then can you tell me how we can optimize it? – john Nov 29 '18 at 00:40
  • https://en.wikipedia.org/wiki/Tree_traversal#In-order – user3386109 Nov 29 '18 at 00:44
  • Your iterative solution is O(n), and your recursive solution is O(n^2). If the tree is guaranteed to be balanced, then you can have O(k + log n) and O(n log n). O(log n) is not possible without balancing and modifications to the data structure. In an interview situation, `countNodes` is the real problem -- people who might write code with quadratic complexity by mistake need to have their work checked when it is performance-sensitive or when the inputs might end up being a lot larger than the ones you test with. – Matt Timmermans Nov 29 '18 at 00:48
  • Counting nodes itself takes O(n) unless your tree is perfectly balanced. If the tree is perfectly balanced. You could get height in O(h) time and compute number of nodes as 2^h - 1. – SomeDude Nov 29 '18 at 01:18
  • Is this something that you're planning on doing multiple times? If so, do a quick Google search for "order statistics tree," which is a modified version of a BST that's optimized to be able to answer queries like this really quickly. – templatetypedef Nov 29 '18 at 01:21
  • @MattTimmermans Can you tell me why recursive is `O(n^2)`? I am kinda confuse there.. Is there any way to optimize recursive solution here? – john Nov 29 '18 at 01:27
  • Also my iterative version takes extra space. Is there any way to do it without any extra space? – john Nov 29 '18 at 01:50
  • Similar question has been asked before: https://stackoverflow.com/questions/2329171/find-kth-smallest-element-in-a-binary-search-tree-in-optimum-way – Vineeth Chitteti Nov 29 '18 at 10:05
  • `my iterative version takes extra space. Is there any way to do it without any extra space?`see [comments to an answer to your (partial) re-post on CR](https://codereview.stackexchange.com/questions/208668/find-kth-smallest-element-in-binary-search-tree#comment403161_208673) – greybeard Nov 29 '18 at 20:48

2 Answers2

1

First, I doubt if it's going to take O(logn). It's at least O(n) for vanilla binary search tree.

For vanilla bst you can do iterative or recursive, both are simulation the same process of ignorer traversal with worst time complexity O(n) and space complexity O(logn) (note that even the recursive solution can take O(logn) space from stack).

However you can speed things up or use less space if you could change the data structure a bit:

  1. If you are using a binary search tree with each node recording the number of total nodes under it, you can do it as if it is a sorted array and get to the k-th element with O(logn) time and O(1) space.

  2. If you are using a binary search tree with each node containing the parent pointer and a status indicator you can traverse the tree in the same fashion as the vanilla solution. On your way you mark whether the node is a. not traversed 2. has its left node traversed 3. has both nodes traversed. then you can do it in O(n) time and O(1) space.

Kevin He
  • 1,210
  • 8
  • 19
  • what about 3rd option where we cannot change data structure and need to find as it is then what will be the complexity for recursive and iterative in an optimal way? – john Nov 29 '18 at 02:07
  • https://stackoverflow.com/questions/14426790/why-lookup-in-a-binary-search-tree-is-ologn – K.Nicholas Nov 29 '18 at 02:07
  • Hi john. It’s mentioned above the two options: for vanilla bst it’s the best we can do. – Kevin He Nov 29 '18 at 02:50
  • Hi Nicholas, it’s for lookup a certain element, not the k-th largest or smallest element. – Kevin He Nov 29 '18 at 02:50
0
 public int printInorder(Node node, int k) 
    { 
        if (node == null || k <= 0) //Stop traversing once you found the k-th smallest element
            return k; 

        /* first recur on left child */
        k = printInorder(node.left, k); 

        k--;
        if(k == 0) {  
            System.out.print(node.key);
        }

        /* now recur on right child */
        return printInorder(node.right, k);
    } 

In countNodes() method of your recursive approach, you're traversing the Tree at each and every node for counting. countNodes() operation is O(n) and you're performing the operation on log(n) number of nodes. So the order of complexity of your recursive solution is O(n^2) whereas the approach I mentioned above solves in O(n).

Vineeth Chitteti
  • 1,454
  • 2
  • 14
  • 30
  • But as of now my recursive solution as o(n^2) time complexity right and O(1) space complexity? – john Nov 29 '18 at 20:23
  • @john Yes. Sorry for the mistake. Your solution would take O(n2) in the worst case and O(1) for space. I stand corrected. – Vineeth Chitteti Nov 29 '18 at 21:08
  • @john: `my recursive solution [has …] O(1) space complexity?` only if you ignore the O(n) calling stack keeping nodes to be revisited if need be. – greybeard Nov 29 '18 at 21:19