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,构建最小高度树
思路:使用折半查找的方法创建二叉搜索树,保证高度最小。
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,根据二叉树创建字符串
思路:前序遍历,难度不大,注意小括号,左右子树分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,二叉树的堂兄弟
思路:二者深度相同且父节点不同。
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小节点
思路:根节点为第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,二叉搜索树转单链表
思路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,找出克隆二叉树中的相同节点
思路:在原树中利用回溯的思想找到目标节点,并记录路径,在克隆树上根据路径找到对应位置。目标节点的判断: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,祖父节点
思路:凡是判断祖父、父节点的题,在递归的参数中将他们带上即可。
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,每层构造单链表
思路:层序遍历的同时创建单链表
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,删除给定值的所有叶子节点
思路:递归删除,如果当前节点等于给定值,并且整树都是给定值,则全部删除。
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,查找子树
思路:递归遍历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,所有的满二叉树
思路:回溯思想,每次在叶子节点上创建其左右子树。
递归终止条件:节点数大于等于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 的值。
思路:递归传递最大值,当前节点不小于最大值,则是好节点。
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,子树元素和
一个结点的「子树元素和」定义为以该结点为根的二叉树上所有结点的元素之和(包括结点本身)。
思路:先计算每个节点的子树元素和,在找出频次最大值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,分配硬币
思路:从叶节点到根节点,硬币从下往上移动。当前节点大于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));
}
}
};