【iOS】二叉樹的各種問題(OC代碼)

昨天學習了一下算法,找了幾個例子,總結到一個demo里面。

什么是二叉樹?

二叉樹算法主要是遞歸的思想,維基百科上對遞歸的定義:在計算機科學中是指一種通過重復將問題分解為同類的子問題而解決問題的方法。我理解的話就是好像一個包子餡的包子,然后包子里面還是包子餡,一直循環下去,最后找到出口就是包子里面是個饅頭。

在計算機科學中,二叉樹是每個節點最多有兩個子樹的樹結構。通常子樹被稱作“左子樹”和“右子樹”,左子樹和右子樹同時也是二叉樹。二叉樹的子樹有左右之分,并且次序不能任意顛倒。二叉樹是遞歸定義的,所以一般二叉樹的相關題目也都可以使用遞歸的思想來解決,當然也有一些可以使用非遞歸的思想解決,我下面列出的一些算法有些采用了遞歸,有些是非遞歸的。

什么是二叉排序樹?

二叉排序樹又叫二叉查找樹或者二叉搜索樹,它首先是一個二叉樹,而且必須滿足下面的條件:

1)若左子樹不空,則左子樹上所有結點的值均小于它的根節點的值;

2)若右子樹不空,則右子樹上所有結點的值均大于它的根結點的值

3)左、右子樹也分別為二叉排序樹

4)沒有鍵值相等的節點

下面是具體的代碼,BinaryTreeNode.h文件

@interface BinaryTreeNode : NSObject
@property (nonatomic, assign) NSInteger value;
@property (nonatomic, strong) BinaryTreeNode *leftNode;
@property (nonatomic, strong) BinaryTreeNode *rightNode;
//創建二叉樹
+ (BinaryTreeNode *)createTreeWithValues:(NSArray *)values;
//二叉樹中某個位置的節點(按層次遍歷)
+ (BinaryTreeNode *)treeNodeAtIndex:(NSInteger)index inTree:(BinaryTreeNode *)rootNode;
//向二叉排序樹節點添加一個節點
+ (BinaryTreeNode *)addTreeNode:(BinaryTreeNode *)treeNode value:(NSInteger)value;
//翻轉二叉樹
+ (BinaryTreeNode *)invertBinaryTree:(BinaryTreeNode *)rootNode;
// 非遞歸方式翻轉
+ (BinaryTreeNode *)invertBinaryTreeNot:(BinaryTreeNode *)rootNode;
//先序遍歷:先訪問根,再遍歷左子樹,再遍歷右子樹。典型的遞歸思想。
+ (void)preOrderTraverseTree:(BinaryTreeNode *)rootNode handler:(void(^)(BinaryTreeNode *treeNode))handler;
//中序遍歷:先遍歷左子樹,再訪問根,再遍歷右子樹
+ (void)inOrderTraverseTree:(BinaryTreeNode *)rootNode handler:(void(^)(BinaryTreeNode *treeNode))handler;
//后序遍歷:先遍歷左子樹,再遍歷右子樹,再訪問根
+ (void)postOrderTraverseTree:(BinaryTreeNode *)rootNode handler:(void(^)(BinaryTreeNode *treeNode))handler;
//層次遍歷(廣度優先)
+ (void)levelTraverseTree:(BinaryTreeNode *)rootNode handler:(void(^)(BinaryTreeNode *treeNode))handler;
//二叉樹的寬度
+ (NSInteger)widthOfTree:(BinaryTreeNode *)rootNode;
//二叉樹的所有節點數
+ (NSInteger)numberOfNodesInTree:(BinaryTreeNode *)rootNode;
//二叉樹某層中的節點數
+ (NSInteger)numberOfNodesOnLevel:(NSInteger)level inTree:(BinaryTreeNode *)rootNode;

//二叉樹葉子節點數
+ (NSInteger)numberOfLeafsInTree:(BinaryTreeNode *)rootNode;
//二叉樹最大距離(直徑)
+ (NSInteger)maxDistanceOfTree:(BinaryTreeNode *)rootNode;

@end

BinaryTreeNode.m文件

