二叉树的其他题集

1,翻转二叉树

来源:226. 翻转二叉树  &&  剑指 Offer 27. 二叉树的镜像

思路:交换左右子树,再递归

class Solution {
public:
    TreeNode* mirrorTree(TreeNode* root) {
        if (root == NULL) return NULL;
        TreeNode* t = root->left;
        root->left = root->right;
        root->right = t;
        mirrorTree(root->left);
        mirrorTree(root->right);
        return root;
    }
};

2,构建最小高度树

来源:面试题 04.02. 最小高度树

思路:使用折半查找的方法创建二叉搜索树,保证高度最小。

class Solution {
public:
    TreeNode* sortedArrayToBST(vector<int>& nums) {
        return create(nums, 0, nums.size() - 1);
    }
    TreeNode* create(const vector<int>& nums, int left, int right) {
        if (left > right) return NULL;
        int mid = (left + right) / 2;
        TreeNode* r = new TreeNode(nums[mid]);
        r->left = create(nums, left, mid - 1);
        r->right = create(nums, mid + 1, right);
        return r;
    }
};

3,根据二叉树创建字符串

来源:606. 根据二叉树创建字符串

思路:前序遍历,难度不大,注意小括号,左右子树分4种情况分别处理。

class Solution {
public:
    string tree2str(TreeNode* t) {
        if (t == NULL) return "";
        string ret = to_string(t->val);
        if (t->left) {
            ret += "("+tree2str(t->left)+")";
            if (t->right) {
                ret += "("+tree2str(t->right)+")";
            }
        } else {
            if (t->right) {
                ret += "()";
                ret += "("+tree2str(t->right)+")";
            }
        }
        return ret;
    }
};

4,二叉树的堂兄弟

来源:993. 二叉树的堂兄弟节点

思路:二者深度相同且父节点不同。

class Solution {
public:
    bool isCousins(TreeNode* root, int x, int y) {
        int dx = depth(root, x, 0);
        int dy = depth(root, y, 0);
        if (dx == -1 || dy == -1 || dx != dy) return false;
        int px = parent(root, x);
        int py = parent(root, y);
        if (px == -1 || py == -1) return false;
        return px != py;
    }
    // 节点的深度
    int depth(TreeNode* root, int v, int dep) {
        if (root == NULL) return -1;
        if (root->val == v) return dep;
        int left = depth(root->left, v, dep+1);
        int right = depth(root->right, v, dep+1);
        return left != -1 ? left : right;
    }
    // 节点的父节点
    int parent(TreeNode* root, int v) {
        if (root == NULL) return -1;
        if (root->left && root->left->val == v) return root->val;
        if (root->right && root->right->val == v) return root->val;
        int left = parent(root->left, v);
        int right = parent(root->right, v);
        return left != -1 ? left : right;
    }
};

5,二叉树第2小节点

来源:671. 二叉树中第二小的节点

思路:根节点为第1小节点,在左右子树上找大于根节点的节点,如果子树节点值等于根节点则继续递归,大于根节点则终止递归并与result判断大小。

class Solution {
public:
    int result;
    int findSecondMinimumValue(TreeNode* root) {
        result = -1;
        if (root == NULL) return -1;
        find(root, root->val);
        return result;
    }
    void find(TreeNode* root, int v) {
        TreeNode *p = root->left;
        if (p) {
            if (p->val == v) find(p, v);
            else {
                if (result == -1) result = p->val;
                else result = min(result, p->val);
            }
        }
        p = root->right;
        if (p) {
            if (p->val == v) find(p, v);
            else {
                if (result == -1) result = p->val;
                else result = min(result, p->val);
            }
        }
    }
};

6,二叉搜索树转单链表

来源:面试题 17.12. BiNode

思路1:定义一个prev指针,中序遍历时prev的right指向当前节点。

思路2:先中序遍历存储结果,再清空所有节点的left指针,重置right指针。

