Swift實踐翻轉(zhuǎn)二叉樹

HomeBrew作者断序,天才程序員Max Howell去Google面試)奸汇,結(jié)果卻因不會在白板上翻轉(zhuǎn)二叉樹被Google粗魯?shù)鼐芙^了颤练,輿論甚是嘩然她倘,其中緣由知乎上也討論的熱火朝天。誰料铅祸,始作俑者翻轉(zhuǎn)二叉樹。作為程序猿的我合武,從未翻過二叉樹临梗。最近閑來無事,閑暇之余稼跳,想及至此盟庞,就順手用Swift翻轉(zhuǎn)了二叉樹。

1.需求如下圖

image.png

2.二叉樹定義

先定義一個二叉樹類Tree汤善,主要定義樹的三個必要屬性什猖,關(guān)鍵值key,左子樹leftTree和右子樹rightTree红淡。

class Tree {
    var key: Int   //節(jié)點上顯示的關(guān)鍵值
    var leftTree: Tree?  //左子樹
    var rightTree: Tree? //右子樹
    
    //初始化方法
    init(key: Int) {
        self.key = key
    }
    
}

3. 解決方案

3.1 遞歸翻轉(zhuǎn)

用遞歸的方式翻轉(zhuǎn)二叉樹不狮,主要分為兩步:

func invertBinaryTreeWithRecursive(root: Tree) {
    //1
    let tempT = root.leftTree
    root.leftTree = root.rightTree
    root.rightTree = tempT

    //2
    if let leftT = root.leftTree {
        //遞歸調(diào)用
        invertBinaryTreeWithRecursive(leftT)
    }
    if let rightT = root.rightTree {
        invertBinaryTreeWithRecursive(rightT)
    }
}

第1步:翻轉(zhuǎn)本樹的左子樹和右子樹,需要借助一個零時變量tempT在旱;
第2步:判斷左右樹是否存在摇零,如存在,則遞歸調(diào)用進(jìn)行翻轉(zhuǎn)桶蝎。
遞歸翻轉(zhuǎn)二叉樹每個節(jié)點只訪問1次驻仅,在每個節(jié)點上的耗時是常數(shù),因此總耗時Θ(n)登渣,n是樹的節(jié)點樹噪服。

3.2 非遞歸翻轉(zhuǎn)

其實也可以不用遞歸翻轉(zhuǎn)二叉樹,分為3步:

func invertBinaryTreeWithoutRecursive(root: Tree) {
    //1
    var arr = [Tree]()
    arr.append(root)
    //2
    while !arr.isEmpty {
        let node = arr.removeFirst()
        let tempT = node.leftTree
        node.leftTree = node.rightTree
        node.rightTree = tempT
        
        //3
        if let leftT = node.leftTree {
            arr.append(leftT)
        }
        if let rightT = node.rightTree {
            arr.append(rightT)
        }
    }
}

第1步:初始化一個數(shù)組胜茧,用來存儲尚未翻轉(zhuǎn)的樹粘优,第一個尚未翻轉(zhuǎn)的樹即是根樹;
第2步:判斷是否仍有樹未翻轉(zhuǎn)竹揍,如果有敬飒,從數(shù)組中取出第一個樹,并將它從數(shù)組中刪除芬位,然后對這個樹翻轉(zhuǎn)左子樹和右子樹无拗,需要借助一個零時變量tempT;
第3步:判斷被翻的樹左右子樹是否存在昧碉,如存在英染,將它依次存儲在數(shù)組的最后面揽惹。
這種翻轉(zhuǎn)二叉樹的方式是從左到右翻完同一層的樹節(jié)點,再從左到右翻下一層的樹節(jié)點四康,直至翻完搪搏,其耗時也是Θ(n)。

4. 遍歷二叉樹

為了遍歷二叉樹闪金,輸出有樹形狀的結(jié)果疯溺,先定義一個輔助函數(shù),計算樹的高度.

4.1 計算樹的高度
//calculate the height of a tree 
func heightOfTree(root: Tree) -> Int {
    //1
    var leftTreeHeight = 0
    var rightTreeHeight = 0
    //2
    if let leftT = root.leftTree {
        leftTreeHeight = 1 + heightOfTree(leftT)
    }
    
    if let rightT = root.rightTree {
        rightTreeHeight = 1 + heightOfTree(rightT)
    }
    //3
    return max(leftTreeHeight, rightTreeHeight)
}