@implementation BinaryTreeNode
/**
 *  創建二叉排序樹
 *  二叉排序樹:左節點值全部小于根節點值,右節點值全部大于根節點值
 *
 *  @param values 數組
 *
  *  @return 二叉樹根節點
 */
+ (BinaryTreeNode *)createTreeWithValues:(NSArray *)values {
    BinaryTreeNode *root = nil;
    for (NSInteger i=0; i<values.count; i++) {
        NSInteger value = [(NSNumber *)[values objectAtIndex:i] integerValue];
        root = [BinaryTreeNode addTreeNode:root value:value];
    }
    return root;
}

/**
 *  向二叉排序樹節點添加一個節點
 *
 *  @param treeNode 根節點
 *  @param value    值
 *
 *  @return 根節點
 */
+ (BinaryTreeNode *)addTreeNode:(BinaryTreeNode *)treeNode value:(NSInteger)value {
    //根節點不存在,創建節點
    if (!treeNode) {
        treeNode = [BinaryTreeNode new];
        treeNode.value = value;
        NSLog(@"node:%@", @(value));
    }
    else if (value <= treeNode.value) {
        NSLog(@"to left");
        //值小于根節點,則插入到左子樹
        treeNode.leftNode = [BinaryTreeNode addTreeNode:treeNode.leftNode value:value];
    }
    else {
        NSLog(@"to right");
        //值大于根節點,則插入到右子樹
        treeNode.rightNode = [BinaryTreeNode addTreeNode:treeNode.rightNode value:value];
    }
    return treeNode;
}
/**
 * 翻轉二叉樹(又叫:二叉樹的鏡像)
 *
 * @param rootNode 根節點
 *
 * @return 翻轉后的樹根節點(其實就是原二叉樹的根節點)
 */
+ (BinaryTreeNode *)invertBinaryTree:(BinaryTreeNode *)rootNode {
    if (!rootNode) {
        return nil;
    }
    if (!rootNode.leftNode && !rootNode.rightNode) {
        return rootNode;
    }
    [BinaryTreeNode invertBinaryTree:rootNode.leftNode];
    [BinaryTreeNode invertBinaryTree:rootNode.rightNode];
    BinaryTreeNode *tempNode = rootNode.leftNode;
    rootNode.leftNode = rootNode.rightNode;
    rootNode.rightNode = tempNode;
    return rootNode;
}

/**
 *    非遞歸方式翻轉
 */
  + (BinaryTreeNode *)invertBinaryTreeNot:(BinaryTreeNode *)rootNode {
    if (!rootNode) {  return nil; }
    if (!rootNode.leftNode && !rootNode.rightNode) {  return rootNode; }
    NSMutableArray *queueArray = [NSMutableArray array]; //數組當成隊列
    [queueArray addObject:rootNode]; //壓入根節點
    while (queueArray.count > 0) {
        BinaryTreeNode *node = [queueArray firstObject];
        [queueArray removeObjectAtIndex:0]; //彈出最前面的節點,仿照隊列先進先出原則
    
        BinaryTreeNode *pLeft = node.leftNode;
        node.leftNode = node.rightNode;
        node.rightNode = pLeft;
    
        if (node.leftNode) {
            [queueArray addObject:node.leftNode];
        }
        if (node.rightNode) {
            [queueArray addObject:node.rightNode];
        }
    
    }

    return rootNode;
}

/**
 *  二叉樹中某個位置的節點(按層次遍歷)
 *
 *  @param index    按層次遍歷樹時的位置(從0開始算)
 *  @param rootNode 樹根節點
 *
 *  @return 節點
 */
+ (BinaryTreeNode *)treeNodeAtIndex:(NSInteger)index inTree:(BinaryTreeNode *)rootNode {
    //按層次遍歷
    if (!rootNode || index < 0) {
        return nil;
    }
    //數組當成隊列
    NSMutableArray *queueArray = [NSMutableArray array];
    //壓入根節點
    [queueArray addObject:rootNode];
    while (queueArray.count > 0) {
        BinaryTreeNode *node = [queueArray firstObject];
        if (index == 0) {
            return node;
        }
       [queueArray removeObjectAtIndex:0]; //彈出最前面的節點,仿照隊列先進先出原則
        //移除節點,index減少
        index--;
        if (node.leftNode) {
            [queueArray addObject:node.leftNode]; //壓入左節點
        }
        if (node.rightNode) {
            [queueArray addObject:node.rightNode]; //壓入右節點
        }
    
    }
    //層次遍歷完,仍然沒有找到位置,返回nil
    return nil;
}