class Solution {
public:
    TreeNode *first;
    TreeNode *prev;
    TreeNode* convertBiNode(TreeNode* root) {
        first = NULL;
        prev = NULL;
        visit(root);
        return first;
    }
    void visit(TreeNode* root) {
        if (root == NULL) return;
        visit(root->left);
        if (prev == NULL) {
            first = root;
            prev = root;
        } else {
            prev->right = root;
            prev->left = NULL;
            prev = root;
            root->left = NULL;
        }
        visit(root->right);
    }
};

7,找出克隆二叉树中的相同节点

来源:1379. 找出克隆二叉树中的相同节点

思路:在原树中利用回溯的思想找到目标节点,并记录路径,在克隆树上根据路径找到对应位置。目标节点的判断:c++可以根据节点地址判断二者是否相同。

class Solution {
public:
    vector<int> result;
    vector<int> path;
    bool ok;
    TreeNode* getTargetCopy(TreeNode* original, TreeNode* cloned, TreeNode* target) {
        if (original == NULL) return NULL;
        ok = false;
        find(original, target);

        TreeNode *p = cloned;
        for (int i = 0; i < result.size(); i++) {
            if (result[i] == 0) p = p->left;
            else p = p->right;
        }
        return p;
    }
    void find(TreeNode* root, TreeNode* target) {
        if (root == target) {
            result = path;
            ok = true;
            return;
        }
        if (ok) return;
        if (root->left) {
            path.push_back(0);
            find(root->left, target);
            path.pop_back();
        }
        if (root->right) {
            path.push_back(1);
            find(root->right, target);
            path.pop_back();
        }
    }
};

8,无序数组构造二叉树

来源:654. 最大二叉树

思路:找到最大值,将数组分成左右2部分分别创建左右子树。

class Solution {
public:
    TreeNode* constructMaximumBinaryTree(vector<int>& nums) {
        return create(nums, 0, nums.size() - 1);
    }
    TreeNode* create(const vector<int>& nums, int l, int r) {
        if (l > r) return NULL;
        int max = nums[l];
        int p = l;
        for (int i = l + 1; i <= r; i++) {
            if (nums[i] > max) {
                max = nums[i];
                p = i;
            }
        }
        TreeNode *ret = new TreeNode(max);
        ret->left = create(nums, l, p - 1);
        ret->right = create(nums, p + 1, r);
        return ret;
    }
};

9,祖父节点

来源:1315. 祖父节点值为偶数的节点和

思路:凡是判断祖父、父节点的题,在递归的参数中将他们带上即可。

class Solution {
public:
    int sum;
    int sumEvenGrandparent(TreeNode* root) {
        sum = 0;
        if (root->left) {
            visit(root, root->left, root->left->left);
            visit(root, root->left, root->left->right);
        }
        if (root->right) {
            visit(root, root->right, root->right->left);
            visit(root, root->right, root->right->right);
        }
        return sum;
    }
    void visit(TreeNode* grand, TreeNode* parent, TreeNode* curr) {
        if (curr == NULL) return;
        if (grand->val % 2 == 0) {
            sum += curr->val;
        }
        visit(parent, curr, curr->left);
        visit(parent, curr, curr->right);
    }
};

10,每层构造单链表

来源:面试题 04.03. 特定深度节点链表

思路:层序遍历的同时创建单链表

class Solution {
public:
    vector<ListNode*> listOfDepth(TreeNode* tree) {
        vector<ListNode*> result;
        vector<TreeNode*> a;
        if (tree) a.push_back(tree);
        while (!a.empty()) {
            result.push_back(createList(a));
            vector<TreeNode*> b;
            for (int i = 0; i < a.size(); i++) {
                if (a[i]->left) b.push_back(a[i]->left);
                if (a[i]->right) b.push_back(a[i]->right);
            }
            a = b;
        }
        return result;
    }
    ListNode *createList(const vector<TreeNode*>& a) {
        ListNode* head = new ListNode(a[0]->val);
        ListNode* tail = head;
        for (int i = 1; i < a.size(); i++) {
            ListNode *p = new ListNode(a[i]->val);
            tail->next = p;
            tail = p;
        }
        return head;
    }
};

