102. 二叉树的层序遍历

https://leetcode.cn/problems/binary-tree-level-order-traversal/solutions/2774207/102-er-cha-shu-de-ceng-xu-bian-li-by-jok-9c45

226. 翻转二叉树

https://leetcode.cn/problems/invert-binary-tree/solutions/2774554/226-fan-zhuan-er-cha-shu-by-joker-ek4-c5xs

递归写法

前序

1
2
3
4
5
6
7
8
9
10
class Solution {
public:
TreeNode *invertTree(TreeNode *root) {
if(root == NULL) return NULL;
swap(root->left, root->right);
invertTree(root->left);
invertTree(root->right);
return root;
}
};

后序

1
2
3
4
5
6
7
8
9
10
class Solution {
public:
TreeNode *invertTree(TreeNode *root) {
if(root == NULL) return NULL;
invertTree(root->left);
invertTree(root->right);
swap(root->left, root->right);
return root;
}
};

中序

1
2
3
4
5
6
7
8
9
10
class Solution {
public:
TreeNode *invertTree(TreeNode *root) {
if(root == NULL) return NULL;
invertTree(root->left);
swap(root->left, root->right);
invertTree(root->left);
return root;
}
};

迭代前后中非统一写法记不清楚的,不实用

前序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Solution {
public:
TreeNode *invertTree(TreeNode *root) {
stack<TreeNode *> st;
if (root != NULL) st.push(root);

while (!st.empty()) {
TreeNode *node = st.top();
st.pop();
swap(node->left, node->right);//中
if (node->right) st.push(node->right);//右

if (node->left) st.push(node->left);//左
}
return root;
}
};

后序这个不是本题的题解,只是一个遍历顺序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Solution {
public:
vector<int> postorderTraversal(TreeNode* root) {
stack<TreeNode*> st;
vector<int> result;
if (root == NULL) return result;
st.push(root);
while (!st.empty()) {
TreeNode* node = st.top();
st.pop();
result.push_back(node->val);
if (node->left) st.push(node->left); // 相对于前序遍历,这更改一下入栈顺序 (空节点不入栈)
if (node->right) st.push(node->right); // 空节点不入栈
}
reverse(result.begin(), result.end()); // 将结果反转之后就是左右中的顺序了
return result;
}
};

中序这个不是本题的题解,只是一个遍历顺序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Solution {
public:
vector<int> inorderTraversal(TreeNode* root) {
vector<int> result;
stack<TreeNode*> st;
TreeNode* cur = root;
while (cur != NULL || !st.empty()) {
if (cur != NULL) { // 指针来访问节点,访问到最底层
st.push(cur); // 将访问的节点放进栈
cur = cur->left; // 左
} else {
cur = st.top(); // 从栈里弹出的数据,就是要处理的数据(放进result数组里的数据)
st.pop();
result.push_back(cur->val); // 中
cur = cur->right; // 右
}
}
return result;
}
};

迭代前中后统一写法

noderoot的区别

同时最最重要的,递归里面root是遍历所有节点的,相当于cur。

但是迭代里面root就是根节点,就处理一次。然后就用node结点从栈或者队列里面取元素了。相当于node是遍历所有节点,相当于cur。

因此进左右子树的时候,递归是这样子的,用的root.leftroot.right

1
2
invertTree(root->left);         // 左
invertTree(root->right); // 右

因此进左右子树的时候,迭代是这样子的,用的node.leftnode.right

1
2
if (node->right) st.push(node->right);  // 右
if (node->left) st.push(node->left); // 左

递归和迭代易混淆类似代码区别,判不判空

个人理解:递归里面第一句一般都是这句话,因为这是递归结束终止条件,因此空结点的判断都交给下一层来判断并返回。所以

1
if (root == NULL) return root;

所以进入下一层的时候允许空姐点进去,因为一进入立刻就返回,不会有影响。

1
2
invertTree(root->left);         // 左
invertTree(root->right); // 右

但是迭代不一样,无论你是用栈来迭代还是用队列来层序

