最近一直在写Swift方面的算法问题,写得多了,自然就有一定的收获,今天有个问题,感觉特别有趣。
始
这个问题是这样的:确定一个二叉树B是否是另外一个二叉树A的子树。
这个问题不难,百度能找到很多的答案,其基本思路是这样的,先遍历first二叉树,找到所有值等于second二叉树根结点值的节点群。然后,再分别比较节点群里的节点的子树节点和first节点的值是否相同,若能找到,就可以确定second二叉树是first二叉树的一个儿子。
在C语言里,代码是这样写的:
struct BinaryTreeNode {
int value;
BinaryTreeNode *left;
BinaryTreeNode *right;
};
// 判断一个二叉树是否是另一个的子树
bool getAnswerWith(BinaryTreeNode * first,BinaryTreeNode * second){
//遍历first二叉树,找到first节点与second根节点值相同的节点
//⚠️ 这里完全可以使用递归的方式去遍历A二叉树,但是那样不好,因为递归深度过于深的话可能造成函数压栈太多,造成栈溢出。
//这里运用压栈的方式遍历二叉树
std::stack myStack;
BinaryTreeNode * tree = first;
bool answer = false;
while (myStack.size() != 0 || tree != NULL) {
if (tree != NULL){
myStack.push(tree);
if (tree->value == second->value){
answer = isHasCommonValue(tree, second);
}
tree = tree->left;
}else{
tree = myStack.top()->right;
myStack.pop();
}
}
return answer;
}
bool isHasCommonValue(BinaryTreeNode * first,BinaryTreeNode *second){
if (second == NULL){ return true;}
if (second->value != first->value){ return false;}
return (isHasCommonValue(first->left, second->left) && (isHasCommonValue(first->right, second->right)));
}
如果把这个代码移植到Swift里,也很简单。照着翻译一遍就好了。
翻译好的代码是这样的:
class BinaryTreeNode {
var value:T
var left:BinaryTreeNode?
var right:BinaryTreeNode?
init(_ value:T) {
self.value = value
self.left = nil
self.right = nil
}
}
func getAnswerWithSwift(_ first:BinaryTreeNode,second:BinaryTreeNode?) -> Bool{
if second == nil {
return true
}
/*同样使用压栈的方式遍历二叉树,这里的栈是我使用链表实现的,跟apple的文档用Array的实现方式不太一样
有兴趣的可以看下我的GitHub,下面会有我的GitHub地址
*/
let stack:Stack> = Stack()
var answer:Bool = false
var tree:BinaryTreeNode? = first
while !stack.isEmpty() || tree != nil {
if let aTree = tree {
stack.push(value: aTree)
if aTree.value == second!.value{
answer = isHasCommonValue(aTree,second)
}
tree = aTree.left
}else{
tree = stack.top()?.right
stack.pop()
}
}
return answer
}
func isHasCommonValue(_ first:BinaryTreeNode?,_ second:BinaryTreeNode?) -> Bool{
if second == nil{ return true}
if first == nil { return false}
if first!.value != second!.value{return false}
return (isHasCommonValue(first!.left,second!.left) && isHasCommonValue(first!.right,second!.right))
}
变
如果仅仅是这样,也不会有什么特别的地方,最近一直在研究Swift的东西,所以想事情都会往Swift那边靠一靠,
Swift里允许我们对运算符进行重载,而且需要注意的是,树节点的value需要遵守Equatable协议才可以进行比较,于是代码就变成了这个样子:
class BinaryTreeNode:Equatable {
var value:T
var left:BinaryTreeNode?
var right:BinaryTreeNode?
init(_ value:T) {
self.value = value
self.left = nil
self.right = nil
}
//重载 ==
static func ==(lhs: BinaryTreeNode, rhs: BinaryTreeNode) -> Bool{
if lhs.value != rhs.value{
return false
}
return (isNil(lhs:lhs.left,rhs:rhs.left) && isNil(lhs:lhs.right,rhs:rhs.right))
}
static func isNil(lhs:BinaryTreeNode?,rhs:BinaryTreeNode?) -> Bool{
if rhs == nil { return true}
if lhs == nil {return false}
return rhs! == lhs!
}
}
func getAnswerWithSwift(_ first:BinaryTreeNode,second:BinaryTreeNode?) -> Bool{
if second == nil {
return true
}
let stack:Stack> = Stack()
var answer:Bool = false
var tree:BinaryTreeNode? = first
while !stack.isEmpty() || tree != nil {
if let aTree = tree {
stack.push(value: aTree)
if aTree.value == second!.value{
answer = (aTree == second!) //注意这里
}
tree = aTree.left
}else{
tree = stack.top()?.right
stack.pop()
}
}
return answer
}
Swift里允许我们写运算符的重载,很强大的特性,在很多地方,或者是复杂的函数嵌套时,我们都可以使用运算符的重载,是代码更简洁,加强可读性。但是我把它用到这里,仔细想想,不大好,因为我在BinaryTreeNode里重载了==运算符,意味着以后用到二叉树的==都会是这样子,使得我们的二叉树有了很大的局限性,但是又想不到啥好的方法。为了更强的可读、扩展性和二叉树的应用范围,我把代码改成了这个样子:
class BinaryTreeNode {
var value:T
var left:BinaryTreeNode?
var right:BinaryTreeNode?
init(_ value:T) {
self.value = value
self.left = nil
self.right = nil
}
}
//将两个方法抽取出来
extension BinaryTreeNode where T:Equatable {
static func ==(lhs: BinaryTreeNode, rhs: BinaryTreeNode) -> Bool{
if lhs.value != rhs.value{
return false
}
return (isNil(lhs:lhs.left,rhs:rhs.left) && isNil(lhs:lhs.right,rhs:rhs.right))
}
static func isNil(lhs:BinaryTreeNode?,rhs:BinaryTreeNode?) -> Bool{
if rhs == nil {
return true
}
if lhs == nil {
return false
}
return rhs! == lhs!
}
}
这样写有上述的好处,但是还是不太好。因为只要二叉树节点的值类型遵守了Equatable协议,使用==的时候,就会使用我们定义的重载方法,还是给二叉树增加了一定的局限性。
末
经过一番的思考,我最后决定,把代码改成最开始时候的样子。有时候,语言特性的东西不一定适合我们的应用场景,不要为了使用而去使用,否则得不偿失。
上文说到我最近在写一些Swift数据结构和算法上面的东西,有需要的朋友可以看一下。
GitHub地址:https://github.com/chaiyanpu/SwiftCustomAlgorithms
-------------------2016年11月19号更新-------------------------
New Idea
感谢Swift3.0新增加的关键字fileprivate,现在只需要把 BinaryTreeNode
//注意,只需要前面加上fileprivate关键字,但是和BinaryTreeNode不在一个文件中就可以了
fileprivate extension BinaryTreeNode where T:Equatable {
static func ==(lhs: BinaryTreeNode, rhs: BinaryTreeNode) -> Bool{
if lhs.value != rhs.value{
return false
}
return (isNil(lhs:lhs.left,rhs:rhs.left) && isNil(lhs:lhs.right,rhs:rhs.right))
}
static func isNil(lhs:BinaryTreeNode?,rhs:BinaryTreeNode?) -> Bool{
if rhs == nil {
return true
}
if lhs == nil {
return false
}
return rhs! == lhs!
}
}
感觉胸前的红领巾更鲜艳了。