Binary Tree
中序遍历 (In-order)¶
Recursion¶
vector<int> inorderTraversal(TreeNode* root) {
vector<int> result;
if (root == NULL) return result;
result = inorderTraversal(root->left);
result.push_back(root->val);
vector<int> right = inorderTraversal(root->right);
for (auto i : right) result.push_back(i);
return result;
}
Stack¶
vector<int> inorderTraversal(TreeNode* root) {
vector<int> result;
if (root == nullptr) return result;
stack<TreeNode*> s;
s.push(root);
while (!s.empty()) {
if (s.top()->left != nullptr) s.push(s.top()->left);
else {
while (!s.empty()) {
TreeNode* node = s.top();
result.push_back(node->val);
s.pop();
if (node->right != nullptr) {
s.push(node->right);
break;
}
}
}
}
return result;
}
下面这个写法感觉更简单些
就是同时维持一个ptr来帮助访问
vector<int> inorderTraversal(TreeNode *root) {
stack<TreeNode*> s;
vector<int> v;
TreeNode *ptr = root;
while (ptr != nullptr || !s.empty()) {
while (ptr != nullptr) {
s.push(ptr);
ptr = ptr->left;
}
if (!s.empty()) {
ptr = s.top();
s.pop();
v.push_back(ptr->val);
ptr = ptr->right;
}
}
return v;
}
Morris¶
只用O(1)空间复杂度
思路是从root出发,将一个节点的前继节点的右指针指向它
流程如下
- 如果一个点左孩子为空,那么将这个节点输出,然后访问其右节点
- 左孩子不为空,从左孩子开始一路向右,即访问当前节点的前继节点
- 如果前继节点右孩子为空,那么指向当前节点,然后访问当前节点左孩子
- 前继节点右孩子指向当前节点,说明左侧子树已经全部访问过,那么输出当前节点,然后访问当前节点的右孩子
算法时间复杂度为O(N),因为每条边访问次数是常数,如修改前继节点的右孩子为两次,输出自己为一次,而二叉树边数目为N-1
vector<int> inorderTraversal(TreeNode* root) {
TreeNode *node = root, *prev = nullptr;
vector<int> v;
while (node != nullptr) {
if (node->left == nullptr) {
v.push_back(node->val);
node = node->right;
} else {
TreeNode *prev = node->left;
while (prev->right != nullptr && prev->right != node) prev = prev->right;
if (prev->right == nullptr) {
prev->right = node;
node = node->left;
} else {
prev->right = nullptr;
v.push_back(node->val);
node = node->right;
}
}
}
return v;
}
前序遍历 (Pre-order)¶
Recursion¶
vector<int> preorderTraversal(TreeNode* root) {
vector<int> v;
if (root == nullptr) return v;
v.push_back(root->val);
vector<int> l = preorderTraversal(root->left);
for (auto n : l) v.push_back(n);
vector<int> r = preorderTraversal(root->right);
for (auto n : r) v.push_back(n);
return v;
}
Stack¶
同样是这种写法,借助一个ptr后简单了很多,只需要记录一下之前入栈的元素,可以回溯即可
vector<int> preorderTraversal(TreeNode* root) {
vector<int> v;
stack<TreeNode*> s;
TreeNode *ptr = root;
while (ptr != nullptr || !s.empty()) {
while (ptr != nullptr) {
v.push_back(ptr->val);
s.push(ptr);
ptr = ptr->left;
}
if (!s.empty()) {
ptr = s.top();
s.pop();
ptr = ptr->right;
}
}
return v;
}
这是一种更简单的写法
vector<int> preorderTraversal(TreeNode* root) {
vector<int> v;
if (root == nullptr) return v;
stack<TreeNode*> s;
s.push(root);
while (!s.empty()) {
TreeNode *node = s.top();
s.pop();
v.push_back(node->val);
if (node->right != nullptr) s.push(node->right);
if (node->left != nullptr) s.push(node->left);
}
return v;
}
Morris¶
流程和in-order的morris一样,区别就在于第二个push_back(cur->val)的位置变一下
vector<int> preorderTraversal(TreeNode* root) {
vector<int> v;
TreeNode *cur = root, *prev = nullptr;
while (cur != nullptr) {
if (cur->left == nullptr) {
v.push_back(cur->val);
cur = cur->right;
} else {
prev = cur->left;
while (prev->right != nullptr && prev->right != cur) prev = prev->right;
if (prev->right == nullptr) {
prev->right = cur;
v.push_back(cur->val);
cur = cur->left;
} else {
prev->right = nullptr;
cur = cur->right;
}
}
}
return v;
}
后序遍历 (Post-order)¶
Recursion¶
vector<int> postorderTraversal(TreeNode* root) {
if (root == nullptr) return {};
vector<int> l = postorderTraversal(root->left);
vector<int> r = postorderTraversal(root->right);
for (auto n : r) l.push_back(n);
l.push_back(root->val);
return l;
}
Stack¶
后序遍历比较麻烦,因为要保证子节点都访问完了才行
这里用一个prev节点来记录前一个输出的节点,如果是当前节点的孩子节点则当前节点也可以输出
vector<int> postorderTraversal(TreeNode* root) {
vector<int> v;
if (root == nullptr) return v;
stack<TreeNode*> s;
s.push(root);
TreeNode *prev = root;
while (!s.empty()) {
TreeNode *ptr = s.top();
if ((ptr->left == nullptr && ptr->right == nullptr) ||
(ptr->right == nullptr && ptr->left == prev) ||
(ptr->right == prev)) {
v.push_back(ptr->val);
s.pop();
prev = ptr;
} else {
if (ptr->right != nullptr) s.push(ptr->right);
if (ptr->left != nullptr) s.push(ptr->left);
}
}
return v;
}
Morris¶
后序遍历morris流程还是和之前一样,区别在于
在第二次发现回环时,倒序进行输出
而这样从根节点开始一路向右的一列无法输出,在结束后专门进行输出
void reverse(TreeNode* begin, TreeNode* end) {
if (begin == end) return;
TreeNode *prev = nullptr, *cur = begin;
while (prev != end) {
TreeNode *next = cur->right;
cur->right = prev;
prev = cur;
cur = next;
}
}
vector<int> postorderTraversal(TreeNode* root) {
vector<int> v;
TreeNode *ptr = root;
while (ptr != nullptr) {
if (ptr->left == nullptr) ptr = ptr->right;
else {
TreeNode *prev = ptr->left;
while (prev->right != nullptr && prev->right != ptr) prev = prev->right;
if (prev->right == nullptr) {
prev->right = ptr;
ptr = ptr->left;
} else {
reverse(ptr->left, prev);
for (TreeNode* temp = prev; temp != ptr->left; temp = temp->right)
v.push_back(temp->val);
v.push_back(ptr->left->val);
reverse(prev, ptr->left);
prev->right = nullptr;
ptr = ptr->right;
}
}
}
if (root != nullptr) {
TreeNode* temp = root;
while (temp->right != nullptr) temp = temp->right;
reverse(root, temp);
for (ptr = temp; ptr != root; ptr = ptr->right) v.push_back(ptr->val);
v.push_back(root->val);
reverse(temp, root);
}
return v;
}
层次遍历¶
BST¶
vector<vector<int>> levelOrder(TreeNode* root) {
vector<vector<int> > result;
if (!root) return result;
deque<TreeNode*> q;
q.push_back(root);
while (!q.empty()) {
result.push_back(vector<int>());
int size = q.size();
while (size --) {
TreeNode* node = q.front();
if (node->left != NULL) q.push_back(node->left);
if (node->right != NULL) q.push_back(node->right);
q.pop_front();
result.back().push_back(node->val);
}
}
return result;
}
高度¶
BST¶
int maxDepth(TreeNode* root) {
if (root == NULL) return 0;
deque<TreeNode*> q;
q.push_back(root);
int result = 0;
while (!q.empty()) {
result ++;
int size = q.size();
while (size --) {
TreeNode* node = q.front();
if (node->left != NULL) q.push_back(node->left);
if (node->right != NULL) q.push_back(node->right);
q.pop_front();
}
}
return result;
}
Morris¶
思路就是维持一个depth变量,然后在第一次将前继叶子节点指回去的时候,就可以得到高度
同时在第二次进入循环后,要把高度减会到原来的值
以及,因为这样只有在路径包含左节点时才能计算出高度,因此需要单独计算一下最右边的路径长度,然后返回较大值
int maxDepth(TreeNode* root) {
if (root == nullptr) return 0;
int mx = 1, depth = 1;
for (TreeNode *cur = root, *prev = nullptr; cur != nullptr ;) {
if (cur->left == nullptr) {
cur = cur->right;
depth ++;
} else {
prev = cur->left;
int temp = 1;
while (prev->right != nullptr && prev->right != cur) {
prev = prev->right;
temp ++;
}
if (prev->right == nullptr) {
prev->right = cur;
cur = cur->left;
mx = max(mx, temp + depth ++);
} else {
prev->right = NULL;
depth = depth - temp;
cur = cur->right;
}
}
}
int rightPath = 0;
for (TreeNode *ptr = root; ptr != nullptr; ptr = ptr->right) rightPath ++;
return max(mx, rightPath);
}