102.二叉树的层序遍历
思路
需要借用一个辅助数据结构即队列来实现,队列先进先出,符合一层一层遍历的逻辑,而是用栈先进后出适合模拟深度优先遍历也就是递归的逻辑。而这种层序遍历方式就是图论中的广度优先遍历,只不过我们应用在二叉树上。
使用队列实现二叉树广度优先遍历,动画如下:
c++实现
class Solution102 {
public:
vector<vector<int>> levelOrder(TreeNode* root) {
queue<TreeNode *> que;
vector<vector<int>> results;
if(root != nullptr)
que.push(root);
while (!que.empty()){
int size = que.size();
vector<int> result;
while (size--){
// 保存结点
TreeNode * node = que.front();
// 弹出结点
que.pop();
// 保存结点值
result.push_back(node->val);
if(node->left != nullptr)
que.push(node->left);
if(node->right != nullptr)
que.push(node->right);
// 保存当前层
}
results.push_back(result);
}
return results;
}
};
python实现
class Solution102:
def levelOrder(self, root: TreeNode) -> List[List[int]]:
results = []
if root is None:
return results
que = deque([root])
while len(que) > 0:
size = len(que)
result = []
for _ in range(size):
# 弹出结点
cur = que.popleft()
# 保存结点结点值
result.append(cur.val)
if cur.left:
# 添加左节点
que.append(cur.left)
if cur.right:
# 添加右节点
que.append(cur.right)
results.append(result)
226.翻转二叉树
翻转的实现:每一个节点的左右孩子交换一下就可以了
方法:这道题目使用前序遍历和后序遍历都可以,唯独中序遍历不方便,因为中序遍历会把某些节点的左右孩子翻转了两次!建议拿纸画一画,就理解了
具体步骤
- 确定递归函数的参数和返回值
参数就是要传入节点的指针,不需要其他参数了,通常此时定下来主要参数,如果在写递归的逻辑中发现还需要其他参数的时候,随时补充。
返回值的话其实也不需要,但是题目中给出的要返回root节点的指针,可以直接使用题目定义好的函数,所以就函数的返回类型为TreeNode*。
TreeNode* invertTree(TreeNode* root)
- 确定终止条件
当前节点为空的时候,就返回
if (root == NULL) return root;
- 确定单层递归的逻辑
因为是先前序遍历,所以先进行交换左右孩子节点,然后反转左子树,反转右子树。
swap(root->left, root->right);
invertTree(root->left);
invertTree(root->right);
C++实现
class Solution {
public:
TreeNode* invertTree(TreeNode* root) {
if (root == NULL) return root;
swap(root->left, root->right); // 中
invertTree(root->left); // 左
invertTree(root->right); // 右
return root;
}
};
python实现
class Solution226:
def invertTree(self, root: Optional[TreeNode]) -> Optional[TreeNode]:
if not root:
return root
root.left, root.right = root.right, root.left
self.invertTree(root.left)
self.invertTree(root.right)
return root
101.对称二叉树
思路
- 判断清楚要不比较那两个结点,要比较的可不是左右节点!
- 对于二叉树是否对称,要比较的是根节点的左子树与右子树是不是相互翻转的,理解这一点就知道了其实我们要比较的是两个树(这两个树是根节点的左右子树),所以在递归遍历的过程中,也是要同时遍历两棵树。
Note: 本题遍历只能是“后序遍历”,因为我们要通过递归函数的返回值来判断两个子树的内侧节点和外侧节点是否相等。
正是因为要遍历两棵树而且要比较内侧和外侧节点,所以准确的来说是一个树的遍历顺序是左右中,一个树的遍历顺序是右左中。
递归法
递归三部曲
- 确定递归函数的参数和返回值
因为我们要比较的是根节点的两个子树是否是相互翻转的,进而判断这个树是不是对称树,所以要比较的是两个树,参数自然也是左子树节点和右子树节点。返回值自然是bool类型。
bool compare(TreeNode* left, TreeNode* right)
- 确定终止条件
要比较两个节点数值相不相同,首先要把两个节点为空的情况弄清楚!否则后面比较数值的时候就会操作空指针了。
节点为空的情况有:注意我们比较的其实不是左孩子和右孩子,所以如下我称之为左节点右节点)
-
左节点为空,右节点不为空,不对称,return false
-
左不为空,右为空,不对称 return false
-
左右都为空,对称,返回true
此时已经排除掉了节点为空的情况,那么剩下的就是左右节点不为空: -
左右都不为空,比较节点数值,不相同就return false
if (left == NULL && right != NULL) return false;
else if (left != NULL && right == NULL) return false;
else if (left == NULL && right == NULL) return true;
else if (left->val != right->val) return false; // 注意这里我没有使用else
注意:没有使用else,而是elseif, 因为我们把以上情况都排除之后,剩下的就是 左右节点都不为空,且数值相同的情况。
3. 确定单层递归的逻辑
此时才进入单层递归的逻辑,单层递归的逻辑就是处理 左右节点都不为空,且数值相同的情况。
- 比较二叉树外侧是否对称:传入的是左节点的左孩子,右节点的右孩子。
- 比较内测是否对称,传入左节点的右孩子,右节点的左孩子。
- 如果左右都对称就返回true ,有一侧不对称就返回false 。
bool outside = compare(left->left, right->right); // 左子树:左、 右子树:右
bool inside = compare(left->right, right->left); // 左子树:右、 右子树:左
bool isSame = outside && inside; // 左子树:中、 右子树:中(逻辑处理)
return isSame;
如上代码中,我们可以看出使用的遍历方式,左子树左右中,右子树右左中,所以我把这个遍历顺序也称之为“后序遍历”(尽管不是严格的后序遍历)。
c++实现
class Solution101 {
public:
bool compare(TreeNode *left, TreeNode *right){
// 首先排除空节点的情况
if(left == nullptr && right != nullptr)
return false;
else if(left != nullptr && right == nullptr)
return false;
else if(left == nullptr && right == nullptr)
return true;
// 排除了空节点,再排除数值不相同的情况
else if(left->val != right->val)
return false;
bool outside = compare(left->left, right->right);// 左子树:左、 右子树:右
bool inside = compare(left->right, right->left);// 左子树:右、 右子树:左
bool result = outside && inside; // 左子树:中、 右子树:中 (逻辑处理)
return result;
}
bool isSymmetric(TreeNode* root) {
if(root == nullptr)
return true;
return compare(root->left, root->right);
}
};
Python实现
class Solution:
def isSymmetric(self, root: TreeNode) -> bool:
if not root:
return True
return self.compare(root.left, root.right)
def compare(self, left, right):
#首先排除空节点的情况
if left == None and right != None: return False
elif left != None and right == None: return False
elif left == None and right == None: return True
#排除了空节点,再排除数值不相同的情况
elif left.val != right.val: return False
#此时就是:左右节点都不为空,且数值相同的情况
#此时才做递归,做下一层的判断
outside = self.compare(left.left, right.right) #左子树:左、 右子树:右
inside = self.compare(left.right, right.left) #左子树:右、 右子树:左
isSame = outside and inside #左子树:中、 右子树:中 (逻辑处理)
return isSame