title: 每日一練(16):對稱的二叉樹
categories:[劍指offer]
tags:[每日一練]
date: 2022/02/11
每日一練(16):對稱二叉樹
請實現一個函式,用來判斷一棵二叉樹是不是對稱的。如果一棵二叉樹和它的映象一樣,那麼它是對稱的。
例如,二叉樹 [1,2,2,3,4,4,3] 是對稱的。
1
/ \
2 2
/ \ / \
3 4 4 3
但是下面這個 [1,2,2,null,3,null,3] 則不是映象對稱的:
1
/ \
2 2
\ \
3 3
示例 1:
輸入:root = [1,2,2,3,4,4,3]
輸出:true
示例 2:
輸入:root = [1,2,2,null,3,null,3]
輸出:false
限制:
0 <= 節點個數 <= 1000
來源:力扣(LeetCode)
連結:https://leetcode-cn.com/probl...
方法出處: 程式碼隨想錄
方法一:遞迴
遞迴三部曲
- 確定遞迴函式的引數和返回值
因為我們要比較的是根節點的兩個子樹是否是相互翻轉的,進而判斷這個樹是不是對稱樹,所以要比較的是兩個樹,引數自然也是左子樹節點和右子樹節點。
返回值自然是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;
}
- 確定單層遞迴的邏輯
此時才進入單層遞迴的邏輯,單層遞迴的邏輯就是處理 右節點都不為空,且數值相同的情況。
- 比較二叉樹外側是否對稱:傳入的是左節點的左孩子,右節點的右孩子。
- 比較內測是否對稱,傳入左節點的右孩子,右節點的左孩子。
- 如果左右都對稱就返回true ,有一側不對稱就返回false 。
程式碼:
bool outside = compare(left->left, right->right); // 左子樹:左、 右子樹:右
bool inside = compare(left->right, right->left); // 左子樹:右、 右子樹:左
bool isSame = outside && inside; // 左子樹:中、 右子樹:中(邏輯處理)
return isSame;
最終整體C++程式碼:
//1
bool compare(TreeNode* left, TreeNode* right) {
// 首先排除空節點的情況
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;
}
// 此時就是:左右節點都不為空,且數值相同的情況
// 此時才做遞迴,做下一層的判斷
bool outside = compare(left->left, right->right); // 左子樹:左、 右子樹:右
bool inside = compare(left->right, right->left); // 左子樹:右、 右子樹:左
bool isSame = outside && inside; // 左子樹:中、 右子樹:中 (邏輯處理)
return isSame;
}
bool isSymmetric(TreeNode* root) {
if (root == NULL) {
return true;
}
return compare(root->left, root->right);
}
//2
bool compare(TreeNode* left, TreeNode* right) {
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 {
return compare(left->left, right->right) && compare(left->right, right->left);
}
}
bool isSymmetric(TreeNode* root) {
if (root == NULL) {
return true;
}
return compare(root->left, root->right);
}
方法二:迭代(棧/佇列)
使用佇列來比較兩個樹(根節點的左右子樹)是否相互翻轉,邏輯和遞迴是一樣的
判斷根節點的左子樹和右子樹的內側和外側是否相等
// 迭代
// 佇列
bool isSymmetric(TreeNode* root) {
if (root == NULL) {
return true;
}
queue<TreeNode*> que;
que.push(root->left); // 將左子樹頭結點加入佇列
que.push(root->right); // 將右子樹頭結點加入佇列
while (!que.empty()) { // 接下來就要判斷這這兩個樹是否相互翻轉
TreeNode* leftNode = que.front(); que.pop();
TreeNode* rightNode = que.front(); que.pop();
if (!leftNode && !rightNode) { // 左節點為空、右節點為空,此時說明是對稱的
continue;
}
// 左右一個節點不為空,或者都不為空但數值不相同,返回false
if ((!leftNode || !rightNode || (leftNode->val != rightNode->val))) {
return false;
}
que.push(leftNode->left); // 加入左節點左孩子
que.push(rightNode->right); // 加入右節點右孩子
que.push(leftNode->right); // 加入左節點右孩子
que.push(rightNode->left); // 加入右節點左孩子
}
return true;
}
// 棧
bool isSymmetric(TreeNode* root) {
if (root == NULL) {
return true;
}
stack<TreeNode*> st; // 這裡改成了棧
st.push(root->left);
st.push(root->right);
while (!st.empty()) {
TreeNode* leftNode = st.top(); st.pop();
TreeNode* rightNode = st.top(); st.pop();
if (!leftNode && !rightNode) {
continue;
}
if ((!leftNode || !rightNode || (leftNode->val != rightNode->val))) {
return false;
}
st.push(leftNode->left);
st.push(rightNode->right);
st.push(leftNode->right);
st.push(rightNode->left);
}
return true;
}