题目链接
代码随想录文章讲解链接
用时:7m29s
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
vector<int> largestValues(TreeNode* root) {
queue<TreeNode*> que;
vector<int> res;
if (root == nullptr) return res;
que.push(root);
while (!que.empty()) {
int maxNum = INT_MIN;
int size = que.size();
for (int i = 0; i < size; ++i) {
TreeNode* node = que.front();
que.pop();
if (node->val > maxNum) maxNum = node->val;
if (node->left) que.push(node->left);
if (node->right) que.push(node->right);
}
res.push_back(maxNum);
}
return res;
}
};
无。
无。
题目链接
代码随想录文章讲解链接
用时:8m48s
BFS。
/*
// Definition for a Node.
class Node {
public:
int val;
Node* left;
Node* right;
Node* next;
Node() : val(0), left(NULL), right(NULL), next(NULL) {}
Node(int _val) : val(_val), left(NULL), right(NULL), next(NULL) {}
Node(int _val, Node* _left, Node* _right, Node* _next)
: val(_val), left(_left), right(_right), next(_next) {}
};
*/
class Solution {
public:
Node* connect(Node* root) {
queue<Node*> que;
if (root == NULL) return root;
que.push(root);
while (!que.empty()) {
int size = que.size();
for (int i = 0; i < size; ++i) {
Node* node = que.front();
que.pop();
if (i < size - 1) node->next = que.front();
if (node->left) que.push(node->left);
if (node->right) que.push(node->right);
}
}
return root;
}
};
从上往下遍历,利用父节点一定是通过next相连这一特性。
记录当前层最左边的节点leftMost
,用于进入下一层。
利用next指针,从左向右遍历当前层的节点,将下一层节点从左向右连起来:
node->left->next = node->right; // 左节点指向右节点
node->right->next = node->next->left; // 右节点指向父节点的next节点的左节点
遍历完当前层后,利用leftMost进入下一层:
leftMost = leftMost->left;
从第一层开始,将第二层所有元素通过next相连,进入第二层后,因为第二层已经连接好了,所以就可以利用第二层来连接第三层,一直迭代到最后一层。
class Solution {
public:
Node* connect(Node* root) {
if (root == NULL) return root;
Node* leftMost = root;
while (leftMost->left != NULL) {
Node* node = leftMost;
while (node != NULL) {
node->left->next = node->right;
if (node->next != NULL) node->right->next = node->next->left;
node = node->next;
}
leftMost = leftMost->left;
}
return root;
}
};
方法二好巧妙。
无。
题目链接
代码随想录文章讲解链接
用时:5m58s
如果是空节点则最大深度为0,否则返回左子节点的最大深度和右子节点的最大深度之间的最大值加1(加上当前节点)。
class Solution {
public:
int maxDepth(TreeNode* root) {
return root == nullptr ? 0 : max(maxDepth(root->left), maxDepth(root->right)) + 1;
}
};
懒得敲了,这两天二叉树的BFS敲了N次了都。
原来苯人也能写出这么简洁的代码^^
无。
题目链接
用时:3m6s
同上一题。
/*
// Definition for a Node.
class Node {
public:
int val;
vector children;
Node() {}
Node(int _val) {
val = _val;
}
Node(int _val, vector _children) {
val = _val;
children = _children;
}
};
*/
class Solution {
public:
int maxDepth(Node* root) {
if (root == nullptr) return 0;
int maxD = 0;
for (Node*& child : root->children) maxD = max(maxD, maxDepth(child));
return maxD + 1;
}
};
无。
无。
题目链接
代码随想录文章讲解链接
用时:6m4s
若节点为空,则最小深度为0;
若节点没有左节点,则最小深度为右节点的最小深度加1;
若节点没有右节点,则最小深度为左节点的最小深度加1;
若节点有左右节点,则最小深度为左节点的最小深度和右节点的最小深度之间的最小值加1。
class Solution {
public:
int minDepth(TreeNode* root) {
if (root == nullptr) return 0;
else if (root->left == nullptr) return minDepth(root->right) + 1;
else if (root->right == nullptr) return minDepth(root->left) + 1;
else return min(minDepth(root->left), minDepth(root->right)) + 1;
}
};
无。
无。
题目链接
代码随想录文章讲解链接
用时:9m5s
BFS遍历所有节点,统计节点个数。
class Solution {
public:
int countNodes(TreeNode* root) {
queue<TreeNode*> que;
int num = 0;
if (root == nullptr) return num;
que.push(root);
while (!que.empty()) {
int size = que.size();
num += size;
for (int i = 0; i < size; ++i) {
TreeNode* node = que.front();
if (node->left) que.push(node->left);
if (node->right) que.push(node->right);
que.pop();
}
}
return num;
}
};
class Solution {
public:
int countNodes(TreeNode* root) {
return root == nullptr ? 0 : countNodes(root->left) + countNodes(root->right) + 1;
}
};
方法一、二本质上都是遍历所有节点来统计节点数,由于本题的二叉树是完全二叉树,所以可以利用完全二叉树的性质来优化算法。
利用以上两点性质,我们可以用优于 O ( n ) O(n) O(n)的时间复杂度完成。
首先利用性质1判断以当前节点作为根节点的二叉树是不是完美二叉树,若是的话,则直接能够计算出当前二叉树的节点数。
如当前的二叉树不是完美二叉树,则递归判断当前节点的左右节点是否是完美二叉树,当前的二叉树的节点数为:左子树节点数+右子树节点数+1。
class Solution {
public:
int countNodes(TreeNode* root) {
if (root == nullptr) return 0;
int leftDepth = 1;
int rightDepth = 1;
TreeNode* leftNode = root;
TreeNode* rightNode = root;
while (leftNode->left) {
++leftDepth;
leftNode = leftNode->left;
}
while (rightNode->right) {
++rightDepth;
rightNode = rightNode->right;
}
if (leftDepth == rightDepth) return pow(2, leftDepth) - 1;
else return countNodes(root->left) + countNodes(root->right) + 1;
}
};
若一棵完全二叉树的深度为 d d d,给每个节点从1开始编号,则它的节点数一定位于 [ 2 d − 1 , 2 d − 1 ] [2^{d-1},2^d-1] [2d−1,2d−1]区间。对于 [ 2 d − 1 , 2 d − 1 ] [2^{d-1},2^d-1] [2d−1,2d−1]区间,可以使用二分查找来获取最后一个节点的位置编号,即节点数。
二分查找:
左边界为 2 d − 1 2^{d-1} 2d−1,右边界为 2 d − 1 2^d-1 2d−1,每次判断中间位置的节点是否存在,然后更新左右边界的位置,直至两边界重合,找到最后一个节点的位置,位置的编号即为二叉树节点的个数。
判断某个节点是否存在:
对于一棵完全二叉树,每个节点可以用二进制编码。根节点从1开始,如图所示,向左走新增的位就是0,向右走新增的位就是1。
根据以上性质,要判断第k个节点是否存在,将k转化成二进制编码,然后根据编码可以从根节点出发到达第k个节点,若第k个节点为空,则不存在。例如:对于节点10,二进制编码为1010,第一位1是根节点,第二位0表示向左走,第三位1表示向右走,第四位0表示向左走,即可到达节点10。
使用位运算实现:
获取二进制编码第n个位上的数字可以使用位运算实现。例如:对于四位编码1010,想获取第3位的数字,就将该编码和0010进行与运算,相与的结果为0则说明第3位的数字为0,相与的结果不为0,则说明第3位的数字为1,1010 & 0010 = 0010,结果不为0,说明1010的第3位为1。
设置一个变量check
,用于获取编码某一位的数字,check
初始化为1 << (depth - 2)
,例如:若二叉树有3层,check初始化为010(1左移3-1=1位得到10,即010),因为第一位默认是1(根节点),所以从第2位开始获取(check第2位为1);同理,若二叉树有4层,初始化为0100。
获取到的编码的对应位数字后,若数字为0则节点向左走,若为1则节点向右走。
时间复杂度: O ( log 2 n ) O(\log^2n) O(log2n), n n n为树的节点数。二分查找时间复杂度为 O ( log n ) O(\log n) O(logn),判断节点存不存在的时间复杂度为 O ( log n ) O(\log n) O(logn)(树的深度)。
空间复杂度: O ( 1 ) O(1) O(1)。
class Solution {
public:
int countNodes(TreeNode* root) {
if (root == nullptr) return 0;
if (root->left == nullptr) return 1;
// 二叉树的最大深度
int depth = 0;
for(TreeNode* node = root; node; node = node->left) ++depth;
// 二分查找
int left = pow(2, depth - 1);
int right = pow(2, depth) - 1;
while (left <= right) {
int mid = left + (right - left) / 2;
if (exists(root, depth, mid)) left = mid + 1;
else right = mid - 1;
}
return right;
}
bool exists(TreeNode* root, int depth, int k) {
/*
root:二叉树的根节点
depth:二叉树的最大深度
k:检查编号为k的节点是否存在
*/
cout << k << endl;
int check = 1 << (depth - 2); // 01000...
TreeNode* node = root;
while (check && node) {
if (check & k) node = node->right; // 当前编码为1,则向右走
else node = node->left; // 当前编码为0,则向左走
check >>= 1; // 将1不断右移,逐位遍历
}
return node != nullptr;
}
};
本以为方法三已经够妙了,方法四…
无。
题目链接
代码随想录文章讲解链接
对于一个根节点,只有满足以下两个条件之一,该根节点对应的二叉树才是平衡二叉树:
对于左右子树,可以递归判断左右子树是否是平衡二叉树。获取左右子树的高度如第104e题。
class Solution {
public:
bool isBalanced(TreeNode* root) {
return root == nullptr || (abs(height(root->left) - height(root->right)) <= 1 && isBalanced(root->left) && isBalanced(root->right)) ? true : false;
}
int height(TreeNode* node) {
return node == nullptr ? 0 : max(height(node->left), height(node->right)) + 1;
}
};
用时:
方法一存在可以优化的地方,在方法一中每个节点都要多次调用height函数计算高度,在递归当中嵌套了递归,可以将计算高度和判断是否是平衡二叉树两个递归用一个递归来实现。
当二叉树是一课平衡二叉树,我们就返回它的高度,当它不是平衡二叉树时,我们就返回-1。对于一个节点,先计算其左子树的高度,若左子树高度为-1,则表示左子树不是平衡二叉树,那么就没有必要计算右子树的了,直接返回-1;计算右子树高度时同理。
所以最后我们只需判断该二叉树的高度是否为-1,不是-1就说明它是一棵平衡二叉树。
class Solution {
public:
bool isBalanced(TreeNode* root) {
return height(root) == -1 ? false : true;
}
int height(TreeNode* node) {
if (node == nullptr) return 0;
int leftH = height(node->left);
if (leftH == -1) return -1;
int rightH = height(node->right);
if (rightH == -1) return -1;
return abs(leftH - rightH) > 1 ? -1 : max(leftH, rightH) + 1;
}
};
无。
无。
二叉树越刷越熟了。