11,删除给定值的所有叶子节点

来源:1325. 删除给定值的叶子节点

思路:递归删除,如果当前节点等于给定值,并且整树都是给定值,则全部删除。

class Solution {
public:
    TreeNode* removeLeafNodes(TreeNode* root, int target) {
        return remove(root, target);
    }
    TreeNode* remove(TreeNode* root, int target) {
        if (root == NULL) return NULL;
        if (root->val == target && same(root, target)) return NULL; // 全部删除
        root->left = remove(root->left, target);
        root->right = remove(root->right, target);
        return root;
    }
    bool same(TreeNode* root, int target) {
        if (root == NULL) return true;
        if (root->val != target) return false;
        return same(root->left, target) && same(root->right, target);
    }
};

814. 二叉树剪枝 与本题类似,移除节点为0的所有叶子节点。

12,左下角的值

来源:513. 找树左下角的值

思路:层序遍历时记录第一个节点的值

class Solution {
public:
    int findBottomLeftValue(TreeNode* root) {
        int result = root->val;
        queue<TreeNode*> q;
        q.push(root);
        while (!q.empty()) {
            int sz = q.size();
            for (int i = 0; i < sz; i++) {
                TreeNode *p = q.front(); q.pop();
                if (i == 0) result = p->val;
                if (p->left) q.push(p->left);
                if (p->right) q.push(p->right);
            }
        }
        return result;
    }
};

13,查找子树

来源:面试题 04.10. 检查子树

思路:递归遍历t1的每个节点,判断当前子树与t2是否相同。

class Solution {
public:
    bool checkSubTree(TreeNode* t1, TreeNode* t2) {
        return find(t1, t2);
    }
    bool find(TreeNode* t1, TreeNode* t2) {
        bool ret = same(t1, t2);
        if (ret) return true;
        return t1 && (find(t1->left, t2) || find(t1->right, t2));
    }
    bool same(TreeNode* t1, TreeNode* t2) {
        if (t1 == NULL && t2 == NULL) return true;
        if (t1 == NULL && t2 != NULL) return false;
        if (t1 != NULL && t2 == NULL) return false;
        if (t1->val != t2->val) return false;
        return same(t1->left, t2->left) && same(t1->right, t2->right);
    }
};

14,所有的满二叉树

来源:894. 所有可能的满二叉树

思路:回溯思想,每次在叶子节点上创建其左右子树。

递归终止条件:节点数大于等于n时退出,等于n时输出,输出需要拷贝整棵树。

所有可能情况:当前所有的叶子节点上依次尝试,这里所有叶节点并不准确,假如当前节点如下,在节点3挂载左右子树后,节点2虽然是叶子节点,也不该去尝试左右子树,应该被跳过,所以参数k就起到这个作用。

class Solution {
public:
    vector<TreeNode*> result;
    vector<TreeNode*> path; // 第1个节点为树根节点
    vector<TreeNode*> allPossibleFBT(int n) {
        TreeNode *root = new TreeNode(0);
        path.push_back(root);
        create(1, 0, n);
        return result;
    }
    void create(int start, int k, int n) {
        if (start > n) return;
        if (start == n) {
            result.push_back(copy(path[0]));
            return;
        }
        // 所有的叶子节点
        int sz = path.size();
        for (int i = k; i < sz; i++) {
            if (path[i]->left == NULL) {
                TreeNode *left = new TreeNode(0);
                TreeNode *right = new TreeNode(0);
                path[i]->left = left;
                path[i]->right = right;
                path.push_back(left);
                path.push_back(right);
                create(start+2, i+1, n); // 递归
                path[i]->left = NULL;
                path[i]->right = NULL;
                path.pop_back();
                path.pop_back();
            }
        }
    }

