leetcode572的几种优化方案

leetcode572的几种优化方案

leetcode572

前言:前几天面试的时候遇到了这题,因为是去年刷的这题,最后只记得一个hash,靠着这个提示和面试官的一些提示最终勉强做出来。

几种一般的解法

  1. 简单深度搜索,判断每一个节点所对应的子树是否结构和目标树相同。
  2. 将树的结构用字符串替代(参考树的序列化等方法),然后进行字符串的比较
  3. 将树转换为一个唯一的hash数字,通过比较数字的相同来判断两个树是否结构相同。

其他信息

  1. 树的节点个数
  2. 树的深度
  3. 如何处理hash冲突

因为面试的时候比较紧张,就只将第三个信息加进来了。所以最终的代码是这样的:

typedef unsigned long long hash_type;
struct node{
	int val;
	node* left;
	node* right;
};
bool isSameTree(node* root1, node* root2)
{
	if(root1 != nullptr && root2 != nullptr){
		return isSameTree(root1->left, root2->left) 
			&& isSameTree(root1->right, root2->right);
	}else if(root1 == nullptr && root2 == nullptr){
		return true;
	}else{
		return false;
	}
}
hash_type getTreeHash(node* root)
{
	if(!root){
		return 0;
	}
	return getTreeHash(root->left)*5+7+getTreeHash(root->right)*11;
}
hash_type dfs_subtree(node* root, hash_type aimValue, bool& find, node* root2)
{
	if(find){
		return 0;
	}
	if(!root){
		return 0;
	}
	hash_type leftValue = dfs_subtree(root->left, aimValue, find, root2);
	hash_type rightValue = dfs_subtree(root->right, aimValue, find, root2);
	hash_type value = leftValue*5+7+rightValue*11;
	if(value == aimValue && !find){
		find = isSameTree(root, root2);
	}
	return value;
}
bool isSubtree(node* root1, node* root2)
{
	if(!root2){
		return true;
	}
	if(!root1){
		return false;
	}
	
	hash_type root2HashValue = getTreeHash(root2);
	bool find = false;
	dfs_subtree(root1, root2HashValue, find, root2);
	return find;
}

面试管针对以上代码的评价:

  1. dfs_subtree函数将两个功能合在了一起
    个人理解:按照leetcode的题解,将其功能进行分离,最终也可以达到目的,事实上,我在创建getTreeHash的函数的时候也是想着将功能进行分离,但是后续考虑到针对每一个节点要么多次计算,要么用hashMap进行记录,时间和空间复杂度都会变高。故没采用。(但是没和面试官说明,功能分离的代码也自行查看leetcode题解,这里不做说明)
  2. 我最初写的版本没有isSameTree这个函数,所以会出现hash冲突的情况。在面试管的提示下添加了这个函数,但是因为之前针对find的修改只是变true。增加这个函数之后就可以出现true变成false的情况,所以需要在进行dfs判断两个树的结构是否相同之前需要判断是否已经找到了。也即将判断条件改成if(value == aimValue && !find)。
    个人说明:后面面试官问我学到了什么,当时答的比较一般,只是简单说明针对非修改参数设置const,针对要修改的参数都要在修改前进行确认。实际上后续思考了一下,其实是针对某一个模块的修改,需要站在上一层架构或者逻辑进行思考确认其能否兼容该修改,如果不能兼容,则需要修改上一层的逻辑,然后递归向上层进行重新思考
  3. 面试管说以上代码他已经感觉没什么问题了。但是实际上,还有一些可以改进的点。
    a) 计算hash的数值应该整合成一个函数,避免后续修改hash的计算方式需要修改多个地方
    b) 未考虑节点的个数和深度等信息,可以进行剪枝来进行加快判断
    c) 因为hash数值是粗判断,可以使用int数值才记录hash数值(int的计算比unsigned long long要快)
typedef int hash_type;
struct node{
	int val;
	node* left;
	node* right;
};
struct hash_node{
	hash_type hashValue; // 记录hash数值
	int depth; // 记录树的深度
	int nodeCnt; // 记录树的节点个数
	hash_node():hahs_Value(0),depth(0),nodeCnt(0){}
};
bool isSameTree(node* root1, node* root2)
{
	if(root1 != nullptr && root2 != nullptr){
		return isSameTree(root1->left, root2->left) 
			&& isSameTree(root1->right, root2->right);
	}else if(root1 == nullptr && root2 == nullptr){
		return true;
	}else{
		return false;
	}
}
hash_type CalHashValue(const hash_type& left, const hash_type& right)
{
	// 记得题解的话也可以按照答案计算hash数值
	return left*101+71+right*301;
}
hash_node getTreeHash(node* root)
{
	hash_node res;
	if(root){
		hash_node&& leftNode = getTreeHash(root->left);
		hash_node&& rightNode = getTreeHash(root->right);
		// 以下三行的功能应该整合成一个函数
		res.hashValue = CalHashValue(leftNode.hashValue, rightNode.hashValue);
		res.depth = max(leftNode.depth, rightNode.depth)+1;
		res.nodeCnt = (leftNode.nodeCnt + rightNode.nodeCnt)+1;
	}
	return res;
}
hash_node dfs_subtree(node* root, const hash_node& aimValue, bool& find, node* root2)
{
	// 多处return也可以按照redis的实现用goto来进行优化
	// 主要是针对编译代码的一个优化,一般不用考虑,并且有的大佬也不喜欢goto
	hash_node res;
	if(find || !root){
		return res;
	}
		
	hash_node&& leftNode = dfs_subtree(root->left, aimValue, find, root2);
	hash_node&& rightNode = dfs_subtree(root->right, aimValue, find, root2);
	
	res.depth = max(leftNode.depth, rightNode.depth)+1;
	res.nodeCnt = (leftNode.nodeCnt + rightNode.nodeCnt)+1;
	// 通过对深度和高度的判断来进行剪枝
	// 可以针对左树特别大,右树很小的情况进行的一个剪枝优化
	if(res.depth > aimValue.depth || res.nodeCnt > aimValue.nodeCnt){
		return res;
	}
	
	res.hashValue = CalHashValue(leftNode.hashValue, rightNode.hashValue);
	if(res == aimValue && !find){
		find = isSameTree(root, root2);
	}
	return res;
}
bool isSubtree(node* root1, node* root2)
{
	if(!root2){
		return true;
	}
	if(!root1){
		return false;
	}
	
	hash_node&& root2HashNode = getTreeHash(root2);
	bool find = false;
	dfs_subtree(root1, root2HashValue, find, root2);
	return find;
}

该代码主要是针对左树特别大的情况下的一种优化。因为该实现仍需要遍历,从下往上算,高度小于等于目标树高度的子树。所以当两边的树相差不大的时候,该实现会因为引进了新的参数,计算速度反而会下降。但是当左树特别大的时候,该优化就可以将高度过高的节点的计算从hash计算改成加法计算,所以树的高度相差越大,优化效果越明显。(当然,该实现可以询问是否需要)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值