/**
 *  先序遍歷:先訪問根,再遍歷左子樹,再遍歷右子樹。典型的遞歸思想。
 *
 *  @param rootNode 根節點
 *  @param handler  訪問節點處理函數
 */
+ (void)preOrderTraverseTree:(BinaryTreeNode *)rootNode handler:(void(^)(BinaryTreeNode *treeNode))handler {
    if (rootNode) {
    
        if (handler) {
            handler(rootNode);
        }
      
        [BinaryTreeNode preOrderTraverseTree:rootNode.leftNode handler:handler];
        [BinaryTreeNode preOrderTraverseTree:rootNode.rightNode handler:handler];
    }
}

/**
 *  中序遍歷
 *  先遍歷左子樹,再訪問根,再遍歷右子樹
 *
 *  @param rootNode 根節點
 *  @param handler  訪問節點處理函數
 */
+ (void)inOrderTraverseTree:(BinaryTreeNode *)rootNode handler:(void(^)  (BinaryTreeNode *treeNode))handler {
    if (rootNode) {
        [BinaryTreeNode inOrderTraverseTree:rootNode.leftNode handler:handler];
    
        if (handler) {
            handler(rootNode);
        }
    
        [BinaryTreeNode inOrderTraverseTree:rootNode.rightNode handler:handler];
    }
}

/**
 *  后序遍歷
 *  先遍歷左子樹,再遍歷右子樹,再訪問根
 *
 *  @param rootNode 根節點
 *  @param handler  訪問節點處理函數
 */
+ (void)postOrderTraverseTree:(BinaryTreeNode *)rootNode handler:(void(^)(BinaryTreeNode *treeNode))handler {
    if (rootNode) {
        [BinaryTreeNode postOrderTraverseTree:rootNode.leftNode handler:handler];
        [BinaryTreeNode postOrderTraverseTree:rootNode.rightNode handler:handler];
        if (handler) {
            handler(rootNode);
        }
    }
}


/**
 *  層次遍歷(廣度優先)
 *
 *  @param rootNode 二叉樹根節點
 *  @param handler  訪問節點處理函數
 */
+ (void)levelTraverseTree:(BinaryTreeNode *)rootNode handler:(void(^)(BinaryTreeNode *treeNode))handler {
    if (!rootNode) {
        return;
    }
    NSMutableArray *queueArray = [NSMutableArray array]; //數組當成隊列
    [queueArray addObject:rootNode]; //壓入根節點
    while (queueArray.count > 0) {
        BinaryTreeNode *node = [queueArray firstObject];
        if (handler) {
            handler(node);
        }
        [queueArray removeObjectAtIndex:0]; //彈出最前面的節點,仿照隊列先進先 出原則
        if (node.leftNode) {
            [queueArray addObject:node.leftNode]; //壓入左節點
        }
        if (node.rightNode) {
            [queueArray addObject:node.rightNode]; //壓入右節點
        }
    }
}

/**
 *  二叉樹的深度
 *
*  @param rootNode 二叉樹根節點
 *
 *  @return 二叉樹的深度
 */
+ (NSInteger)depthOfTree:(BinaryTreeNode *)rootNode {
    if (!rootNode) {
        return 0;
    }
    if (!rootNode.leftNode && !rootNode.rightNode) {
        return 1;
    }

    //左子樹深度
    NSInteger leftDepth = [BinaryTreeNode depthOfTree:rootNode.leftNode];
    //右子樹深度
    NSInteger rightDepth = [BinaryTreeNode depthOfTree:rootNode.rightNode];

    return MAX(leftDepth, rightDepth) + 1;
}

/**
 *  二叉樹的寬度
 *
 *  @param rootNode 二叉樹根節點
 *
 *  @return 二叉樹寬度
 */
