tree traversal (樹的遍歷) - 前序遍歷 (preorder traversal) - 對稱二叉樹
symmetric [sɪ'metrɪk]:adj. 對稱的,勻稱的
1. 二叉搜索樹 (binary search tree) - 對稱二叉樹
- 如果所給的根節點爲空,那麼它是對稱二叉樹。
- 如果所給的根節點不爲空,當它的左子樹與右子樹對稱時,那麼它是對稱二叉樹。
- 如果左子樹的左孩子與右子樹的右孩子對稱,左子樹的右孩子與右子樹的左孩子對稱,那麼左子樹和右子樹對稱。
- 分析可以遞歸的點 (遞歸點):判斷左子樹與右子樹對稱的條件時,其跟兩樹的孩子的對稱情況有關係。函數 function_B (左樹, 右樹) 功能是返回是否對稱。
函數 function_B (左樹, 右樹)
:左樹節點值等於右樹節點值且函數function_B (左樹的左子樹, 右樹的右子樹)
,函數 function_B (左樹的右子樹, 右樹的左子樹)
均爲真才返回真。
1.1 遞歸
如果一個樹的左子樹與右子樹鏡像對稱,那麼這個樹是對稱的。
如果同時滿足下面的條件,兩個樹互爲鏡像:
- 它們的兩個根結點具有相同的值。
- 左樹的左節點 == 右樹的右節點
- 左樹的右節點 == 右樹的左節點
就像人站在鏡子前審視自己那樣,鏡中的反射與現實中的人具有相同的頭部,但反射的右臂對應於人的左臂,反之亦然。
- 時間複雜度:
O(n)
。遍歷整個輸入樹一次,總的運行時間爲O(n)
,其中n
是樹中結點的總數。 - 空間複雜度:遞歸調用的次數受樹的高度限制。在最糟糕情況下,樹是線性的,其高度爲
O(n)
。在最糟糕的情況下,由棧上的遞歸調用造成的空間複雜度爲O(n)
。
1.2 迭代
除了遞歸的方法外,也可以利用兩個棧 (stack) 進行迭代。兩個棧 (stack) 初始化爲 root->left
和 root->right
。每次提取兩個結點並比較它們的值。然後,將兩個結點的左右子結點按相反的順序插入棧 (stack) 中。當隊列爲空或者檢測到樹不對稱 (即從隊列中取出兩個不相等的連續結點) 時,該算法結束。
要判斷二叉樹是不是對稱,只需要訪問鏡像元素值是否相等,即對左右子樹做相反順序的遍歷即可。
根節點的左子樹進行前序遍歷 [DLR,首先訪問根結點,然後前序遍歷其左子樹,最後前序遍歷其右子樹],同時對根節點的右子樹進行 [DRL,首先訪問根結點,然後前序遍歷其右子樹,最後前序遍歷其左子樹] 遍歷。
首先查看左右子樹的當前節點,然後對於左子樹採用 DLR 方式遍歷的同時,對於右子樹採用 DRL 方式遍歷,這樣同時訪問的兩個結點是鏡像位置的兩個結點,若結點值不同,則不對稱。
2. 對稱二叉樹
給定一個二叉樹,檢查它是否是鏡像對稱的。
例如二叉樹 [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
遞歸和迭代兩種方法解決問題。
//============================================================================
// Name : preorder traversal
// Author : Yongqiang Cheng
// Version : Feb 22, 2020
// Copyright : Copyright (c) 2020 Yongqiang Cheng
// Description : Hello World in C++, Ansi-style
//============================================================================
/*
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* struct TreeNode *left;
* struct TreeNode *right;
* };
*/
bool isSymmetric(struct TreeNode* root)
{
int front_l = 0, front_r = 0;
struct TreeNode* STACK_L[512] = { NULL };
struct TreeNode* STACK_R[512] = { NULL };
struct TreeNode* pnode = NULL;
struct TreeNode* qnode = NULL;
if (NULL == root)
{
return true;
}
front_l = -1;
front_r = -1;
STACK_L[++front_l] = root->left;
STACK_R[++front_r] = root->right;
while (front_l >= 0)
{
pnode = STACK_L[front_l];
qnode = STACK_R[front_r];
front_l--;
front_r--;
if ((NULL == pnode) && (NULL == qnode))
{
continue;
}
if ((NULL == pnode) || (NULL == qnode))
{
return false;
}
if (pnode->val != qnode->val)
{
return false;
}
++front_l;
STACK_L[front_l] = pnode->right;
++front_l;
STACK_L[front_l] = pnode->left;
++front_r;
STACK_R[front_r] = qnode->left;
++front_r;
STACK_R[front_r] = qnode->right;
}
return true;
}
//============================================================================
// Name : preorder traversal
// Author : Yongqiang Cheng
// Version : Feb 22, 2020
// Copyright : Copyright (c) 2020 Yongqiang Cheng
// Description : Hello World in C++, Ansi-style
//============================================================================
/*
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* struct TreeNode *left;
* struct TreeNode *right;
* };
*/
bool isSymmetric(struct TreeNode* root)
{
int front_l = 0, front_r = 0;
struct TreeNode* STACK_L[512] = { NULL };
struct TreeNode* STACK_R[512] = { NULL };
struct TreeNode* pnode = NULL;
struct TreeNode* qnode = NULL;
if (NULL == root)
{
return true;
}
front_l = -1;
front_r = -1;
STACK_L[++front_l] = root->left;
STACK_R[++front_r] = root->right;
while (front_l >= 0)
{
pnode = STACK_L[front_l];
qnode = STACK_R[front_r];
front_l--;
front_r--;
if ((NULL == pnode) && (NULL == qnode))
{
continue;
}
if ((NULL == pnode) || (NULL == qnode))
{
return false;
}
if (pnode->val != qnode->val)
{
return false;
}
STACK_L[++front_l] = pnode->right;
STACK_L[++front_l] = pnode->left;
STACK_R[++front_r] = qnode->left;
STACK_R[++front_r] = qnode->right;
}
return true;
}