[LEECODE]算法进阶自练习7-8 二叉树&二叉搜索树
7.二叉树
简单
- 二叉树的最小深度 leetcode 111 递归解法:
int minDepth(TreeNode* root) {
if(!root) return 0;
if(root->left == nullptr && root->right == nullptr) return 1;
int minDep = INT_MAX;
if(root->left){
minDep = min(minDep,minDepth(root->left));
}
if(root->right){
minDep = min(minDep,minDepth(root->right));
}
return minDep + 1; // +1是加上当前节点
}
广度优先解法:
int minDepth(TreeNode* root) {
if(!root) return 0;
int ret = 0;
queue<pair<TreeNode*, int>> queue;
queue.emplace(root,1);
while (!queue.empty()) {
TreeNode *node = queue.front().first;
int depth = queue.front().second;
queue.pop();
if (node->left == nullptr && node->right == nullptr) {
return depth;
}
if (node->left != nullptr) {
queue.emplace(node->left, depth + 1);
}
if (node->right != nullptr) {
queue.emplace(node->right, depth + 1);
}
}
return 0;
}
- 二叉树的最近公共祖先 leetcode 236 递归解法,理解思路可以参考这里(leetcode-cn.com/problems/lo…
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
/*
1.如果 p 和 q 都存在,则返回它们的公共祖先;
2.如果只存在一个,则返回存在的一个;
3. 如果 p 和 q 都不存在,则返回NULL
*/
if(!root) return nullptr;
if(root == p || root == q) return root;
TreeNode* l = lowestCommonAncestor(root->left,p,q);
TreeNode* r = lowestCommonAncestor(root->right,p,q);
if(!l) return r; // p q在右子树 注意这里判断条件是先判断为空的, 不然走不到 l&&r
if(!r) return l; // p q在左子树 注意这里判断条件是先判断为空的, 不然走不到 l&&r
if(l && r) return root; // p q在左右两个子树
return nullptr;
}
记录父节点,然后遍历查找最后的一个共同的父节点。
class Solution {
public:
unordered_map<int, TreeNode*> fa;
unordered_map<int, bool> vi;
void dfs(TreeNode* root){
if(root->left){
fa[root->left->val] = root;
dfs(root->left);
}
if(root->right){
fa[root->right->val] = root;
dfs(root->right);
}
}
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
fa[root->val] = nullptr;
dfs(root);
while(p != nullptr){
vi[p->val] = true;
p = fa[p->val];
}
while(q != nullptr){
if(vi[q->val]) return q;
q = fa[q->val];
}
return nullptr;
}
};
中等
- 对称二叉树 leetcode 101 递归解法:
bool check(TreeNode* l, TreeNode* r){
if(!l && !r) return true; // 左右节点都为空了
if(!l || !r) return false; // 有一个节点不为空
return l->val == r->val && check(l->left,r->right) && check(l->right,r->left);
}
bool isSymmetric(TreeNode* root) {
if(!root) return true;
return check(root->left, root->right);
}
层序遍历解法:
bool check(TreeNode* l, TreeNode* r){
queue<TreeNode*> queue;
queue.push(l);queue.push(r);
while(!queue.empty()){
TreeNode* tl = queue.front(); queue.pop();
TreeNode* tr = queue.front(); queue.pop();
if(!tl && !tr) continue;
if((!tl || !tr) || tl->val != tr->val) return false;
queue.push(tl->left);
queue.push(tr->right);
queue.push(tl->right);
queue.push(tr->left);
}
return true;
}
bool isSymmetric(TreeNode* root) {
if(!root) return true;
return check(root->left, root->right);
}
- 二叉树的锯齿形层次遍历 leetcode 103
vector<vector<int>> zigzagLevelOrder(TreeNode* root) {
if(root == nullptr) return {};
vector<vector<int>> ret;
queue<TreeNode*> queue;
queue.push(root);
int level = 0;
while(!queue.empty()){
int tsize = queue.size();
vector<int> vt;
while(tsize > 0){
TreeNode* node = queue.front();
queue.pop();
if(level%2 == 0) {
vt.push_back(node->val);
}
else{
vt.insert(vt.begin(),node->val);
}
if(node->left) queue.push(node->left);
if(node->right) queue.push(node->right);
tsize--;
}
level++;
ret.push_back(vt);
}
return ret;
}
- 二叉树的层序遍历 102 上面那道题删除几行代码就行了。
vector<vector<int>> levelOrder(TreeNode* root) {
if(root == nullptr) return {};
vector<vector<int>> ret;
queue<TreeNode*> queue;
queue.push(root);
int level = 0;
while(!queue.empty()){
int tsize = queue.size();
vector<int> vt;
while(tsize > 0){
TreeNode* node = queue.front();
queue.pop();
vt.push_back(node->val);
if(node->left) queue.push(node->left);
if(node->right) queue.push(node->right);
tsize--;
}
level++;
ret.push_back(vt);
}
return ret;
}
困难
- 二叉树的序列化与反序列化 leetcode 297
class Codec {
public:
void levelOrder(TreeNode* root, string& ret){
if(root == nullptr) return;
queue<TreeNode*> qu;
qu.push(root);
while(!qu.empty()){
TreeNode* node = qu.front();
qu.pop();
if(node){
qu.push(node->left);
qu.push(node->right);
ret += to_string(node->val);
ret += ",";
}
else{
ret += "$,";
}
}
}
bool getData(string_view& data, int& number){
int n = data.size();
int i = 0;
bool isNumber = true;
number = 0;
bool negative = false;
while(i<n){
if(data[i] == '-'){
negative = true;
i++;
}
else if(data[i] == '$'){
isNumber = false;
i++;
}else if(data[i] == ','){
data = data.substr(i+1);
if(negative) number = -number;
return isNumber;
}
else{
number = number*10 + (data[i++] - '0');
}
}
return isNumber;
}
TreeNode* deLevelOrder(string_view data){
int number;
queue<TreeNode*> qu;
getData(data, number);
TreeNode* root = new TreeNode(number);
qu.push(root);
while(!qu.empty()){
TreeNode* node = qu.front();
qu.pop();
if(getData(data, number)){
node->left = new TreeNode(number);
qu.push(node->left);
}
if(getData(data, number)){
node->right = new TreeNode(number);
qu.push(node->right);
}
}
return root;
}
// Encodes a tree to a single string.
string serialize(TreeNode* root) {
string ret;
levelOrder(root,ret);
return ret;
}
// Decodes your encoded data to tree.
TreeNode* deserialize(string data) {
if(data.empty()) return nullptr;
return deLevelOrder(data);
}
};
8.二叉搜索树
简单
- 二叉搜索树节点最小距离 leetcode 783
注意此题是求
任意两个节点的差值递归解法:
class Solution {
public:
vector<int> nums;
void collection(TreeNode* root){
if(!root) return;
if(root->left) collection(root->left);
nums.push_back(root->val);
if(root->right) collection(root->right);
}
int minDiffInBST(TreeNode* root) {
int min = INT_MAX;
collection(root);
for(int i = 1; i< nums.size(); i++){
min = min > abs(nums[i] - nums[i-1]) ? abs(nums[i] - nums[i-1]) : min;
}
return min;
}
};
深度优先遍历解法:
class Solution {
public:
vector<int> nums;
void collection(TreeNode* root){
if(!root) return;
stack<TreeNode*> st;
while(root || !st.empty()){
while(root){
st.push(root);
root = root->left;
}
if(!st.empty()){
root = st.top();
st.pop();
nums.push_back(root->val);
root = root->right;
}
}
}
int minDiffInBST(TreeNode* root) {
int min = INT_MAX;
collection(root);
for(int i = 1; i< nums.size(); i++){
min = min > abs(nums[i] - nums[i-1]) ? abs(nums[i] - nums[i-1]) : min;
}
return min;
}
};
- 二叉搜索树的最近公共祖先 leetcode 235 递归解法,理解思路可以参考这里(leetcode-cn.com/problems/lo…
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
/*
1.如果 p 和 q 都存在,则返回它们的公共祖先;
2.如果只存在一个,则返回存在的一个;
3. 如果 p 和 q 都不存在,则返回NULL
*/
if(!root) return nullptr;
if(root == p || root == q) return root;
TreeNode* l = lowestCommonAncestor(root->left,p,q);
TreeNode* r = lowestCommonAncestor(root->right,p,q);
if(!l) return r; // p q在右子树 注意这里判断条件是先判断为空的, 不然走不到 l&&r
if(!r) return l; // p q在左子树 注意这里判断条件是先判断为空的, 不然走不到 l&&r
if(l && r) return root; // p q在左右两个子树
return nullptr;
}
记录父节点,然后遍历查找最后的一个共同的父节点。
class Solution {
public:
unordered_map<int, TreeNode*> fa;
unordered_map<int, bool> vi;
void dfs(TreeNode* root){
if(root->left){
fa[root->left->val] = root;
dfs(root->left);
}
if(root->right){
fa[root->right->val] = root;
dfs(root->right);
}
}
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
fa[root->val] = nullptr;
dfs(root);
while(p != nullptr){
vi[p->val] = true;
p = fa[p->val];
}
while(q != nullptr){
if(vi[q->val]) return q;
q = fa[q->val];
}
return nullptr;
}
};
中等
- 验证二叉搜索树 leetcode 98 递归解法:左子树的值要小于根节点的值,右子树的值要大于根节点的值
bool helper(TreeNode* root, long upper, long lower){
if(root == nullptr) return true;
if(root->val >= upper || root->val <= lower) return false;
return helper(root->left, root->val, lower) && helper(root->right, upper, root->val); // 左子树更新当前最大值, 右子树更新当前最小值
}
bool isValidBST(TreeNode* root) {
return helper(root, LONG_MAX, LONG_MIN);
}
中序遍历判断是否有序
bool isValidBST(TreeNode* root) {
if(root == nullptr) return true;
long prev = (long)INT_MIN-1;
stack<TreeNode*> st;
while(!st.empty() || root != nullptr){
while(root != nullptr){
st.push(root);
root = root->left;
}
if(!st.empty()){
root = st.top();
st.pop();
if(root->val <= prev) return false; // 前面一个值较大则不是二叉搜索树
prev = root->val;
root = root->right;
}
}
return true;
}
- 二叉搜索树的范围和 leetcode 938 这个题目很简单,但是题目没有说明白,乍一看很懵,看了题解才知道这样啊。如果根节点值>=L&& <=R 计算左右子树和,如果根节点值<L计算右子树,如果根节点值>R计算左子树即可,这样递归就很简单了。
class Solution {
public:
int num = 0;
void rangeSum(TreeNode* root, int L, int R){
if(!root) return;
if(root->val >= L && root->val <= R){
num += root->val;
rangeSum(root->right,L,R);
rangeSum(root->left,L,R);
}
if(root->val > R){
rangeSum(root->left,L,R);
}
if(root->val < L){
rangeSum(root->right,L,R);
}
}
int rangeSumBST(TreeNode* root, int L, int R) {
rangeSum(root, L, R);
return num;
}
};
非递归版本就更容易了,直接全部遍历然后计算和就行了,这里其实应该可以优化如果在L和R之间的去遍历就可以了,其他的就没必要处理了。
class Solution {
public:
int num = 0;
void rangeSum(TreeNode* root, int L, int R){
if(!root) return;
stack<TreeNode*> st;
while(!st.empty() || root){
while(root){
st.push(root);
root = root->left;
}
if(!st.empty()){
root = st.top();
st.pop();
if(root->val >= L && root->val <= R){
num += root->val;
}
root = root->right;
}
}
}
int rangeSumBST(TreeNode* root, int L, int R) {
rangeSum(root, L, R);
return num;
}
};
- 存在重复元素 III leetcode 220 暴力算法求解,不能AC。
bool containsNearbyAlmostDuplicate(vector<int>& nums, int k, int t) {
int nsize = nums.size();
for(int i=0; i<nsize; i++){
for(int j=max(i-k,0); j<i; j++){
if(abs((long)nums[i] - nums[j]) <= (long)t){
return true;
}
}
}
return false;
}
暴力求解的缺点是在于查询的时候是线性查找的,用set做滑动窗口,查询使用平衡二叉树来解决每次查询k范围的问题,降低为查询O(logK); lower_bound返回的是map/set中小于等于给定值的最大值迭代器,upper_bound返回的大于给定值的第一个值得迭代器。
bool containsNearbyAlmostDuplicate(vector<int>& nums, int k, int t) {
set<long> window; // 滑动窗口
int nsize = nums.size();
for(int i=0; i<nsize; i++){
auto it = window.lower_bound((long)nums[i]-t); // 找到大于或者等于 nums[i]-t 的最小的数字
if(it != window.end() && *it <= (long)nums[i] + t){ // 如果找到了并且这个数字大于nums[i]+t
return true;
}
window.insert(nums[i]);
if(window.size() == k+1){
window.erase(nums[i-k]);
}
}
return false;
}
困难
- 计算右侧小于当前元素的个数 leetcode 315 循环判断的暴力解法,不能AC:
vector<int> countSmaller(vector<int>& nums) {
int nsize = nums.size();
vector<int> ret(nsize,0);
for(int i = nsize-2; i>=0; i--){
int count = 0;
for(int j=nsize-1; j>i; j--){
if(nums[j] < nums[i]){
count++;
}
}
ret[i] = count;
}
return ret;
}
树状数组解法:
struct SegmentTreeNode{
int start, end, count;
SegmentTreeNode* left, *right;
SegmentTreeNode(int _start, int _end):start(_start),end(_end),count(0),left(nullptr),right(nullptr){}
};
class Solution {
public:
SegmentTreeNode* build(int start, int end){
if(start > end) return nullptr;
SegmentTreeNode* node = new SegmentTreeNode(start, end);
if(start == end){
node->count = 0;
}
else{
int mid = (end - start)/2 + start;
node->left = build(start, mid);
node->right = build(mid+1, end);
}
return node;
}
int count(SegmentTreeNode* node, int start, int end){
if(node == nullptr || start > end) return 0;
if(start == node->start && end == node->end) return node->count;
int mid = node->start + (node->end - node->start)/2;
int left_num = 0, right_num = 0;
if(start <= mid){
if(mid < end){
left_num = count(node->left,start, mid);
}
else{
left_num = count(node->left, start, end);
}
}
if(mid < end){
if(start <= mid){
right_num = count(node->right,mid+1,end);
}
else{
right_num = count(node->right,start,end);
}
}
return left_num + right_num;
}
void insert(SegmentTreeNode* node, int index, int val){
if(node->start == index && node->end == index){
node->count += val;
return;
}
int mid = node->start + (node->end - node->start)/2;
if(index >= node->start && index<=mid){
insert(node->left, index, val);
}
if(index > mid && index<=node->end){
insert(node->right, index, val);
}
node->count = node->left->count + node->right->count;
}
vector<int> countSmaller(vector<int>& nums) {
int nsize = nums.size();
vector<int> ret(nsize,0);
if(nsize == 0) return ret;
int n_min = nums[0], n_max = nums[0];
for(int i=0; i<nsize; i++){
n_min = min(n_min, nums[i]);
n_max = max(n_max, nums[i]);
}
SegmentTreeNode* root = build(n_min, n_max);
for(int i=nsize-1; i>=0; i--){
ret[i] = count(root, n_min, nums[i]-1);
insert(root, nums[i], 1);
}
return ret;
}
};
归并排序思路解法:
void mergeCount(vector<int>& nums, int start, int mid, int end, vector<int>& index, vector<int>& tmp, vector<int>& ret){
for(int i=start; i<=end; i++) tmp[i] = index[i];
int i = start, j = mid+1;
for(int k=start; k<=end; k++){
if(i>mid){
index[k] = tmp[j];
j++;
}
else if(j>end){
index[k] = tmp[i];
i++;
ret[index[k]] += (end - mid);
}else if(nums[tmp[i]] <= nums[tmp[j]]){
index[k] = tmp[i];
i++;
ret[index[k]] += (j-mid-1);
}
else{
index[k] = tmp[j];
j++;
}
}
}
void mergeAndCountSmaller(vector<int>& nums, int start, int end, vector<int>& index, vector<int>& tmp, vector<int>& ret){
if(start == end) return;
int mid = start + (end - start)/2;
mergeAndCountSmaller(nums, start, mid, index, tmp, ret);
mergeAndCountSmaller(nums, mid+1, end, index, tmp, ret);
if(nums[index[mid]] <= nums[index[mid+1]]) return;
mergeCount(nums, start, mid, end, index, tmp, ret);
}
vector<int> countSmaller(vector<int>& nums) {
int nsize = nums.size();
vector<int> ret(nsize,0);
if(nsize == 0) return ret;
vector<int> tmp(nsize,0); // 归并临时空间
vector<int> index(nsize,0); // 数组顺序
for(int i=0; i<nsize; i++) index[i] = i;
mergeAndCountSmaller(nums, 0, nsize-1, index, tmp, ret);
return ret;
}