+ (NSInteger)widthOfTree:(BinaryTreeNode *)rootNode {
    if (!rootNode) {
        return 0;
    }

    NSMutableArray *queueArray = [NSMutableArray array]; //數組當成隊列
    [queueArray addObject:rootNode]; //壓入根節點
    NSInteger maxWidth = 1; //最大的寬度,初始化為1(因為已經有根節點)
    NSInteger curWidth = 0; //當前層的寬度

    while (queueArray.count > 0) {
    
        curWidth = queueArray.count;
        //依次彈出當前層的節點
        for (NSInteger i=0; i<curWidth; i++) {
            BinaryTreeNode *node = [queueArray firstObject];
            [queueArray removeObjectAtIndex:0]; //彈出最前面的節點,仿照隊列先進先出原則
            //壓入子節點
            if (node.leftNode) {
                [queueArray addObject:node.leftNode];
            }
            if (node.rightNode) {
                [queueArray addObject:node.rightNode];
            }
        }
        //寬度 = 當前層節點數
        maxWidth = MAX(maxWidth, queueArray.count);
    }

    return maxWidth;
}    

  /**
 *  二叉樹的所有節點數
 *
 *  @param rootNode 根節點
 *
 *  @return 所有節點數
 */
+ (NSInteger)numberOfNodesInTree:(BinaryTreeNode *)rootNode {
    if (!rootNode) {
        return 0;
    }
    //節點數=左子樹節點數+右子樹節點數+1(根節點)
    return [BinaryTreeNode numberOfNodesInTree:rootNode.leftNode] + [BinaryTreeNode numberOfNodesInTree:rootNode.rightNode] + 1;
}

/**
 *  二叉樹某層中的節點數
 *
 *  @param level    層
 *  @param rootNode 根節點
 *
 *  @return 層中的節點數
 */
+ (NSInteger)numberOfNodesOnLevel:(NSInteger)level inTree:(BinaryTreeNode *)rootNode {
    if (!rootNode || level < 1) { //根節點不存在或者level<0
        return 0;
    }
    if (level == 1) { //level=1,返回1(根節點)
        return 1;
    }
    //遞歸:level層節點數 = 左子樹level-1層節點數+右子樹level-1層節點數
    return [BinaryTreeNode numberOfNodesOnLevel:level-1 inTree:rootNode.leftNode] + [BinaryTreeNode numberOfNodesOnLevel:level-1 inTree:rootNode.rightNode];
}

/**
 *  二叉樹葉子節點數
 *
 *  @param rootNode 根節點
 *
*  @return 葉子節點數
 */
+ (NSInteger)numberOfLeafsInTree:(BinaryTreeNode *)rootNode {
    if (!rootNode) {
          return 0;
    }
    //左子樹和右子樹都是空,說明是葉子節點
    if (!rootNode.leftNode && !rootNode.rightNode) {
        return 1;
    }
    //遞歸:葉子數 = 左子樹葉子數 + 右子樹葉子數
    return [BinaryTreeNode numberOfLeafsInTree:rootNode.leftNode] + [BinaryTreeNode numberOfLeafsInTree:rootNode.rightNode];
}

/**
 *  二叉樹最大距離(直徑)
 *
 *  @param rootNode 根節點
 *
 *  @return 最大距離
 */
+ (NSInteger)maxDistanceOfTree:(BinaryTreeNode *)rootNode {
    if (!rootNode) {
        return 0;
    }
    //    方案一:(遞歸次數較多,效率較低)
    //分3種情況:
    //1、最遠距離經過根節點:距離 = 左子樹深度 + 右子樹深度
    NSInteger distance = [BinaryTreeNode depthOfTree:rootNode.leftNode] + [BinaryTreeNode depthOfTree:rootNode.rightNode];
   //2、最遠距離在根節點左子樹上,即計算左子樹最遠距離
    NSInteger disLeft = [BinaryTreeNode maxDistanceOfTree:rootNode.leftNode];
    //3、最遠距離在根節點右子樹上,即計算右子樹最遠距離
    NSInteger disRight = [BinaryTreeNode maxDistanceOfTree:rootNode.rightNode];

    return MAX(MAX(disLeft, disRight), distance);
}

@end

