【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
);
}
}
};