Third Exercise:对称的二叉树

题目要求:请实现一个函数,用来判断一颗二叉树是不是对称的。注意,如果一个二叉树同此二叉树的镜像是同样的,定义其为对称的。【来源于剑指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来保存成对的节点

错误代码(逻辑关系捋不顺,不知何处返回逻辑值)

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之间的关系,完全没有深入思考。由于采用边实战边学习基础知识的方法:通过参考答案学习解题思路,发现新知识点,然后补充学习。这种学习方法决定了深入思考的重要性,不然会遗落很多知识点,以后要理解透彻,思考深入!!!
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值