SINGLE在二进制搜索树中打印/获取第k个最小元素的递归函数

时间:2017-10-14 17:20:10

标签: algorithm recursion data-structures binary-search-tree

我正在尝试在BST中打印第k个最小元素。 第一种解决方案是使用有序遍历。 下一个解决方案是通过计算其左子树的大小来查找当前节点的索引。 完整的算法:

Find size of left subtree:
   1.If size = k-1, return current node
   2.If size>k return (size-k)th node in right subtree
   3.If size<k return kth node in left subtree

这可以使用单独的计数功能实现,类似于

public class Solution {
    public int kthSmallest(TreeNode root, int k) {
        //what happens if root == null
        //what happens if k > total size of tree
        return kthSmallestNode(root,k).val;

    }

    public static TreeNode kthSmallestNode(TreeNode root,int k){
        if(root==null) return root;
        int numberOfNodes = countNodes(root.left);

        if(k == numberOfNodes ) return root;
        if(k<numberOfNodes ) return kthSmallestNode(root.left,k);
        else return kthSmallestNode(root.right,k-numberOfNodes );
    } 

    private static int countNodes(TreeNode node){
        if(node == null) return 0;
        else return 1+countNodes(node.left)+countNodes(node.right);
    }
}

但是我看到我们多次计算相同树的大小,所以一种方法是维护一个数组来存储像DP一样的大小。

但我想为此编写一个递归解决方案。这里是我编写的代码。

class Node {
    int data;
    Node left;
    Node right;

    public Node(int data, Node left, Node right) {
        this.left = left;
        this.data = data;
        this.right = right;
    }
}

public class KthInBST
{
    public static Node createBST(int headData)
    {
          Node head = new Node(headData, null, null);
          //System.out.println(head.data);
          return head;
    }

    public static void insertIntoBst(Node head, int data) 
    {

            Node newNode = new Node(data, null, null);
            while(true) {

            if (data > head.data) {
                if (head.right == null) {
                    head.right = newNode;
                    break;
                } else {
                    head = head.right;
                }
            } else {
                if (head.left == null) {
                    head.left = newNode;
                    break;
                } else {
                    head = head.left;
                }
            }
          }
    }

    public static void main(String[] args) 
    {
           Node head = createBST(5);
           insertIntoBst(head, 7);
           insertIntoBst(head, 6);

           insertIntoBst(head, 2);
           insertIntoBst(head, 1);
           insertIntoBst(head, 21);
           insertIntoBst(head, 11);
           insertIntoBst(head, 14);
           insertIntoBst(head, 3);


           printKthElement(head, 3);
    }

    public static int printKthElement(Node head, int k)
    {
         if (head == null) {
            return 0;
         }

         int leftIndex  = printKthElement(head.left, k);

         int index = leftIndex + 1;


         if (index == k) {
            System.out.println(head.data);
         } else if (k > index) {
            k = k - index;
            printKthElement(head.right, k);
         } else {
            printKthElement(head.left, k);
         }
         return index;
    }


}

这是打印正确的答案,但多次,我弄清楚为什么它多次打印但不了解如何避免它。 并且如果我想返回节点而不是仅仅打印我该怎么做? 有人可以帮我这个吗?

1 个答案:

答案 0 :(得分:0)

目标:

递归地在二叉搜索树中找到第k个最小元素,并返回与该元素对应的节点。

观察:

小于当前元素的元素数量是左子树的大小,所以我们不是递归地计算它的大小,而是在class Node中引入一个新成员,即lsize代表当前节点左子树的大小。

解决方案:

在每个节点,我们将左子树的大小与k的当前值进行比较:

  1. if head.lsize + 1 == k:我们答案中的当前节点。
  2. 如果head.lsize + 1 > k:左子树中的元素大于k,也就是说,最小元素的k位于左子树中。所以,我们走了。
  3. 如果head.lsize + 1 < k:当前元素连同左子树中的所有元素都小于我们需要找到的第k个元素。因此,我们转到右子树,但也减少k左子树+ 1(当前元素)中的元素数量。通过从k中减去它,我们确保已经考虑了小于k并且作为当前节点的左子树(包括当前节点本身)的元素的数量。
  4. 代码:

    class Node {
        int data;
        Node left;
        Node right;
        int lsize;
    
        public Node(int data, Node left, Node right) {
            this.left = left;
            this.data = data;
            this.right = right;
            lsize = 0;
        }
    }
    
    public static void insertIntoBst(Node head, int data) {
    
            Node newNode = new Node(data, null, null);
            while (true) {
    
                if (data > head.data) {
                    if (head.right == null) {
                        head.right = newNode;
                        break;
                    } else {
                        head = head.right;
                    }
                } else {
                    head.lsize++; //as we go left, size of left subtree rooted 
                                 //at current node will increase, hence the increment.
                    if (head.left == null) {
                        head.left = newNode;
                        break;
                    } else {
                        head = head.left;
                    }
                }
            }
        }
    
        public static Node printKthElement(Node head, int k) {
            if (head == null) {
                return null;
            }
    
            if (head.lsize + 1 == k) return head;
            else if (head.lsize + 1 > k) return printKthElement(head.left, k);
            return printKthElement(head.right, k - head.lsize - 1);
        }
    

    更改:

    1. lsize
    2. 中引入了新成员class Node
    3. insertIntoBst
    4. 中的轻微修改
    5. printKthElement
    6. 的重大变化

      转角案例:

      添加检查以确保k介于1和树的大小之间,否则将返回null节点,从而生成NullPointerException

      到目前为止,这是我试过的测试用例。任何建议或更正都是最受欢迎的。 :)