题目描述
给定一个有相同值的二叉搜索树(BST),找出 BST 中的所有众数(出现频率最高的元素)。
假定 BST 有如下定义:
- 结点左子树中所含结点的值小于等于当前结点的值
- 结点右子树中所含结点的值大于等于当前结点的值
- 左子树和右子树都是二叉搜索树
例如:
给定 BST [1,null,2,2],
返回[2]。
提示:如果众数超过1个,不需考虑输出顺序
进阶:你可以不使用额外的空间吗?(假设由递归产生的隐式调用栈的开销不被计算在内)
解题思路
递归法
非二叉搜索树的方式
最直观的方法是遍历这个树,用map统计频率,把频率排个序,最后取前面高频的元素的集合。
- 遍历二叉树,用map统计频率
// map<int, int> key->元素, value->出现频率
void searchBST(TreeNode* cur, unordered_map<int, int>& map) {
if (cur == NULL) return ;
map[cur->val]++; // 统计元素频率
searchBST(cur->left, map);
searchBST(cur->right, map);
return;
}
- 把统计出来的出现频率排序
将map转化为数组再进行排序(C++中如果使用std::map或者std::multimap可以对key排序,但不能对value排序。),vector里面放的也是pair<int, int>类型的数据,第一个int为元素,第二个int为出现频率。
bool static cmp (const pair<int, int>& a, const pair<int, int>& b) {
return a.second > b.second; // 按照频率从大到小排序
}
vector<pair<int, int>> vec(map.begin(), map.end());
sort(vec.begin(), vec.end(), cmp); // 给频率排序
- 取高频元素
此时数组vector中已经是存放着按照频率排好序的pair,那么把前面高频的元素取出来就可以了。
result.push_back(vec[0].first);
for (int i = 1; i < vec.size(); i++) {
if (vec[i].second == vec[0].second)
result.push_back(vec[i].first);
else break;
}
return result;
整体代码:
class Solution {
private:
void searchBST(TreeNode* cur, unordered_map<int, int>& map) {
if (cur == NULL) return;
map[cur->val]++;
searchBST(cur->left, map);
searchBST(cur->right, map);
return;
}
bool static cmp (const pair<int, int>& a, const pair<int, int>& b) {
return a.second > b.second;
}
public:
vector<int> findMode(TreeNode* root) {
unordered_map<int, int> map;
vector<int> result;
if (root == NULL) return result;
searchBST(root, map);
vector<pair<int, int>> vec(map.begin(), map.end());
sort(vec.begin(), vec.end(), cmp);
result.push_back(vec[0].first);
for (int i = 1; i < vec.size(); i++) {
if (vec[i].second == vec[0].second)
result.push_back(vec[i].first);
else break;
}
return result;
}
};
二叉搜索树的方式
二叉搜索树的中序遍历是有序的。
在遍历有序数组的元素出现频率时,一般采用从头遍历的方式,让相邻的两个元素作比较,把出现频率最高的元素输出。
在树上也可以这样做,我们设置一个指针指向前一个节点,这样每次cur(当前节点)才能和pre(前一个节点)作比较。
而且初始化的时候pre = NULL,这样当pre为NULL时候,我们就知道这是比较的第一个元素。
代码如下:
if (pre == NULL) { // 第一个节点
count = 1; // 频率为1
} else if (pre->val == cur->val) { // 与前一个节点数值相同
count++;
} else { // 与前一个节点数值不同
count = 1;
}
pre = cur; // 更新上一个节点
本题要求的是最大频率的元素集合,在数组上,要先找出最大频率,然后再遍历一次数组,将最大频率的元素放入集合中。这样就遍历了两次数组。
本题也可以遍历两次数组,但这里提供一种只遍历一次的写法。
如果频率 = 最大频率 (count = maxCount),就将这个元素加入到结果集中:
if (count == maxCount) {
result.push_back(cur->val);
}
这里存在一个问题,如果此时的maxCount不是最大频率就会出现错误,因此要做如下操作:
当频率大于最大频率(count > maxCount)时,不仅要更新 maxCount,而且要清空结果集result,因为当前结果集中的元素已经不是最大频率了。
if (count > maxCount) { // 如果计数大于最大值
maxCount = count; // 更新最大频率
result.clear(); // 晴空result
result.push_back(cur=>val);
}
完整代码如下:
class Solution {
private:
int maxCount = 0;
int count = 0;
TreeNode* pre = NULL;
vector<int> result;
void searchBST(TreeNode* cur) {
if (cur == NULL) return;
searchBST(cur->left);
if (pre == NULL) {
count = 1;
} else if(pre->val == cur->val) {
count++;
} else {
count = 1;
}
pre = cur;
if (count == maxCount) {
result.push_back(cur->val);
}
if (count > maxCount) {
maxCount = count;
result.clear();
result.push_back(cur->val);
}
searchBST(cur->right);
return;
}
public:
vector<int> findMode(TreeNode* root) {
count = 0;
maxCount = 0;
pre = NULL;
result.claer();
searchBST(root);
return result;
}
};
迭代法
只要把中序遍历转成迭代,中间节点的处理逻辑完全一样。
class Solution {
public:
vector<int> findMode(TreeNode* root) {
stack<TreeNode*> st;
TreeNode* cur = root;
TreeNode* pre = NULL:
int maxCount = 0;
int count = 0;
vector<int> result;
while (cur != NULL || !st.empty()) {
if (cur != NULL) {
st.push(cur);
cur = cur->left;
} else {
cur = st.top();
st.pop();
if (pre = NULL) {
count = 1;
} else if (pre->val = cur->val) {
count++;
} else {
count = 1;
}
if (count = maxCount) {
result.push_back(cur->val);
}
if (count > maxCount) {
maxCount = count;
result.clear();
result.push_back(cur->val);
}
pre = cur;
cur = cur->right;
}
}
return result;
}
};