Swift 算法實戰(zhàn)二(二叉樹、二分搜索)

前言

這是繼Swift 算法實戰(zhàn)一(集合,字典,鏈表,棧,隊列等算法)之后的又一篇文章苍糠,算是所學知識總結雅镊,也算是之后找工作的敲門磚。筆者這月月底要離職捺信,即使目前這家公司給我加薪的條件酌媒,也不再想繼續(xù)留下了。

這篇文章主要涉及到二叉樹迄靠、二分搜索等相關秒咨,具體請看文章內容。

一掌挚、二叉樹

不得不承認實際開發(fā)中很少用到二叉樹相關的知識陡厘,但是面試過程中卻被問道的不少。

1.1 二叉樹的認識

1.1.1 概念

二叉樹是 n (n >= 0)個結構的有限集合,改集合或者為空集(稱為空二叉樹),或者有一個根節(jié)點和兩棵互不相交的、分別稱為根節(jié)點的左子樹和右子樹的二叉樹組成。

public class ZWTreeNode {
  public var val: Int
  public var left: ZWTreeNode?
  public var right: ZWTreeNode?
  public init(val: Int) {
    self.val = val
  }
}
1.1.2 性質
  • 在二叉樹的第 i 層上有至多 2^( i-1) 個結點 (i >= 1).
  • 深度為 k 的二叉樹至多有 2^k - 1 個節(jié)點(k >= 1).
  • 具有n個結點的完全二叉樹的深度為 [log (2) n] + 1 ([x] 表示不大于x的最大整數) .

1.2 判斷是否為二叉查找樹

二叉查找樹的特點是:左子樹節(jié)點值都小于根節(jié)點的值,而右子樹節(jié)點值大于根節(jié)點值。那么問題就來了,給你一個二叉樹,怎么通過最簡單的方式,判斷是否是二叉查找樹。

對于解答上面這個問題,我們至少需要考慮到這兩種情況。首先铸磅,二叉樹但從定義上就能看出和遞歸有一定的關系弧械,所以通常解決二叉樹的問題界轩,第一反應就是要和遞歸綁定在一起;其次浊猾,二叉樹節(jié)點有為空的情況抖甘,所以一般針對空二叉樹這種邊界條件要做一些額外處理;

具體判斷實現如下代碼葫慎,代碼中包含詳細注釋衔彻,以及調用實例展示。

    //根據根節(jié)點做判斷
    func isValidTree(root:ZWTreeNode?) -> Bool{
        return self.helper(node: root, min: nil, max: nil)
    }
    
    func helper(node:ZWTreeNode?,min:Int?,max:Int?) -> Bool {
        //對于空節(jié)點的處理
        guard let node = node else { return true }
        //右子樹要求大于根節(jié)點
        if let min = min ,node.val <= min {
            return false
        }
        //左節(jié)點要小于根節(jié)點
        if let max = max,node.val >= max {
            return false
        }
        //根據左右節(jié)點同時做判斷
        return helper(node:node.left,min:min,max:node.val) && helper(node:node.right,min:node.val,max:max)
    }
//方法調用
let leftNode = ZWTreeNode(val: 1)
let rightNode = ZWTreeNode(val: 3)
let root = ZWTreeNode(val: 2)
root.left = leftNode
root.right = rightNode
print(isValidTree(root: root))//結果為:true

1.3 二叉樹的深度

計算二叉樹的深度偷办,同樣是只要知道根節(jié)點即可艰额,同樣也要借助遞歸實現。

//實現
func treeDepth(root:ZWTreeNode?) -> Int {
    guard let root = root else {
        return 0
    }
    return max(treeDepth(root:root.left), treeDepth(root: root.right)) + 1
 }
//調用形式
let leftNode = ZWTreeNode(val: 1)
let rightNode = ZWTreeNode(val: 3)
let root = ZWTreeNode(val: 2)
root.left = leftNode
root.right = rightNode
print(treeDepth(root: root))//結果為:2

1.4 二叉樹的遍歷

1.4.1 三種遍歷方式

二叉樹的遍歷多種多樣,,三種常用的遍歷: 前序遍歷椒涯、中序遍歷柄沮、后續(xù)遍歷(主要依據根節(jié)點遍歷的先后順序而定義的)。

前序遍歷首先訪問根結點废岂,然后前序遍歷左子樹铡溪,再前序遍歷右子樹。


前序遍歷

