230_二叉搜索树中第K小的元素

230_二叉搜索树中第K小的元素

package 二叉树.二叉搜索树;

import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Deque;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * https://leetcode-cn.com/problems/kth-smallest-element-in-a-bst/
 * 
 * @author Huangyujun
 *
 */
public class _230_二叉搜索树中第K小的元素 {
    // 有bug:原因是最小不一定只是左子树的左,因为k 的原因可能有机会出现在右子树的左呀
//    public int kthSmallest(TreeNode root, int k) {
//        small(root);
//        return list.get(list.size()  - k);    
//    }
//    List<Integer> list = new ArrayList<>();
//    public int small(TreeNode root) {
//        if(root == null)    return 0;
//        list.add(small(root.left));
//        return root.val;
//    }
    //方法一:中序遍历(实现升序)
    public int kthSmallest(TreeNode root, int k) {
        inorder(root);
        return list.get(k - 1);
    }

    List<Integer> list = new ArrayList<>();

    public void inorder(TreeNode root) {
        if(root == null)    return;
        inorder(root.left);
        //拿到当前结点
        list.add(root.val);
        inorder(root.right);
    }
    //就是利用中序遍历(特点是二叉搜索树的中序遍历是从小到大啦,利用其特点,遍历到第k 次就能拿到 第k小)
    //因为升序的特点,应该在中序的过程(不断的拿最小、第二小的过程,到第k 小的时候就刹车了)
    //递归(二叉树的中序遍历递归):
    class Solution {
        private int count = 0;
        private int ans = -1;

        public void dfs(TreeNode root, int k){
            if (null == root || ans != -1) return;
            dfs(root.left, k);
            ++count;
            //当是第k小时拿到值,然后判断调整判断条件( ans != -1 ),退出循环(递归 )
            if (count == k) ans = root.val;
            dfs(root.right, k);
        }

        public int kthSmallest(TreeNode root, int k) {
            dfs(root, k);
            return ans;
        }
    }
    //非递归(二叉树的中序遍历递归):
    class Solution2 {
        public int kthSmallest(TreeNode root, int k) {
            Deque<TreeNode> stack = new ArrayDeque<TreeNode>();
            while (root != null || !stack.isEmpty()) {
                while (root != null) {
                    stack.push(root);
                    root = root.left;
                }
                root = stack.pop();
                --k;
                if (k == 0) {
                    break;
                }
                root = root.right;
            }
            return root.val;
        }
    }
    
    // (本思路算法时间很不优秀)通过 map 存储了每个结点下对应的所有结点数量(就是因为这个步骤,导致算法时间不优秀了):
    //思路可以看看:
    /**
 * 对当前结点 node 进行如下操作:

如果 node 的左子树的结点数  left 小于 k-1k−1,则第 kk 小的元素一定在node 的右子树中,令 node 等于其的右子结点,k 等于 k−left−1,并继续搜索;
如果 node 的左子树的结点数 left 等于 k−1,则第 k 小的元素即为 node ,结束搜索并返回 node 即可;
如果 node 的左子树的结点数 left 大于 k−1,则第 k 小的元素一定在 node 的左子树中,令 node 等于其左子结点,并继续搜索。
     * @author Huangyujun
     *
     */
    class Solution3 {
        public int kthSmallest(TreeNode root, int k) {
            MyBst bst = new MyBst(root);
            return bst.kthSmallest(k);
        }
        
        class MyBst {
            TreeNode root;
            //当前根结点,对应的结点数量其所有结点的数量
            Map<TreeNode, Integer> nodeNum;

            public MyBst(TreeNode root) {
                this.root = root;
                this.nodeNum = new HashMap<TreeNode, Integer>();
                countNodeNum(root);
            }

            // 返回二叉搜索树中第k小的元素
            public int kthSmallest(int k) {
                TreeNode node = root;
                while (node != null) {
                    int left = getNodeNum(node.left);
                    if (left < k - 1) {
                        node = node.right;
                        k -= left + 1;
                    } else if (left == k - 1) {
                        break;
                    } else {
                        node = node.left;
                    }
                }
                return node.val;
            }

            // 统计以node为根结点的子树的结点数
            private int countNodeNum(TreeNode node) {
                if (node == null) {
                    return 0;
                }
                nodeNum.put(node, 1 + countNodeNum(node.left) + countNodeNum(node.right));
                return nodeNum.get(node);
            }

            // 获取以node为根结点的子树的结点数(getOrDefault(node, 0); 这个方法是当非空是返回原本的值,否则返回第二个参数(默认值))
            private int getNodeNum(TreeNode node) {
                return nodeNum.getOrDefault(node, 0);
            }
        }
    }

    
}

本文来自博客园,作者:一乐乐,转载请注明原文链接:https://www.cnblogs.com/shan333/p/15709277.html

原文地址:https://www.cnblogs.com/shan333/p/15709277.html