計算樹的高度也必須用到遞歸哎垦。
第1步:初始化兩個高度變量囱嫩,分別代表左右子樹高度,缺省值為0漏设,即左右子樹均為nil
時的值墨闲;
第2步:判斷左右子樹是否存在,如存在遞歸求左右子樹的高度郑口;
第3步:返回左右子樹高度的最大值鸳碧,這一步是遞歸終止條件,是整個遞歸的精髓犬性。
因為遍歷了所有樹節(jié)點瞻离,因此總耗時Θ(n)。

4.2 遍歷

為輸出的結(jié)果具有樹的形狀仔夺,樹的遍歷輸出要費(fèi)點腦筋琐脏,分4步:

//print out binary tree 
func tranverseTree(root: Tree) {
    //1
    let height = heightOfTree(root)
    var trees = [(tree: root, number: 1, height: height, depth: 0, placeHolderTree: false)]
    let placeHolderTree = Tree(key: 0)
    
    //2
    func appendTree(tree: Tree?, node: (tree: Tree, number: Int, height: Int, depth: Int, placeHolderTree: Bool)) {
        let number: Int
        if let last = trees.last {
            number = last.height == node.height - 1 ? last.number + 1 : 1
        } else {
            number = 1
        }
        if let t = tree {
            trees.append((tree: t,
                number: number,
                height: node.height - 1,
                depth: node.depth + 1,
                placeHolderTree: false))
        } else {
            trees.append((tree: placeHolderTree,
                number: number,
                height: node.height - 1,
                depth: node.depth + 1,
                placeHolderTree: true))
        }
    }
    
    //3
    while trees[0].height >= 0 {
        let node = trees.removeFirst()
        let space: Int
        if node.number == 1 {
            space = Int(pow(2.0, Double(node.height)))
        } else {
            space = Int(pow(2.0, Double(node.height + 1)))
        }
        for _ in 1..<space {
            print(" ", terminator: "")
        }
        if node.placeHolderTree {
            print(" ", terminator: "")
        } else {
            print(node.tree.key, terminator: "")
        }
        if node.number == Int(pow(2.0, Double(node.depth))) {
            print("")
        }
        
        //4
        appendTree(node.tree.leftTree, node: node)
        appendTree(node.tree.rightTree, node: node)

    }
    
}

整個遍歷的思路與invertBinaryTreeWithoutRecursive完全一致。
第1步:初始化一個數(shù)組缸兔,存儲元組類型(tree: Tree, number: Int, height: Int, depth: Int, placeHolderTree: Bool)日裙;這個元組中tree表示一個樹節(jié)點;number表示這個樹節(jié)點在其所在層的位置惰蜜,第一個number為1昂拂,第二個number為2,依次類推抛猖;height表示這個樹節(jié)點的高度格侯;depth表示這個樹節(jié)點的深度;placeHolderTree表示這個樹節(jié)點是否為占位樹财著。為便于后續(xù)遍歷联四,當(dāng)樹為非完全二叉樹時,空缺的樹節(jié)點全部用占位樹填補(bǔ)撑教,因此才有placeHolderTree
元組元素朝墩,以標(biāo)示占位樹;
第2步:這是一個utility函數(shù)伟姐,在第4步時調(diào)用收苏;
第3步:判斷樹是否到底亿卤,當(dāng)沒到底時,從數(shù)組中取出并刪除第一個節(jié)點鹿霸,然后計算并打印這個節(jié)點相應(yīng)的空格數(shù)排吴,然后打印節(jié)點key值,如果這個節(jié)點是同一層級最后一個節(jié)點懦鼠,打印換行钻哩;
第4步:將這個節(jié)點的左右子樹連同相應(yīng)信息存儲到數(shù)組中,這時調(diào)用了第2步中的utility函數(shù)appendTree:node:葛闷,appendTree:node:
中對傳入的樹節(jié)點進(jìn)行了判斷憋槐,當(dāng)為空時,自動填補(bǔ)占位樹淑趾。
此函數(shù)遍歷了所有樹節(jié)點,因此總耗時Θ(n)忧陪。

4.3 測試

生成二叉樹扣泊,并進(jìn)行測試:

let tree = Tree(key: 1)
tree.leftTree = Tree(key: 2)
tree.rightTree = Tree(key: 3)
tree.rightTree?.leftTree = Tree(key: 4)
print("------tree before inverting-------")
tranverseTree(tree)
print("------tree inverted-------")
invertBinaryTreeWithRecursive(tree)
tranverseTree(tree)

