首页 > 其他分享 >力扣 230. 二叉搜索树中第K小的元素

力扣 230. 二叉搜索树中第K小的元素

时间:2022-12-07 23:23:34浏览次数:117  
标签:node right TreeNode 230 力扣 root 树中 节点 left

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

给定一个二叉搜索树的根节点 root ,和一个整数 k ,请你设计一个算法查找其中第 k 个最小元素(从 1 开始计数)。

示例 1:

输入:root = [3,1,4,null,2], k = 1
输出:1

示例 2:

输入:root = [5,3,6,2,4,null,null,1], k = 3
输出:3

提示:

  • 树中的节点数为 n 。
  • 1 <= k <= n <= 104
  • 0 <= Node.val <= 104

进阶:如果二叉搜索树经常被修改(插入/删除操作)并且你需要频繁地查找第 k 小的值,你将如何优化算法?

未进阶

不考虑进阶的问题,因为二叉搜索树的中序遍历得到的结果是升序的,可以通过递归或者迭代,中序遍历到第k个节点,然后返回。这里选择Morris中序遍历。

查看代码
 /**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
public:
    int kthSmallest(TreeNode* root, int k) {
        int res=0;
        int cnt=0;//计数
        while (root != NULL)
        {
            if (root->left == NULL)
            {
                // cout << root->val << " ";//输出当前节点
                cnt++;
                if(cnt==k)
                    res=root->val;
                root = root->right;
            }
            else
            {
                // 找当前节点的前趋结点
                TreeNode* predecessor = root->left;
                while (predecessor->right != NULL
                    && predecessor->right != root)
                {
                    predecessor = predecessor->right;
                }

                // 使当前节点成为inorder的前序节点的右侧子节点
                if (predecessor->right == NULL)
                {
                    predecessor->right = root;
                    root = root->left;
                }
                //复原之前的修改
                else
                {
                    predecessor->right = NULL;
                    // cout << root->val << " ";//输出当前节点
                    cnt++;
                    if(cnt==k)
                        res=root->val;
                    root = root->right;
                }
            }
        }
        return res;
    }
};

进阶

如果二叉搜索树经常被修改(插入/删除操作)并且你需要频繁地查找第 k 小的值,你将如何优化算法?

记录子树节点

既然频繁地查找第 k 小的值,那么就记录每个节点中序遍历前面有几个节点,记为pre_nums,在查找的时候,提高比较kpre_nums的值,省略一部分搜索,提高查找效率。

  • 如果k-1(因为在比较cur左节点)<cur左节点的pre_nums,则当前节点cur右子树就不需要去遍历了,k会在左子树中。
  • 如果k-1>cur左节点的pre_nums,则当前节点cur左子树就不需要去遍历了,k会在右子树中。
  • 如果k-1==cur左节点的pre_nums,则当前节点curpre_nums==k

可以将pre_nums存储在结点中,也可以将其记录在哈希表中。

代码来自官方

查看代码
class MyBst {
public:
    MyBst(TreeNode *root) {
        this->root = root;
        countNodeNum(root);//统计节点数
    }

    // 返回二叉搜索树中第k小的元素
    int kthSmallest(int k) {
        TreeNode *node = root;
        while (node != nullptr) {
            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;
    }

private:
    TreeNode *root;
    unordered_map<TreeNode *, int> nodeNum;//存储节点对应节点数

    // 递归统计以node为根结点的子树的结点数
    int countNodeNum(TreeNode * node) {
        if (node == nullptr) {
            return 0;
        }
        nodeNum[node] = 1 + countNodeNum(node->left) + countNodeNum(node->right);//本身(1)+左子树+右子树
        return nodeNum[node];
    }

    // 获取以node为根结点的子树的结点数
    int getNodeNum(TreeNode * node) {
        if (node != nullptr && nodeNum.count(node)) {
            return nodeNum[node];
        }else{
            return 0;
        }
    }
};

class Solution {
public:
    int kthSmallest(TreeNode* root, int k) {
        MyBst bst(root);
        return bst.kthSmallest(k);
    }
};

平衡二叉树

等后面做到平衡二叉树再来补

 

标签:node,right,TreeNode,230,力扣,root,树中,节点,left
From: https://www.cnblogs.com/fudanxi/p/16953852.html

相关文章

  • 力扣 leetcode 78. 子集
    问题描述给你一个整数数组nums,数组中的元素互不相同。返回该数组所有可能的子集(幂集)。解集不能包含重复的子集。你可以按任意顺序返回解集。提示:1<=nums.le......
  • 力扣-77-组合
    直达链接这个问题应该就是我想找的答案了,把k=1~n全部输出一遍然后如果k=n,那就是全排列问题不对,还是不一样,这里只考虑数字组合,而没考虑数字顺序也就是排列问题两种解法,第......
  • 力扣 leetcode 1775. 通过最少操作次数使数组的和相等
    问题描述给你两个长度可能不等的整数数组nums1和nums2。两个数组中的所有值都在1到6之间(包含1和6)。每次操作中,你可以选择任意数组中的任意一个整数,将它变成......
  • 力扣540(java&python)-有序数组中的单一元素(中等)
    题目:给你一个仅由整数组成的有序数组,其中每个元素都会出现两次,唯有一个数只会出现一次。请你找出并返回只出现一次的那个数。你设计的解决方案必须满足O(logn)时间复......
  • 12306抢票神器,助力远在他乡想回家的你
    目录​​背景​​​​修改配置文件:TickerConfig​​​​开始运行:pythonrun.pyr​​​​分析源码​​​​拭目以待 ​​背景每逢佳节倍思亲,年关将近,思乡的情绪是不是愈发......
  • linux不用设备树写中断,linux-kernel – 将设备树中断标志映射到devm_request_irq
    我目前正在为Linux使用PowerPC编写设备驱动程序.设备树条目如下://PPSInterruptclientpps_hwirq{compatible="pps-hwirq";interrupts=<170x02>;//IPIC1......
  • 力扣12 数字转为罗马数字
    力扣12数字转为罗马数字题目:罗马数字包含以下七种字符:I,V,X,L,C,D和M。字符数值I1V5X10L50C......
  • 力扣 leetcode 797. 所有可能的路径
    问题描述给你一个有n个节点的有向无环图(DAG),请你找出所有从节点0到节点n-1的路径并输出(不要求按特定顺序)graph[i]是一个从节点i可以访问的所有节点的列表(即从节......
  • 力扣 leetcode 130. 被围绕的区域
    问题描述给你一个m*n的矩阵board,由若干字符'X'和'O',找到所有被'X'围绕的区域,并将这些区域里所有的'O'用'X'填充。提示:m==board.lengthn==board[i......
  • 「查找表」字符串中不同整数的数目(力扣第1805题)
    本题为12月6日力扣每日一题题目来源:力扣第1805题题目tag:查找表双指针题面题目描述给你一个字符串word,该字符串由数字和小写英文字母组成。请你用空格替换每个不......