中序遍歷首先遍歷左子樹泪喊,然后訪問根結點棕硫,最后遍歷右子樹。在遍歷左袒啼、右子樹時哈扮,仍然先遍歷左子樹,再訪問根結點蚓再,最后遍歷右子樹滑肉。


中序遍歷

后序遍歷首先遍歷左子樹,然后遍歷右子樹摘仅,最后訪問根結點靶庙。在遍歷左、右子樹時娃属,仍然先遍歷左子樹六荒,然后遍歷右子樹护姆,最后遍歷根結點。


后序遍歷
1.4.2 前序遍歷實現

這里主要看一下如何借助棧來實現前序遍歷掏击。實際上其他幾種遍歷方式筆者是沒有怎么看的卵皂。

代碼邏輯的思路就是先遍歷節(jié)點的左子節(jié)點,當左子節(jié)點為空的時候砚亭,就進行pop操作灯变,獲取父節(jié)點,根據父節(jié)點獲取右子節(jié)點捅膘。

    func formerSequenceTraversal(root:ZWTreeNode?) -> [Int] {
        var arr = [Int]()
        var stack = [ZWTreeNode]()
        var node = root
        //代碼邏輯的思路就是先遍歷節(jié)點的左子節(jié)點添祸,當左子節(jié)點為空的時候,就進行pop操作寻仗,獲取父節(jié)點膝捞,根據父節(jié)點獲取右子節(jié)點。
        while stack.isEmpty == false || node != nil{
            if node != nil {
                arr.append(node!.val)
                stack.append(node!)//push操作愧沟,進入子節(jié)點
                node = node?.left
            }else{
                node = stack.removeLast().right//pop操作蔬咬,返回上一節(jié)點
            }
        }
        return arr
    }

前序遍歷調用形式。

        let subLeftLeftNode = ZWTreeNode(val: 4)
        let subLeftRightNode = ZWTreeNode(val: 5)
        let leftNode = ZWTreeNode(val: 1)
        leftNode.left = subLeftLeftNode
        leftNode.right = subLeftRightNode
        
        let rightNode = ZWTreeNode(val: 3)
        let root = ZWTreeNode(val: 2)
        root.left = leftNode
        root.right = rightNode
        print(formerSequenceTraversal(root: root))//打印結果為:[2, 1, 4, 5, 3]

二沐寺、二分搜索

2.1 概念

一個有序數組中林艘,如果查找某個特定的元素』煳耄可以先從中間的元素開始尋找狐援,如果中間元素是要找的元素,直接返回究孕;如果中間元素小于目標元素啥酱,則目標原始在大于中間元素的那一邊;如果中間元素大于目標元素厨诸,則目標元素在小于中間元素的那一邊镶殷。按照上面的三種情況無限的循環(huán)下去,最終就能找到目標元素微酬。算法的時間復雜度為O(logn)绘趋。

2.2 簡單實現

接下來看看如果在一個 Int 型有升序數組中,檢測是否存在給定的目標值颗管。代碼如下陷遮。

     //實現代碼
    func binarySearch(arr: [Int], target: Int) -> Bool {
        var left = 0
        var mid = 0
        var right = arr.count - 1
        while left <= right {
            mid = (right - left) / 2 + left
            if arr[mid] == target {
                return true
            } else if arr[mid] < target {
                left = mid + 1
            } else {
                right = mid - 1
            }
        }
        return false
    }
//調用形式
let arr = [1,2,3,4,5,6,7,8]
 print(binarySearch(arr: arr, target: 2))//打印結果為:true

