题目要求:请实现一个函数,用来判断一颗二叉树是不是对称的。注意,如果一个二叉树同此二叉树的镜像是同样的,定义其为对称的。【来源于剑指offer】
知识巩固:通过参考答案发现了之前忽略的一个知识点:层次遍历。下面两篇文章非常有用,使我对树的整体结构有了新认识。
- 二叉树遍历(前序、中序、后序、层次、深度优先、广度优先遍历)
- 层次遍历二叉树-三种不同的方法(还未仔细看每一行代码,打印二叉树题目时再细细分析)
二叉树是一种非常重要的数据结构,非常多其他数据结构都是基于二叉树的基础演变而来的。
对于二叉树,有深度遍历和广度遍历,深度遍历有前序、中序以及后序三种遍历方法,广度遍历即我们寻常所说的层次遍历。
树的定义本身就是递归,采用递归的方法去实现树的三种遍历不仅easy理解并且代码非常简洁,而对于广度遍历来说,须要其他数据结构的支撑。
采用非递归方法时,深度遍历使用堆栈,广度遍历采用队列。
基本思路:二叉树是否对称,只要采用前序、中序、后序、层次遍历等任何一种遍历方法,分为先左后右和先右后左两种方法,只要两次结果相等就说明这棵树是一颗对称二叉树。
代码实现:
方法一采用递归法,为了在vs里实现完整功能以便调试,借用了重建二叉树的代码,也可以借用键盘输入构建二叉树的代码。
#include<iostream>
#include<vector>
struct TreeNode
{
int val;
TreeNode* Left;
TreeNode* Right;
TreeNode(int x):val(x),Left(NULL),Right(NULL){}
};
bool isSymmetrical(TreeNode* leftRoot, TreeNode* rightRoot)
{
if (leftRoot == NULL)
return rightRoot == NULL;
if (rightRoot != NULL)
{
if (leftRoot->val == rightRoot->val)
return isSymmetrical(leftRoot->Left, rightRoot->Right) && isSymmetrical(leftRoot->Right, rightRoot->Left);
else
return false;
}
return false;
}
bool isSymmetrical(TreeNode* pRoot)
{
if (pRoot == NULL)return NULL;
return isSymmetrical(pRoot->Left, pRoot->Right);
}
TreeNode* reConstructBinaryTree(std::vector<int> pre, int pre_left, int pre_right, std::vector<int> vin, int vin_left, int vin_right)
{
if (pre_left > pre_right || vin_left > vin_right)
return NULL;
TreeNode* tree = new TreeNode(pre[pre_left]);
int i = 0;
for (i = vin_left; i < vin_right + 1; i++)
//for (int i = 0; i < pre.size(); i++)对称树,有重复值时会出现异常
{
if (pre[pre_left] == vin[i])
{
tree->Left = reConstructBinaryTree(pre, pre_left + 1, i - vin_left + pre_left, vin, vin_left, i - 1);
tree->Right = reConstructBinaryTree(pre, i - vin_left + pre_left + 1, pre_right, vin, i + 1, vin_right);
}
}
return tree;
}
TreeNode* reConstructBinaryTree(std::vector<int> pre, std::vector<int> vin)
{
TreeNode* tree = reConstructBinaryTree(pre, 0, pre.size() - 1, vin, 0, pre.size() - 1);
return tree;
}
void main()
{
TreeNode* tree;
std::vector<int> pre = { 2, 1, 3, 4, 1, 4, 3 };
std::vector<int> vin = { 3, 1, 4, 2, 4, 1, 3 };
tree = reConstructBinaryTree(pre, vin);
bool a = isSymmetrical(tree);
if (a == true)
std::cout << "true" << std::endl;
else
std::cout << "false" << std::endl;
}
在 bool isSymmetical(TreeNode* leftRoot, TreeNode* rightRoot) 函数中代码不精简,讨论中 华科渣硕
的代码很精简 。
//https://www.nowcoder.com/questionTerminal/ff05d44dfdb04e1d83bdbdab320efbcb
bool isSymmetrical(TreeNode left, TreeNode right) {
if(left == null && right == null) return true;
if(left == null || right == null) return false;
return left.val == right.val //为镜像的条件:左右节点值相等
&& isSymmetrical(left.left, right.right) //对称的子树也是镜像
&& isSymmetrical(left.right, right.left);
}
代码实现:
方法二采用非递归法,深度遍历使用堆栈,广度遍历采用队列,DFS BFS算法以后再细学。
- BFS使用Queue来保存成对的节点
- 1.出队的时候也是成对成对的
1.若都为空,继续;
2.一个为空,返回false;
3.不为空,比较当前值,值不等,返回false; - 2.确定入队顺序,每次入队都是成对成对的,如left.left, right.right ;left.rigth,right.left
来源:https://www.nowcoder.com/questionTerminal/ff05d44dfdb04e1d83bdbdab320efbcb
- 1.出队的时候也是成对成对的
错误代码(逻辑关系捋不顺,不知何处返回逻辑值)
bool issymmetrical(treenode* proot)
{
if (proot == null)return true;
//std::queue<int> qleft, qright; 队列基本语法
std::queue<treenode*> qleft, qright;
if (proot->left != null && proot->right != null)
{
qleft.push(proot->left);
qright.push(proot->right);
treenode *left, *right;
do
{
left = qleft.front();
qleft.pop();
right = qright.front();
qright.pop();
qleft.push(left->left);
qleft.push(left->right);
qright.push(right->right);
qright.push(right->left);
} while (left == right);
}
else if (proot->left == null && proot->right == null)
return true;
else return false;
}
修正后代码(或者定义一个队列,成对地出入队列)
bool isSymmetrical(TreeNode* pRoot)
{
if (pRoot == NULL)return true;
std::queue<TreeNode*> qleft, qright;
TreeNode *left, *right;
qleft.push(pRoot->Left);
qright.push(pRoot->Right);
while (!qleft.empty() || !qright.empty())//判断队列是否为空,队列为空时即遍历完所有元素
{
left = qleft.front();
qleft.pop();
right = qright.front();
qright.pop();
//判断队首元素两边全为空
if (left == NULL && right == NULL)
continue;
//队首元素两边不全为空,若全为空则进上一个判断
if (left == NULL || right == NULL || (left->val != right->val))
return false;
qleft.push(left->Left);
qleft.push(left->Right);
qright.push(right->Right);
qright.push(right->Left);
}
return true;//当遍历完没有不相等的情况时,即可确定该树为对称树。
}
当定义一个队列成对压入压出时,要注意压入的先后顺序,一定要left->Left、right->Right成队出入,其实两个队列也一样,压入left->Left、right->Right要并行处理。
queue.push(left->Left);
queue.push(right->Right);
queue.push(left->Right);
queue.push(right->Left);
反思总结:
- 低级错误:看是指针还是对象,->和.的使用要正确
使用左右子节点时错用“.”结果报错:error #2153: expression must have class type。 - 队列的基础知识掌握不牢固:队列里的元素类型定义,一开始错误定义为int型;读取队首元素front(),弹出元素pop()。
- 代码实现能力太差:明白基本思路后,方法一中无法很好地处理左右节点只有一个为空的情况;方法二中,竟然不知while循环情况下何处返回逻辑值。
- 第一次做题时完全没意识到层次遍历,递归,DFS,BFS之间的关系,完全没有深入思考。由于采用边实战边学习基础知识的方法:通过参考答案学习解题思路,发现新知识点,然后补充学习。这种学习方法决定了深入思考的重要性,不然会遗落很多知识点,以后要理解透彻,思考深入!!!