都要先判断是否为空,不为空才能压入栈或者队列

用栈迭代前中后序遍历统一写法里面

对根节点的判断

1
if (root != NULL) st.push(root);

对左右结点的判断

1
2
if (node->right) st.push(node->right);  // 右
if (node->left) st.push(node->left); // 左

用队列层序遍历里面也适合迭代一样的,先判空

1
if (root != NULL) que.push(root);
1
2
if (node->left) que.push(node->left);
if (node->right) que.push(node->right);

前序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class Solution {
public:
TreeNode *invertTree(TreeNode *root) {
//前中后序遍历迭代统一写法
stack<TreeNode *> st;
if (root != NULL) st.push(root);
while (!st.empty()) {
TreeNode *node = st.top();
if (node != NULL) {
st.pop();
if (node->right) st.push(node->right);
if (node->left) st.push(node->left);
st.push(node);
st.push(NULL);
} else {
st.pop();
node = st.top();
st.pop();
swap(node->left, node->right);
}
}
return root;
}
};

后序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

class Solution {
public:
TreeNode *invertTree(TreeNode *root) {
//前中后序遍历迭代统一写法
stack<TreeNode *> st;
if (root != NULL) st.push(root);
while (!st.empty()) {
TreeNode *node = st.top();
if (node != NULL) {
st.pop();
st.push(node);
st.push(NULL);
if (node->right) st.push(node->right);
if (node->left) st.push(node->left);
} else {
st.pop();
node = st.top();
st.pop();
swap(node->left, node->right);
}
}
return root;
}
};

中序

但使用迭代方式统一写法的中序是可以的。为什么这个中序就是可以的呢,因为这是用栈来遍历,而不是靠指针来遍历,避免了递归法中翻转了两次的情况,

个人理解:

好像是因为递归返回的时候通过根节点到此时根节点的右子树,但是此时的,根节点的右子树是之前已经被处理过的根节点的左子树

而迭代不一样,迭代开始就访问了右,中,左结点。并且把他们的地址都放到了st栈里面。所以到处理左,中,右的时候,这个时候右结点的地址就是原来的右节点的地址,没有改变过。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class Solution {
public:
TreeNode *invertTree(TreeNode *root) {
//前中后序遍历迭代统一写法
stack<TreeNode *> st;
if (root != NULL) st.push(root);
while (!st.empty()) {
TreeNode *node = st.top();
if (node != NULL) {
st.pop();
if (node->right) st.push(node->right);
st.push(node);
st.push(NULL);
if (node->left) st.push(node->left);
} else {
st.pop();
node = st.top();
st.pop();
swap(node->left, node->right);
}
}
return root;
}
};

层序写法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Solution {
public:
TreeNode *invertTree(TreeNode *root) {
queue<TreeNode *> q;
if (root != NULL) q.push(root);
while (!q.empty()) {
int size = q.size();
for (int i = 0; i < size; i++) {
TreeNode *node = q.front();
q.pop();
swap(node->left, node->right);
if(node->left) q.push(node->left);
if(node->right) q.push(node->right);
}
}
return root;
}
};

101. 对称二叉树

这道题目想了一会,看到弹幕说用层序加栈类似括号匹配一般判断,但是自己还是不会。算是不管了

就记住carl的思路和讲解得了

个人经验总结:二叉树一般都是后序。一般收集孩子的信息,想上一层返回的都需要后续便利

递归写法

https://leetcode.cn/problems/symmetric-tree/solutions/2786926/101-dui-cheng-er-cha-shu-by-joker-ek4-1b2p

这道题目我们也可以使用迭代法,但要注意,这里的迭代法可不是前中后序的迭代写法,因为本题的本质是判断两个树是否是相互翻转的,其实已经不是所谓二叉树遍历的前中后序的关系了。

这里我们可以使用队列来比较两个树(根节点的左右子树)是否相互翻转,(注意这不是层序遍历

还有栈还有队列的写法。

明天再卡吧