總的來說代碼實現起來比較簡單,但是要注意到一點絕對不寫寫成mid = (right + left) / 2,否則可能發(fā)生崩潰垦江,因為當搜索結果在右邊范圍的時候可能出現越界的情況帽馋,最正確的寫法應當是mid = (right - left) / 2 + left。如果想獲取到目標元素的下表也很簡單,只要簡單改寫一下即可绽族。如下代碼姨涡,其中如果返回值為 -1 ,則表示不存在目標元素项秉。

    func binarySearch(arr: [Int], target: Int) -> Int {
        var left = 0
        var mid = 0
        var right = arr.count - 1
        while left <= right {
            mid = (right - left) / 2 + left
            if arr[mid] == target {
                return mid
            } else if arr[mid] < target {
                left = mid + 1
            } else {
                right = mid - 1
            }
        }
        return -1
    }
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末绣溜,一起剝皮案震驚了整個濱河市慷彤,隨后出現的幾起案子娄蔼,更是在濱河造成了極大的恐慌,老刑警劉巖底哗,帶你破解...
    沈念sama閱讀 221,635評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件岁诉,死亡現場離奇詭異,居然都是意外死亡跋选,警方通過查閱死者的電腦和手機涕癣,發(fā)現死者居然都...
    沈念sama閱讀 94,543評論 3 399
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來前标,“玉大人坠韩,你說我怎么就攤上這事×读校” “怎么了只搁?”我有些...
    開封第一講書人閱讀 168,083評論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長俭尖。 經常有香客問我氢惋,道長,這世上最難降的妖魔是什么稽犁? 我笑而不...
    開封第一講書人閱讀 59,640評論 1 296
  • 正文 為了忘掉前任焰望,我火速辦了婚禮,結果婚禮上已亥,老公的妹妹穿的比我還像新娘熊赖。我一直安慰自己,他們只是感情好虑椎,可當我...
    茶點故事閱讀 68,640評論 6 397
  • 文/花漫 我一把揭開白布秫舌。 她就那樣靜靜地躺著,像睡著了一般绣檬。 火紅的嫁衣襯著肌膚如雪足陨。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,262評論 1 308
  • 那天娇未,我揣著相機與錄音墨缘,去河邊找鬼。 笑死,一個胖子當著我的面吹牛镊讼,可吹牛的內容都是我干的病蛉。 我是一名探鬼主播,決...
    沈念sama閱讀 40,833評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼那婉,長吁一口氣:“原來是場噩夢啊……” “哼塑荒!你這毒婦竟也來了?” 一聲冷哼從身側響起玩裙,我...
    開封第一講書人閱讀 39,736評論 0 276
  • 序言:老撾萬榮一對情侶失蹤兼贸,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后吃溅,有當地人在樹林里發(fā)現了一具尸體溶诞,經...
    沈念sama閱讀 46,280評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 38,369評論 3 340
  • 正文 我和宋清朗相戀三年决侈,在試婚紗的時候發(fā)現自己被綠了螺垢。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,503評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡赖歌,死狀恐怖枉圃,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情庐冯,我是刑警寧澤孽亲,帶...
    沈念sama閱讀 36,185評論 5 350
  • 正文 年R本政府宣布,位于F島的核電站肄扎,受9級特大地震影響墨林,放射性物質發(fā)生泄漏。R本人自食惡果不足惜犯祠,卻給世界環(huán)境...
    茶點故事閱讀 41,870評論 3 333
  • 文/蒙蒙 一旭等、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧衡载,春花似錦搔耕、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,340評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至梨睁,卻和暖如春鲸睛,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背坡贺。 一陣腳步聲響...
    開封第一講書人閱讀 33,460評論 1 272
  • 我被黑心中介騙來泰國打工官辈, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留箱舞,地道東北人。 一個月前我還...
    沈念sama閱讀 48,909評論 3 376
  • 正文 我出身青樓拳亿,卻偏偏與公主長得像晴股,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子肺魁,可洞房花燭夜當晚...
    茶點故事閱讀 45,512評論 2 359

推薦閱讀更多精彩內容

  • 樹的概述 樹是一種非常常用的數據結構电湘,樹與前面介紹的線性表,棧鹅经,隊列等線性結構不同寂呛,樹是一種非線性結構 1.樹的定...
    Jack921閱讀 4,462評論 1 31
  • 數據結構和算法--二叉樹的實現 幾種二叉樹 1、二叉樹 和普通的樹相比瞬雹,二叉樹有如下特點: 每個結點最多只有兩棵子...
    sunhaiyu閱讀 6,475評論 0 14
  • 四酗捌、樹與二叉樹 1. 二叉樹的順序存儲結構 二叉樹的順序存儲就是用數組存儲二叉樹。二叉樹的每個結點在順序存儲中都有...
    MinoyJet閱讀 1,548評論 0 7
  • B樹的定義 一棵m階的B樹滿足下列條件: 樹中每個結點至多有m個孩子涌哲。 除根結點和葉子結點外胖缤,其它每個結點至少有m...
    文檔隨手記閱讀 13,240評論 0 25
  • 目錄 0.樹0.1 一般樹的定義0.2 二叉樹的定義 1.查找樹ADT 2.查找樹的實現2.1 二叉查找樹2.2 ...
    王偵閱讀 7,245評論 0 3