一個 Swift 算法問題引發(fā)的思考

最近一直在寫Swift方面的算法問題,寫得多了,自然就有一定的收獲,今天有個問題,感覺特別有趣??。

這個問題是這樣的:確定一個二叉樹B是否是另外一個二叉樹A的子樹。
這個問題不難,百度能找到很多的答案,其基本思路是這樣的,先遍歷first二叉樹,找到所有值等于second二叉樹根結點值的節(jié)點群。然后,再分別比較節(jié)點群里的節(jié)點的子樹節(jié)點和first節(jié)點的值是否相同,若能找到,就可以確定second二叉樹是first二叉樹的一個兒子??。

在C語言里,代碼是這樣寫的:

struct BinaryTreeNode {
    int value;
    BinaryTreeNode *left;
    BinaryTreeNode *right;
};
//  判斷一個二叉樹是否是另一個的子樹
bool getAnswerWith(BinaryTreeNode * first,BinaryTreeNode * second){
    //遍歷first二叉樹,找到first節(jié)點與second根節(jié)點值相同的節(jié)點
    //??  這里完全可以使用遞歸的方式去遍歷A二叉樹,但是那樣不好,因為遞歸深度過于深的話可能造成函數(shù)壓棧太多,造成棧溢出。
    //這里運用壓棧的方式遍歷二叉樹
    std::stack<BinaryTreeNode *> 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<T> {
    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<Int>,second:BinaryTreeNode<Int>?) -> Bool{
    
    if second == nil {
        return true
    }
    /*同樣使用壓棧的方式遍歷二叉樹,這里的棧是我使用鏈表實現(xiàn)的,跟apple的文檔用Array的實現(xiàn)方式不太一樣
      有興趣的可以看下我的GitHub,下面會有我的GitHub地址
  */
    let stack:Stack<BinaryTreeNode<Int>> = Stack()
    var answer:Bool = false
    var tree:BinaryTreeNode<Int>? = 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<Int>?,_ second:BinaryTreeNode<Int>?) -> 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里允許我們對運算符進行重載,而且需要注意的是,樹節(jié)點的value需要遵守Equatable協(xié)議才可以進行比較,于是代碼就變成了這個樣子:

class BinaryTreeNode<T:Equatable>: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<T>, rhs: BinaryTreeNode<T>) -> 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<T>?,rhs:BinaryTreeNode<T>?) -> Bool{
        if rhs == nil { return true}
        if lhs == nil {return false}
        return rhs! == lhs!
    }
}

func getAnswerWithSwift(_ first:BinaryTreeNode<Int>,second:BinaryTreeNode<Int>?) -> Bool{
    
    if second == nil {
        return true
    }
    let stack:Stack<BinaryTreeNode<Int>> = Stack()
    var answer:Bool = false
    var tree:BinaryTreeNode<Int>? = 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里允許我們寫運算符的重載,很強大的特性,在很多地方,或者是復雜的函數(shù)嵌套時,我們都可以使用運算符的重載,是代碼更簡潔,加強可讀性。但是我把它用到這里,仔細想想,不大好,因為我在BinaryTreeNode里重載了==運算符,意味著以后用到二叉樹的==都會是這樣子,使得我們的二叉樹有了很大的局限性,但是又想不到啥好的方法。為了更強的可讀、擴展性和二叉樹的應用范圍,我把代碼改成了這個樣子:

class BinaryTreeNode<T> {
    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<T>, rhs: BinaryTreeNode<T>) -> 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<T>?,rhs:BinaryTreeNode<T>?) -> Bool{
        if rhs == nil {
            return true
        }
        if lhs == nil {
            return false
        }
        return rhs! == lhs!
    }
}

這樣寫有上述的好處,但是還是不太好。因為只要二叉樹節(jié)點的值類型遵守了Equatable協(xié)議,使用==的時候,就會使用我們定義的重載方法,還是給二叉樹增加了一定的局限性。

經(jīng)過一番的思考,我最后決定,把代碼改成最開始時候的樣子??。有時候,語言特性的東西不一定適合我們的應用場景,不要為了使用而去使用,否則得不償失。

上文說到我最近在寫一些Swift數(shù)據(jù)結構和算法上面的東西,有需要的朋友可以看一下。
GitHub地址:https://github.com/chaiyanpu/SwiftCustomAlgorithms

-------------------2016年11月19號更新-------------------------
New Idea

感謝Swift3.0新增加的關鍵字fileprivate,現(xiàn)在只需要把 BinaryTreeNode<T> 定義 和 BinaryTreeNode拓展放到不同的文件中就可以了,修改后的代碼是這樣子的:

//注意,只需要前面加上fileprivate關鍵字,但是和BinaryTreeNode<T>不在一個文件中就可以了
fileprivate extension BinaryTreeNode where T:Equatable {    
    static func ==(lhs: BinaryTreeNode<T>, rhs: BinaryTreeNode<T>) -> 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<T>?,rhs:BinaryTreeNode<T>?) -> Bool{
        if rhs == nil {
            return true
        }
        if lhs == nil {
            return false
        }
        return rhs! == lhs!
    }
}

感覺胸前的紅領巾更鮮艷了??。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容