zoukankan      html  css  js  c++  java
  • 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

  • 相关阅读:
    各种平衡树板子
    字符串板子
    数学公式/定理/板子整理
    线性筛 板子整理
    set乱搞时需注意的坑点
    可持久化数据结构板子整理(可持久化 线段树/字典树/可并堆)
    洛谷p2483 模板k短路 可持久化可并堆
    p4929 DLX舞蹈链
    百度ai 图像增强与特效
    百度ai php请求获取access_token返回false
  • 原文地址:https://www.cnblogs.com/shan333/p/15709277.html
Copyright © 2011-2022 走看看