【LeetCode & 剑指offer刷题】树题16:Kth Smallest Element in a BST

【LeetCode & 剑指offer 刷题笔记】目录(持续更新中...)

Kth Smallest Element in a BST

Given a binary search tree, write a function   kthSmallest   to find the   k th smallest element in it.
Note:  
You may assume k is always valid, 1 ≤ k ≤ BST's total elements.
Example 1:
Input: root = [3,1,4,null,2], k = 1
  3
 / \
1   4
 \
  2
Output: 1
Example 2:
Input: root = [5,3,6,2,4,null,null,1], k = 3
        5
       / \
      3   6
     / \
    2   4
   /
  1
Output: 3
Follow up:
What if the BST is modified (insert/delete operations) often and you need to find the kth smallest frequently? How would you optimize the kthSmallest routine?

C++
 
/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
 
/*
方法一:中序遍历递归法
BST中序遍历之后为从小到大排列
中序遍历递归法,不是最优的,因为是遍历完之后在给出的结果(可用迭代法进行改进,参考题目 Validate Binary Search Tree
注:可在递归中加入判断进行减枝
*/
class Solution
{
public :
    int kthSmallest ( TreeNode * root , int k )
    {
        vector < int > nodes ;
        inorder ( root , nodes );
        return nodes [ k - 1 ];
    }
   
    void inorder ( TreeNode * root , vector < int >& nodes )
    {
        if ( root == nullptr ) return ; //递归的出口
       
        inorder ( root -> left , nodes );
        nodes . push_back ( root -> val );
        inorder ( root -> right , nodes );
    }
};
 
/*
方法二:中序遍历迭代法
在遍历的过程中统计数量
*/
class Solution
{
public :
    int kthSmallest ( TreeNode * root , int k )
    {
        int cnt = 0 ;
        stack < TreeNode *> s ;
        TreeNode * p = root ;
        while (! s . empty () || p )
        {
            if ( p ) // 左结点不为空时
            {
                s . push ( p );    // 入栈
 
                p = p -> left ; // 指向下一个左结点
            }
            else // 左结点为空时
            {
                p = s . top ();
                cnt++; //统计数目(遍历到了要访问的父结点)
                if ( cnt == k ) return p -> val ;
                s . pop ();
               
                p = p -> right ;   // 指向右结点              
            }
          
        }
        return 0 ;
    }
};
/*
方法三:分治法
首先计算出左子树的结点个数总和 cnt
如果 k 小于等于左子树结点总和 cnt ,说明第 k 小的元素在左子树中,直接对左子结点调用递归即可。
如果 k 大于 cnt+1 ,说明目标值在右子树中,对右子结点调用递归函数
如果 k 等于 cnt+1, 则当前结点即为所求
*/
class Solution
{
public :
    int kthSmallest ( TreeNode * root , int k )
     {
        int cnt = count ( root -> left );
        if ( k <= cnt )
         {
            return kthSmallest ( root -> left , k );
        }
         else if ( k > cnt + 1 )
         {
            return kthSmallest ( root -> right , k - cnt - 1 ); // 注意此处变为k - (cnt-1)
        }
         else
             return root -> val ;
    }
    int count ( TreeNode * node )
     {
        if (! node ) return 0 ;
        return 1 + count ( node -> left ) + count ( node -> right );
    }
};
/*
Follow up: 假设该 BST 被修改的很频繁,而且查找第 k 小元素的操作也很频繁,问我们如何优化
方法:改进方法三
修改原树结点的结构,使其保存包括当前结点和其左右子树所有结点的个数,
这样我们使用的时候就可以快速得到任何左子树结点总数来帮我们快速定位目标值了
 
分析:
对于查找很频繁的情况,由于保存了结点个数,每次查找时,不需要递归统计结点个数,这样可以节省大量时间
对于修改很频繁的情况,插入或者删除某个结点时,需更新其所有祖先结点,所以该方法并不太适用于修改很频繁的情况
*/
 
class Solution
{
private :
    struct MyTreeNode
     {
        int val ;
        int count ;
        MyTreeNode * left ;
        MyTreeNode * right ;
        MyTreeNode ( int x ) : val ( x ), count (1 ), left ( NULL ), right ( NULL ) {}
    };
    
public :
    int kthSmallest ( TreeNode * root , int k )
     {
        MyTreeNode * node = build ( root );
        return helper ( node , k );
    }
    
    MyTreeNode * build ( TreeNode * root )
     {
        if (! root )
             return NULL ;
         else
         {
            MyTreeNode * node = new MyTreeNode ( root -> val ); //count在构造函数中被初始化为1
            node -> left = build ( root -> left );
            node -> right = build ( root -> right );        
             if ( node -> left ) node -> count += node -> left -> count ; // 统计数量
             if ( node -> right ) node -> count += node -> right -> count ;      
            
             return node ;
         }   
    }
   
    int helper ( MyTreeNode * node , int k )
     {
        if ( node -> left )
         {
            int cnt = node -> left -> count ; // 左结点存储了当前结点左子树的所有结点个数
            if ( k <= cnt )
                return helper ( node -> left , k );
             else if ( k > cnt + 1 )
                return helper ( node -> right , k - 1 - cnt );
             else
                 return node -> val ;
        }
         else
         {
            if ( k == 1 )
                 return node -> val ;
             else
                 return helper ( node -> right , k - 1 );
           
        }
    }
};
 
 

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值