輸出結(jié)果為:

------tree before inverting-------
       1
   2       3
         4            
------tree inverted-------
       1
   3       2
      4      
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市嘶摊,隨后出現(xiàn)的幾起案子延蟹,更是在濱河造成了極大的恐慌,老刑警劉巖叶堆,帶你破解...
    沈念sama閱讀 217,907評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件阱飘,死亡現(xiàn)場離奇詭異,居然都是意外死亡虱颗,警方通過查閱死者的電腦和手機(jī)沥匈,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,987評論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來忘渔,“玉大人高帖,你說我怎么就攤上這事∑枇福” “怎么了散址?”我有些...
    開封第一講書人閱讀 164,298評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長宣赔。 經(jīng)常有香客問我预麸,道長,這世上最難降的妖魔是什么儒将? 我笑而不...
    開封第一講書人閱讀 58,586評論 1 293
  • 正文 為了忘掉前任吏祸,我火速辦了婚禮,結(jié)果婚禮上椅棺,老公的妹妹穿的比我還像新娘齐蔽。我一直安慰自己,他們只是感情好床估,可當(dāng)我...
    茶點故事閱讀 67,633評論 6 392
  • 文/花漫 我一把揭開白布含滴。 她就那樣靜靜地躺著,像睡著了一般丐巫。 火紅的嫁衣襯著肌膚如雪谈况。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,488評論 1 302
  • 那天递胧,我揣著相機(jī)與錄音碑韵,去河邊找鬼。 笑死缎脾,一個胖子當(dāng)著我的面吹牛祝闻,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播遗菠,決...
    沈念sama閱讀 40,275評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼联喘,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了辙纬?” 一聲冷哼從身側(cè)響起豁遭,我...
    開封第一講書人閱讀 39,176評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎贺拣,沒想到半個月后蓖谢,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,619評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡譬涡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,819評論 3 336
  • 正文 我和宋清朗相戀三年闪幽,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片昂儒。...
    茶點故事閱讀 39,932評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡沟使,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出渊跋,到底是詐尸還是另有隱情腊嗡,我是刑警寧澤,帶...
    沈念sama閱讀 35,655評論 5 346
  • 正文 年R本政府宣布拾酝,位于F島的核電站燕少,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏蒿囤。R本人自食惡果不足惜客们,卻給世界環(huán)境...
    茶點故事閱讀 41,265評論 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧底挫,春花似錦恒傻、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,871評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至官边,卻和暖如春沸手,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背注簿。 一陣腳步聲響...
    開封第一講書人閱讀 32,994評論 1 269
  • 我被黑心中介騙來泰國打工契吉, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人诡渴。 一個月前我還...
    沈念sama閱讀 48,095評論 3 370
  • 正文 我出身青樓捐晶,卻偏偏與公主長得像,于是被迫代替她去往敵國和親妄辩。 傳聞我的和親對象是個殘疾皇子租悄,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,884評論 2 354

推薦閱讀更多精彩內(nèi)容

  • 樹的概述 樹是一種非常常用的數(shù)據(jù)結(jié)構(gòu),樹與前面介紹的線性表恩袱,棧,隊列等線性結(jié)構(gòu)不同胶哲,樹是一種非線性結(jié)構(gòu) 1.樹的定...
    Jack921閱讀 4,452評論 1 31
  • 本文轉(zhuǎn)自 http://www.cnblogs.com/manji/p/4903990.html二叉樹-****你...
    doublej_yjj閱讀 681評論 0 8
  • 1.什么是二叉樹畔塔? 在計算機(jī)科學(xué)中,二叉樹是每個節(jié)點最多有兩個子樹的樹結(jié)構(gòu)鸯屿。通常子樹被稱作“左子樹”和“右子樹”澈吨,...
    zcaaron閱讀 1,263評論 2 15
  • 去年二叉樹算法的事情鬧的沸沸揚(yáng)揚(yáng),起因是Homebrew 的作者 @Max Howell 在 twitter 上發(fā)...
    Masazumi柒閱讀 1,602評論 0 8
  • 2017.1.16 武功山 前年,一行人計劃四處漂流婶恼,卻輾轉(zhuǎn)多無定數(shù)桑阶。人生歷...
    南方有南初閱讀 535評論 0 5