在其他頁面調用部分方法代碼:

    NSArray *arr = [NSArray arrayWithObjects:@(7),@(6),@(3),@(2),@(1),@(9),@(10),@(12),@(14),@(4),@(14), nil];
    BinaryTreeNode *tree = [BinaryTreeNode new];
    tree = [BinaryTreeNode createTreeWithValues:arr];
    
    BinaryTreeNode *tree1 = [BinaryTreeNode treeNodeAtIndex:3 inTree:tree];
    NSLog(@"%@",tree1);
    
    NSMutableArray *orderArray = [NSMutableArray array];
    [BinaryTreeNode preOrderTraverseTree:tree handler:^(BinaryTreeNode *treeNode) {
        [orderArray addObject:@(treeNode.value)];
    }];
    NSLog(@"先序遍歷結果:%@", [orderArray componentsJoinedByString:@","]);
    
    NSMutableArray *orderArray1 = [NSMutableArray array];
   [BinaryTreeNode inOrderTraverseTree:tree handler:^(BinaryTreeNode *treeNode) {
        
        [orderArray1 addObject:@(treeNode.value)];
    
    }];
    NSLog(@"中序遍歷結果:%@", [orderArray1 componentsJoinedByString:@","]);
    
    
    NSMutableArray *orderArray2 = [NSMutableArray array];
    [BinaryTreeNode postOrderTraverseTree:tree handler:^(BinaryTreeNode *treeNode) {
        [orderArray2 addObject:@(treeNode.value)];
        
    }];
    NSLog(@"后序遍歷結果:%@", [orderArray2 componentsJoinedByString:@","]);
    
    
    NSMutableArray *orderArray3 = [NSMutableArray array];
    [BinaryTreeNode levelTraverseTree:tree handler:^(BinaryTreeNode *treeNode) {
        [orderArray3 addObject:@(treeNode.value)];
        
    }];
    NSLog(@"層次遍歷結果:%@", [orderArray3 componentsJoinedByString:@","]);
    

    NSArray *arr = [NSArray arrayWithObjects:@(7),@(6),@(3),@(2),@(1),@(9),@(10),@(12),@(14),@(4),@(14), nil];
    BinaryTreeNode *tree = [BinaryTreeNode new];
    tree = [BinaryTreeNode createTreeWithValues:arr];
    
    BinaryTreeNode *tree1 = [BinaryTreeNode treeNodeAtIndex:3 inTree:tree];
    NSLog(@"%@",tree1);
    
    NSMutableArray *orderArray = [NSMutableArray array];
    [BinaryTreeNode preOrderTraverseTree:tree handler:^(BinaryTreeNode *treeNode) {
        [orderArray addObject:@(treeNode.value)];
    }];
    NSLog(@"先序遍歷結果:%@", [orderArray componentsJoinedByString:@","]);
    
    NSMutableArray *orderArray1 = [NSMutableArray array];
   [BinaryTreeNode inOrderTraverseTree:tree handler:^(BinaryTreeNode *treeNode) {
        
        [orderArray1 addObject:@(treeNode.value)];
    
    }];
    NSLog(@"中序遍歷結果:%@", [orderArray1 componentsJoinedByString:@","]);
    
    
    NSMutableArray *orderArray2 = [NSMutableArray array];
    [BinaryTreeNode postOrderTraverseTree:tree handler:^(BinaryTreeNode *treeNode) {
        [orderArray2 addObject:@(treeNode.value)];
        
    }];
    NSLog(@"后序遍歷結果:%@", [orderArray2 componentsJoinedByString:@","]);
    
    
    NSMutableArray *orderArray3 = [NSMutableArray array];
    [BinaryTreeNode levelTraverseTree:tree handler:^(BinaryTreeNode *treeNode) {
        [orderArray3 addObject:@(treeNode.value)];
        
    }];
    NSLog(@"層次遍歷結果:%@", [orderArray3 componentsJoinedByString:@","]);
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,836評論 6 540
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,275評論 3 428
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 177,904評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,633評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,368評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,736評論 1 328
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,740評論 3 446
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,919評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,481評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,235評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,427評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,968評論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,656評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,055評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,348評論 1 294
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,160評論 3 398
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,380評論 2 379

推薦閱讀更多精彩內容