    TreeNode* copy(TreeNode* root) {
        if (root == NULL) return NULL;
        TreeNode *p = new TreeNode(root->val);
        p->left = copy(root->left);
        p->right = copy(root->right);
        return p;
    }
};

15,二叉树好节点数量

「好节点」X 定义为:从根到该节点 X 所经过的节点中,没有任何节点的值大于 X 的值。

来源:1448. 统计二叉树中好节点的数目

思路:递归传递最大值,当前节点不小于最大值,则是好节点。

class Solution {
public:
    int good;
    int goodNodes(TreeNode* root) {
        good = 0;
        visit(root, -999999);
        return good;
    }
    void visit(TreeNode *root, int max) {
        if (root == NULL) return;
        if (root->val >= max) good++;
        max = (max > root->val ? max : root->val);
        visit(root->left, max);
        visit(root->right, max);
    }
};

16,子树元素和

一个结点的「子树元素和」定义为以该结点为根的二叉树上所有结点的元素之和(包括结点本身)。

来源:508. 出现次数最多的子树元素和

思路:先计算每个节点的子树元素和,在找出频次最大值max,输出频次等于max的所有数字。

class Solution {
public:
    vector<int> nums;
    vector<int> findFrequentTreeSum(TreeNode* root) {
        if (root == NULL) return nums;
        sum(root);
        return most(nums);
    }
    int sum(TreeNode *root) {
        if (root->left == NULL && root->right == NULL) {
            nums.push_back(root->val);
            return root->val;
        }
        int s = root->val;
        if (root->left) s += sum(root->left);
        if (root->right) s += sum(root->right);
        nums.push_back(s);
        return s;
    }
    vector<int> most(const vector<int>& nums) {
        map<int, int> freq;
        for (int i = 0; i < nums.size(); i++) {
            map<int, int>::iterator it = freq.find(nums[i]);
            if (it == freq.end()) {
                freq[nums[i]] = 1;
            } else {
                it->second++;
            }
        }
        int max = 0;
        for (map<int, int>::iterator it = freq.begin(); it != freq.end(); it++) {
            if (it->second > max) max = it->second;
        }
        vector<int> result;
        for (map<int, int>::iterator it = freq.begin(); it != freq.end(); it++) {
            if (it->second == max) result.push_back(it->first);
        }
        return result;
    }
};

17,分配硬币

来源:979. 在二叉树中分配硬币

思路:从叶节点到根节点,硬币从下往上移动。当前节点大于1时,移动的硬币是正数,反之移动负数。稀里糊涂通过了。

class Solution {
public:
    int result;
    int distributeCoins(TreeNode* root) {
        result = 0;
        if (root->left) visit(root, root->left);
        if (root->right) visit(root, root->right);
        return result;
    }
    void visit(TreeNode *parent, TreeNode *curr) {
        if (curr->left == NULL && curr->right == NULL) {
            // 叶节点
            move(curr, parent);
            return;
        }
        if (curr->left) visit(curr, curr->left);
        if (curr->right) visit(curr, curr->right);
        move(curr, parent);
    }
    void move(TreeNode* from, TreeNode *to) {
        if (from->val != 1) {
            int d = from->val - 1;
            from->val -= d;
            to->val += d;
            if (d > 0) result += d;
            else result -= d;
        }
    }
};

18,翻转二叉树

一棵树X翻转一定次数后是否等价于树Y

来源:951. 翻转等价二叉树

思路:2个节点同时为NULL时返回true,同时不为NULL时递归调用翻转和不翻转的情况。

class Solution {
public:
    bool flipEquiv(TreeNode* root1, TreeNode* root2) {
        if (root1 == NULL) {
            if (root2 == NULL) return true;
            else return false;
        } else {
            if (root2 == NULL) return false;
            if (root1->val != root2->val) return false;
            return (flipEquiv(root1->left, root2->left) && flipEquiv(root1->right, root2->right)) ||
            (flipEquiv(root1->left, root2->right) && flipEquiv(root1->right, root2->left));
        }
    }
};

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值