OC二叉樹(shù)相關(guān)操作

二叉樹(shù)-你必須要懂!(二叉樹(shù)相關(guān)算法實(shí)現(xiàn)-iOS)

http://www.cnblogs.com/manji/p/4903990.html原文鏈接

(http://blog.devtang.com/blog/2015/06/16/talk-about-tech-interview/))

什么是二叉樹(shù)?

在計(jì)算機(jī)科學(xué)中,二叉樹(shù)是每個(gè)節(jié)點(diǎn)最多有兩個(gè)子樹(shù)的樹(shù)結(jié)構(gòu)。通常子樹(shù)被稱作“左子樹(shù)”和“右子樹(shù)”,左子樹(shù)和右子樹(shù)同時(shí)也是二叉樹(shù)。二叉樹(shù)的子樹(shù)有左右之分,并且次序不能任意顛倒。二叉樹(shù)是遞歸定義的,所以一般二叉樹(shù)的相關(guān)題目也都可以使用遞歸的思想來(lái)解決,當(dāng)然也有一些可以使用非遞歸的思想解決,我下面列出的一些算法有些采用了遞歸,有些是非遞歸的。

什么是二叉排序樹(shù)?

二叉排序樹(shù)又叫二叉查找樹(shù)或者二叉搜索樹(shù),它首先是一個(gè)二叉樹(shù),而且必須滿足下面的條件:
1)若左子樹(shù)不空,則左子樹(shù)上所有結(jié)點(diǎn)的值均小于它的根節(jié)點(diǎn)的值;
2)若右子樹(shù)不空,則右子樹(shù)上所有結(jié)點(diǎn)的值均大于它的根結(jié)點(diǎn)的值
3)左、右子樹(shù)也分別為二叉排序樹(shù)
4)沒(méi)有鍵值相等的節(jié)點(diǎn)(?可能是因?yàn)椴缓锰幚礞I值相等的節(jié)點(diǎn)到底是左節(jié)點(diǎn)還是右節(jié)點(diǎn)吧)
概念就介紹這么多,都是來(lái)自網(wǎng)上,下面主要看算法和具體實(shí)現(xiàn)代碼。

二叉樹(shù)節(jié)點(diǎn)定義

采用單項(xiàng)鏈表的形式,只從根節(jié)點(diǎn)指向孩子節(jié)點(diǎn),不保存父節(jié)點(diǎn)。

/**
 *  二叉樹(shù)節(jié)點(diǎn)
 */
@interface BinaryTreeNode : NSObject

/**
 *  值
 */
@property (nonatomic, assign) NSInteger value;
/**
 *  左節(jié)點(diǎn)
 */
@property (nonatomic, strong) BinaryTreeNode *leftNode;
/**
 *  右節(jié)點(diǎn)
 */
@property (nonatomic, strong) BinaryTreeNode *rightNode;

@end

創(chuàng)建二叉排序樹(shù)

二叉樹(shù)中左右節(jié)點(diǎn)值本身沒(méi)有大小之分,所以如果要?jiǎng)?chuàng)建二叉樹(shù),就需要考慮如何處理某個(gè)節(jié)點(diǎn)是左節(jié)點(diǎn)還是右節(jié)點(diǎn),如何終止某個(gè)子樹(shù)而切換到另一個(gè)子樹(shù)。 因此我選擇了二叉排序樹(shù),二叉排序樹(shù)中對(duì)于左右節(jié)點(diǎn)有明確的要求,程序可以自動(dòng)根據(jù)鍵值大小自動(dòng)選擇是左節(jié)點(diǎn)還是右節(jié)點(diǎn)。

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

/**
 *  向二叉排序樹(shù)節(jié)點(diǎn)添加一個(gè)節(jié)點(diǎn)
 *
 *  @param treeNode 根節(jié)點(diǎn)
 *  @param value    值
 *
 *  @return 根節(jié)點(diǎn)
 */
+ (BinaryTreeNode *)addTreeNode:(BinaryTreeNode *)treeNode value:(NSInteger)value {
    //根節(jié)點(diǎn)不存在,創(chuàng)建節(jié)點(diǎn)
    if (!treeNode) {
        treeNode = [BinaryTreeNode new];
        treeNode.value = value;
        NSLog(@"node:%@", @(value));
    }
    else if (value <= treeNode.value) {
        NSLog(@"to left");
        //值小于根節(jié)點(diǎn),則插入到左子樹(shù)
        treeNode.leftNode = [BinaryTree addTreeNode:treeNode.leftNode value:value];
    }
    else {
        NSLog(@"to right");
        //值大于根節(jié)點(diǎn),則插入到右子樹(shù)
        treeNode.rightNode = [BinaryTree addTreeNode:treeNode.rightNode value:value];
    }
    
    return treeNode;
}

二叉樹(shù)中某個(gè)位置的節(jié)點(diǎn)

類似索引操作,按層次遍歷,位置從0開(kāi)始算。

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

先序遍歷

先訪問(wèn)根,再遍歷左子樹(shù),再遍歷右子樹(shù)。典型的遞歸思想。

/**
 *  先序遍歷
 *  先訪問(wèn)根,再遍歷左子樹(shù),再遍歷右子樹(shù)
 *
 *  @param rootNode 根節(jié)點(diǎn)
 *  @param handler  訪問(wèn)節(jié)點(diǎn)處理函數(shù)
 */
+ (void)preOrderTraverseTree:(BinaryTreeNode *)rootNode handler:(void(^)(BinaryTreeNode *treeNode))handler {
    if (rootNode) {
        
        if (handler) {
            handler(rootNode);
        }
        
        [self preOrderTraverseTree:rootNode.leftNode handler:handler];
        [self preOrderTraverseTree:rootNode.rightNode handler:handler];
    }
}

調(diào)用方法如下:(用到了block)

NSMutableArray *orderArray = [NSMutableArray array];
[BinaryTree preOrderTraverseTree:root handler:^(BinaryTreeNode *treeNode) {
     [orderArray addObject:@(treeNode.value)];
}];
NSLog(@"先序遍歷結(jié)果:%@", [orderArray componentsJoinedByString:@","]);

中序遍歷

先遍歷左子樹(shù),再訪問(wèn)根,再遍歷右子樹(shù)。
對(duì)于二叉排序樹(shù)來(lái)說(shuō),中序遍歷得到的序列是一個(gè)從小到大排序好的序列。

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

后序遍歷

先遍歷左子樹(shù),再遍歷右子樹(shù),再訪問(wèn)根

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

層次遍歷

按照從上到下、從左到右的次序進(jìn)行遍歷。先遍歷完一層,再遍歷下一層,因此又叫廣度優(yōu)先遍歷。需要用到隊(duì)列,在OC里可以用可變數(shù)組來(lái)實(shí)現(xiàn)。

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

二叉樹(shù)的深度

二叉樹(shù)的深度定義為:從根節(jié)點(diǎn)到葉子結(jié)點(diǎn)依次經(jīng)過(guò)的結(jié)點(diǎn)形成樹(shù)的一條路徑,最長(zhǎng)路徑的長(zhǎng)度為樹(shù)的深度。

1)如果根節(jié)點(diǎn)為空,則深度為0;

2)如果左右節(jié)點(diǎn)都是空,則深度為1;

3)遞歸思想:二叉樹(shù)的深度=max(左子樹(shù)的深度,右子樹(shù)的深度)+ 1

/**
 *  二叉樹(shù)的深度
 *
 *  @param rootNode 二叉樹(shù)根節(jié)點(diǎn)
 *
 *  @return 二叉樹(shù)的深度
 */
+ (NSInteger)depthOfTree:(BinaryTreeNode *)rootNode {
    if (!rootNode) {
        return 0;
    }
    if (!rootNode.leftNode && !rootNode.rightNode) {
        return 1;
    }
    
    //左子樹(shù)深度
    NSInteger leftDepth = [self depthOfTree:rootNode.leftNode];
    //右子樹(shù)深度
    NSInteger rightDepth = [self depthOfTree:rootNode.rightNode];
    
    return MAX(leftDepth, rightDepth) + 1;
}

二叉樹(shù)的寬度

二叉樹(shù)的寬度定義為各層節(jié)點(diǎn)數(shù)的最大值。

/**
 *  二叉樹(shù)的寬度
 *
 *  @param rootNode 二叉樹(shù)根節(jié)點(diǎn)
 *
 *  @return 二叉樹(shù)寬度
 */
+ (NSInteger)widthOfTree:(BinaryTreeNode *)rootNode {
    if (!rootNode) {
        return 0;
    }
    
    NSMutableArray *queueArray = [NSMutableArray array]; //數(shù)組當(dāng)成隊(duì)列
    [queueArray addObject:rootNode]; //壓入根節(jié)點(diǎn)
    NSInteger maxWidth = 1; //最大的寬度,初始化為1(因?yàn)橐呀?jīng)有根節(jié)點(diǎn))
    NSInteger curWidth = 0; //當(dāng)前層的寬度
    
    while (queueArray.count > 0) {
        
        curWidth = queueArray.count;
        //依次彈出當(dāng)前層的節(jié)點(diǎn)
        for (NSInteger i=0; i<curWidth; i++) {
            BinaryTreeNode *node = [queueArray firstObject];
            [queueArray removeObjectAtIndex:0]; //彈出最前面的節(jié)點(diǎn),仿照隊(duì)列先進(jìn)先出原則
            //壓入子節(jié)點(diǎn)
            if (node.leftNode) {
                [queueArray addObject:node.leftNode];
            }
            if (node.rightNode) {
                [queueArray addObject:node.rightNode];
            }
        }
        //寬度 = 當(dāng)前層節(jié)點(diǎn)數(shù)
        maxWidth = MAX(maxWidth, queueArray.count);
    }
    
    return maxWidth;
}

二叉樹(shù)的所有節(jié)點(diǎn)數(shù)

遞歸思想:二叉樹(shù)所有節(jié)點(diǎn)數(shù)=左子樹(shù)節(jié)點(diǎn)數(shù)+右子樹(shù)節(jié)點(diǎn)數(shù)+1

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

二叉樹(shù)某層中的節(jié)點(diǎn)數(shù)

1)根節(jié)點(diǎn)為空,則節(jié)點(diǎn)數(shù)為0;

2)層為1,則節(jié)點(diǎn)數(shù)為1(即根節(jié)點(diǎn))

3)遞歸思想:二叉樹(shù)第k層節(jié)點(diǎn)數(shù)=左子樹(shù)第k-1層節(jié)點(diǎn)數(shù)+右子樹(shù)第k-1層節(jié)點(diǎn)數(shù)

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

二叉樹(shù)葉子節(jié)點(diǎn)數(shù)

葉子節(jié)點(diǎn),又叫終端節(jié)點(diǎn),是左右子樹(shù)都是空的節(jié)點(diǎn)。

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

二叉樹(shù)最大距離(二叉樹(shù)的直徑)

二叉樹(shù)中任意兩個(gè)節(jié)點(diǎn)都有且僅有一條路徑,這個(gè)路徑的長(zhǎng)度叫這兩個(gè)節(jié)點(diǎn)的距離。二叉樹(shù)中所有節(jié)點(diǎn)之間的距離的最大值就是二叉樹(shù)的直徑。

有一種解法,把這個(gè)最大距離劃分了3種情況:

1)這2個(gè)節(jié)點(diǎn)分別在根節(jié)點(diǎn)的左子樹(shù)和右子樹(shù)上,他們之間的路徑肯定經(jīng)過(guò)根節(jié)點(diǎn),而且他們肯定是根節(jié)點(diǎn)左右子樹(shù)上最遠(yuǎn)的葉子節(jié)點(diǎn)(他們到根節(jié)點(diǎn)的距離=左右子樹(shù)的深度)。

2)這2個(gè)節(jié)點(diǎn)都在左子樹(shù)上

3)這2個(gè)節(jié)點(diǎn)都在右子樹(shù)上

綜上,只要取這3種情況中的最大值,就是二叉樹(shù)的直徑。

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

這個(gè)方案效率較低,因?yàn)橛?jì)算子樹(shù)的深度和最遠(yuǎn)距離是分開(kāi)遞歸的,存在重復(fù)遞歸遍歷的情況。其實(shí)一次遞歸,就可以分別計(jì)算出深度和最遠(yuǎn)距離,于是有了第二種方案:

/**
 *  二叉樹(shù)最大距離(直徑)
 *
 *  @param rootNode 根節(jié)點(diǎn)
 *
 *  @return 最大距離
 */
+ (NSInteger)maxDistanceOfTree:(BinaryTreeNode *)rootNode {
    if (!rootNode) {
        return 0;
    }
//    方案2:將計(jì)算節(jié)點(diǎn)深度和最大距離放到一次遞歸中計(jì)算,方案一是分別單獨(dú)遞歸計(jì)算深度和最遠(yuǎn)距離
    TreeNodeProperty *p = [self propertyOfTreeNode:rootNode];
    return p.distance;
}

/**
 *  計(jì)算樹(shù)節(jié)點(diǎn)的最大深度和最大距離
 *
 *  @param rootNode 根節(jié)點(diǎn)
 *
 *  @return TreeNodeProperty
 */
+ (TreeNodeProperty *)propertyOfTreeNode:(BinaryTreeNode *)rootNode {
    
    if (!rootNode) {
        return nil;
    }
    
    TreeNodeProperty *left = [self propertyOfTreeNode:rootNode.leftNode];
    TreeNodeProperty *right = [self propertyOfTreeNode:rootNode.rightNode];
    TreeNodeProperty *p = [TreeNodeProperty new];
    //節(jié)點(diǎn)的深度depth = 左子樹(shù)深度、右子樹(shù)深度中最大值+1(+1是因?yàn)楦?jié)點(diǎn)占了1個(gè)depth)
    p.depth = MAX(left.depth, right.depth) + 1;
    //最遠(yuǎn)距離 = 左子樹(shù)最遠(yuǎn)距離、右子樹(shù)最遠(yuǎn)距離和橫跨左右子樹(shù)最遠(yuǎn)距離中最大值
    p.distance = MAX(MAX(left.distance, right.distance), left.depth+right.depth);
    
    return p;
}

二叉樹(shù)中某個(gè)節(jié)點(diǎn)到根節(jié)點(diǎn)的路徑

既是尋路問(wèn)題,又是查找節(jié)點(diǎn)問(wèn)題。

定義一個(gè)存放路徑的棧(不是隊(duì)列了,但是還是用可變數(shù)組來(lái)實(shí)現(xiàn)的)

1)壓入根節(jié)點(diǎn),再?gòu)淖笞訕?shù)中查找(遞歸進(jìn)行的),如果未找到,再?gòu)挠易訕?shù)中查找,如果也未找到,則彈出根節(jié)點(diǎn),再遍歷棧中上一個(gè)節(jié)點(diǎn)。

2)如果找到,則棧中存放的節(jié)點(diǎn)就是路徑所經(jīng)過(guò)的節(jié)點(diǎn)。

/**
 *  二叉樹(shù)中某個(gè)節(jié)點(diǎn)到根節(jié)點(diǎn)的路徑
 *
 *  @param treeNode 節(jié)點(diǎn)
 *  @param rootNode 根節(jié)點(diǎn)
 *
 *  @return 存放路徑節(jié)點(diǎn)的數(shù)組
 */
+ (NSArray *)pathOfTreeNode:(BinaryTreeNode *)treeNode inTree:(BinaryTreeNode *)rootNode {
    NSMutableArray *pathArray = [NSMutableArray array];
    [self isFoundTreeNode:treeNode inTree:rootNode routePath:pathArray];
    return pathArray;
}

/**
 *  查找某個(gè)節(jié)點(diǎn)是否在樹(shù)中
 *
 *  @param treeNode 待查找的節(jié)點(diǎn)
 *  @param rootNode 根節(jié)點(diǎn)
 *  @param path  根節(jié)點(diǎn)到待查找節(jié)點(diǎn)的路徑
 *
 *  @return YES:找到,NO:未找到
 */
+ (BOOL)isFoundTreeNode:(BinaryTreeNode *)treeNode inTree:(BinaryTreeNode *)rootNode routePath:(NSMutableArray *)path {
    
    if (!rootNode || !treeNode) {
        return NO;
    }
    
    //找到節(jié)點(diǎn)
    if (rootNode == treeNode) {
        [path addObject:rootNode];
        return YES;
    }
    //壓入根節(jié)點(diǎn),進(jìn)行遞歸
    [path addObject:rootNode];
    //先從左子樹(shù)中查找
    BOOL find = [self isFoundTreeNode:treeNode inTree:rootNode.leftNode routePath:path];
    //未找到,再?gòu)挠易訕?shù)查找
    if (!find) {
        find = [self isFoundTreeNode:treeNode inTree:rootNode.rightNode routePath:path];
    }
    //如果2邊都沒(méi)查找到,則彈出此根節(jié)點(diǎn)
    if (!find) {
        [path removeLastObject];
    }
    
    return find;
}

二叉樹(shù)中兩個(gè)節(jié)點(diǎn)最近的公共父節(jié)點(diǎn)

首先需要明白,根節(jié)點(diǎn)肯定是二叉樹(shù)中任意兩個(gè)節(jié)點(diǎn)的公共父節(jié)點(diǎn)(不一定是最近的),因此二叉樹(shù)中2個(gè)節(jié)點(diǎn)的最近公共父節(jié)點(diǎn)一定在從根節(jié)點(diǎn)到這個(gè)節(jié)點(diǎn)的路徑上。因此我們可以先分別找到從根節(jié)點(diǎn)到這2個(gè)節(jié)點(diǎn)的路徑,再?gòu)倪@兩個(gè)路徑中找到最近的公共父節(jié)點(diǎn)。

/**
 *  二叉樹(shù)中兩個(gè)節(jié)點(diǎn)最近的公共父節(jié)點(diǎn)
 *
 *  @param nodeA    第一個(gè)節(jié)點(diǎn)
 *  @param nodeB    第二個(gè)節(jié)點(diǎn)
 *  @param rootNode 二叉樹(shù)根節(jié)點(diǎn)
 *
 *  @return 最近的公共父節(jié)點(diǎn)
 */
+ (BinaryTreeNode *)parentOfNode:(BinaryTreeNode *)nodeA andNode:(BinaryTreeNode *)nodeB inTree:(BinaryTreeNode *)rootNode {
    if (!rootNode || !nodeA || !nodeB) {
        return nil;
    }
    if (nodeA == nodeB) {
        return nodeA;
    }
    //從根節(jié)點(diǎn)到節(jié)點(diǎn)A的路徑
    NSArray *pathA = [self pathOfTreeNode:nodeA inTree:rootNode];
    //從根節(jié)點(diǎn)到節(jié)點(diǎn)B的路徑
    NSArray *pathB = [self pathOfTreeNode:nodeB inTree:rootNode];
    //其中一個(gè)節(jié)點(diǎn)不在樹(shù)中,則沒(méi)有公共父節(jié)點(diǎn)
    if (pathA.count == 0 || pathB == 0) {
        return nil;
    }
    //從后往前推,查找第一個(gè)出現(xiàn)的公共節(jié)點(diǎn)
    for (NSInteger i = pathA.count-1; i>=0; i--) {
        for (NSInteger j = pathB.count - 1; j>=0; j--) {
            if ([pathA objectAtIndex:i] == [pathB objectAtIndex:j]) {
                //找到
                return [pathA objectAtIndex:i];
            }
        }
    }
    return nil;
}

二叉樹(shù)中兩個(gè)節(jié)點(diǎn)之間的路徑

從查找最近公共父節(jié)點(diǎn)衍生出來(lái)的。

/**
 *  二叉樹(shù)中兩個(gè)節(jié)點(diǎn)之間的路徑
 *
 *  @param nodeA    第一個(gè)節(jié)點(diǎn)
 *  @param nodeB    第二個(gè)節(jié)點(diǎn)
 *  @param rootNode 二叉樹(shù)根節(jié)點(diǎn)
 *
 *  @return 兩個(gè)節(jié)點(diǎn)間的路徑
 */
+ (NSArray *)pathFromNode:(BinaryTreeNode *)nodeA toNode:(BinaryTreeNode *)nodeB inTree:(BinaryTreeNode *)rootNode {
    if (!rootNode || !nodeA || !nodeB) {
        return nil;
    }
    NSMutableArray *path = [NSMutableArray array];
    if (nodeA == nodeB) {
        [path addObject:nodeA];
        [path addObject:nodeB];
        return path;
    }
    //從根節(jié)點(diǎn)到節(jié)點(diǎn)A的路徑
    NSArray *pathA = [self pathOfTreeNode:nodeA inTree:rootNode];
    //從根節(jié)點(diǎn)到節(jié)點(diǎn)B的路徑
    NSArray *pathB = [self pathOfTreeNode:nodeB inTree:rootNode];
    //其中一個(gè)節(jié)點(diǎn)不在樹(shù)中,則沒(méi)有路徑
    if (pathA.count == 0 || pathB == 0) {
        return nil;
    }
    //從后往前推,查找第一個(gè)出現(xiàn)的公共節(jié)點(diǎn)
    for (NSInteger i = pathA.count-1; i>=0; i--) {
        [path addObject:[pathA objectAtIndex:i]];
        for (NSInteger j = pathB.count - 1; j>=0; j--) {
            //找到公共父節(jié)點(diǎn),則將pathB中后面的節(jié)點(diǎn)壓入path
            if ([pathA objectAtIndex:i] == [pathB objectAtIndex:j]) {
                j++; //j++是為了避開(kāi)公共父節(jié)點(diǎn)
                while (j<pathB.count) {
                    [path addObject:[pathB objectAtIndex:j]];
                    j++;
                }
                
                return path;
            }
        }
    }
    return nil;
}

二叉樹(shù)兩個(gè)節(jié)點(diǎn)之間的距離

可以從兩個(gè)節(jié)點(diǎn)之間的路徑衍生出來(lái)。

/**
 *  二叉樹(shù)兩個(gè)節(jié)點(diǎn)之間的距離
 *
 *  @param nodeA    第一個(gè)節(jié)點(diǎn)
 *  @param nodeB    第二個(gè)節(jié)點(diǎn)
 *  @param rootNode 二叉樹(shù)根節(jié)點(diǎn)
 *
 *  @return 兩個(gè)節(jié)點(diǎn)間的距離(-1:表示沒(méi)有找到路徑)
 */
+ (NSInteger)distanceFromNode:(BinaryTreeNode *)nodeA toNode:(BinaryTreeNode *)nodeB inTree:(BinaryTreeNode *)rootNode {
    if (!rootNode || !nodeA || !nodeB) {
        return -1;
    }
    if (nodeA == nodeB) {
        return 0;
    }
    //從根節(jié)點(diǎn)到節(jié)點(diǎn)A的路徑
    NSArray *pathA = [self pathOfTreeNode:nodeA inTree:rootNode];
    //從根節(jié)點(diǎn)到節(jié)點(diǎn)B的路徑
    NSArray *pathB = [self pathOfTreeNode:nodeB inTree:rootNode];
    //其中一個(gè)節(jié)點(diǎn)不在樹(shù)中,則沒(méi)有路徑
    if (pathA.count == 0 || pathB == 0) {
        return -1;
    }
    //從后往前推,查找第一個(gè)出現(xiàn)的公共節(jié)點(diǎn)
    for (NSInteger i = pathA.count-1; i>=0; i--) {
        for (NSInteger j = pathB.count - 1; j>=0; j--) {
            //找到公共父節(jié)點(diǎn)
            if ([pathA objectAtIndex:i] == [pathB objectAtIndex:j]) {
                //距離=路徑節(jié)點(diǎn)數(shù)-1 (這里要-2,因?yàn)楣哺腹?jié)點(diǎn)重復(fù)了一次)
                return (pathA.count - i) + (pathB.count - j) - 2;
            }
        }
    }
    return -1;
}

翻轉(zhuǎn)二叉樹(shù)

你會(huì)翻轉(zhuǎn)二叉樹(shù)嗎?如果不會(huì),那對(duì)不起,我們不會(huì)錄用你!

翻轉(zhuǎn)二叉樹(shù),又叫求二叉樹(shù)的鏡像,就是把二叉樹(shù)的左右子樹(shù)對(duì)調(diào)(當(dāng)然是遞歸的)

/**
 *  翻轉(zhuǎn)二叉樹(shù)(又叫:二叉樹(shù)的鏡像)
 *
 *  @param rootNode 根節(jié)點(diǎn)
 *
 *  @return 翻轉(zhuǎn)后的樹(shù)根節(jié)點(diǎn)(其實(shí)就是原二叉樹(shù)的根節(jié)點(diǎn))
 */
+ (BinaryTreeNode *)invertBinaryTree:(BinaryTreeNode *)rootNode {
    if (!rootNode) {
        return nil;
    }
    if (!rootNode.leftNode && !rootNode.rightNode) {
        return rootNode;
    }
    
    [self invertBinaryTree:rootNode.leftNode];
    [self invertBinaryTree:rootNode.rightNode];
    
    BinaryTreeNode *tempNode = rootNode.leftNode;
    rootNode.leftNode = rootNode.rightNode;
    rootNode.rightNode = tempNode;
    
    return rootNode;
}

判斷二叉樹(shù)是否完全二叉樹(shù)

完全二叉樹(shù)定義為:若設(shè)二叉樹(shù)的高度為h,除第h層外,其它各層的結(jié)點(diǎn)數(shù)都達(dá)到最大個(gè)數(shù),第h層有葉子結(jié)點(diǎn),并且葉子結(jié)點(diǎn)都是從左到右依次排布。

完全二叉樹(shù)必須滿足2個(gè)條件:

1)如果某個(gè)節(jié)點(diǎn)的右子樹(shù)不為空,則它的左子樹(shù)必須不為空

2)如果某個(gè)節(jié)點(diǎn)的右子樹(shù)為空,則排在它后面的節(jié)點(diǎn)必須沒(méi)有孩子節(jié)點(diǎn)

這里還需要理解“排在它后面的節(jié)點(diǎn)”,回頭看看層次遍歷算法,我們就能知道在層次遍歷時(shí),是從上到下從左到右遍歷的,先將根節(jié)點(diǎn)彈出隊(duì)列,再壓入孩子節(jié)點(diǎn),因此“排在它后面的節(jié)點(diǎn)”有2種情況:

1)同層次的后面的節(jié)點(diǎn)

2)同層次的前面的節(jié)點(diǎn)的孩子節(jié)點(diǎn)(因?yàn)楸闅v前面的節(jié)點(diǎn)時(shí),會(huì)彈出節(jié)點(diǎn),同時(shí)將孩子節(jié)點(diǎn)壓入隊(duì)列)

通過(guò)上面的分析,我們可以設(shè)置一個(gè)標(biāo)志位flag,當(dāng)子樹(shù)滿足完全二叉樹(shù)時(shí),設(shè)置flag=YES。當(dāng)flag=YES而節(jié)點(diǎn)又破壞了完全二叉樹(shù)的條件,那么它就不是完全二叉樹(shù)。

/**
 *  是否完全二叉樹(shù)
 *  完全二叉樹(shù):若設(shè)二叉樹(shù)的高度為h,除第h層外,其它各層的結(jié)點(diǎn)數(shù)都達(dá)到最大個(gè)數(shù),第h層有葉子結(jié)點(diǎn),并且葉子結(jié)點(diǎn)都是從左到右依次排布
 *
 *  @param rootNode 根節(jié)點(diǎn)
 *
 *  @return YES:是完全二叉樹(shù),NO:不是完全二叉樹(shù)
 */
+ (BOOL)isCompleteBinaryTree:(BinaryTreeNode *)rootNode {
    if (!rootNode) {
        return NO;
    }
    //左子樹(shù)和右子樹(shù)都是空,則是完全二叉樹(shù)
    if (!rootNode.leftNode && !rootNode.rightNode) {
        return YES;
    }
    //左子樹(shù)是空,右子樹(shù)不是空,則不是完全二叉樹(shù)
    if (!rootNode.leftNode && rootNode.rightNode) {
        return NO;
    }
    
    //按層次遍歷節(jié)點(diǎn),找到滿足完全二叉樹(shù)的條件:
    //條件1:如果某個(gè)節(jié)點(diǎn)的右子樹(shù)不為空,則它的左子樹(shù)必須不為空
    //條件2:如果某個(gè)節(jié)點(diǎn)的右子樹(shù)為空,則排在它后面的節(jié)點(diǎn)必須沒(méi)有孩子節(jié)點(diǎn)
    //排在該節(jié)點(diǎn)后面的節(jié)點(diǎn)有2種:1)同層次的后面的節(jié)點(diǎn) 2)同層次的前面的節(jié)點(diǎn)的孩子節(jié)點(diǎn)(因?yàn)楸闅v前面的節(jié)點(diǎn)的時(shí)候,會(huì)將節(jié)點(diǎn)從隊(duì)列里pop,同時(shí)把它的孩子節(jié)點(diǎn)push到隊(duì)列里)
    NSMutableArray *queue = [NSMutableArray array];
    [queue addObject:rootNode];
    BOOL isComplete = NO; //是否已經(jīng)滿足完全二叉樹(shù)
    while (queue.count > 0) {
        BinaryTreeNode *node = [queue firstObject];
        [queue removeObjectAtIndex:0];
        
        //左子樹(shù)為空且右子樹(shù)不為空,則不是完全二叉樹(shù)
        if (!node.leftNode && node.rightNode) {
            return NO;
        }
        if (isComplete && (node.leftNode || node.rightNode)) {
            //前面的節(jié)點(diǎn)已滿足完全二叉樹(shù),如果還有孩子節(jié)點(diǎn),則不是完全二叉樹(shù)
            return NO;
        }
        
        //右子樹(shù)為空,則已經(jīng)滿足完全二叉樹(shù)
        if (!node.rightNode) {
            isComplete = YES;
        }
        
        //壓入
        if (node.leftNode) {
            [queue addObject:node.leftNode];
        }
        if (node.rightNode) {
            [queue addObject:node.rightNode];
        }
    }
    return isComplete;
}

判斷二叉樹(shù)是否滿二叉樹(shù)

滿二叉樹(shù)定義為:除了葉結(jié)點(diǎn)外每一個(gè)結(jié)點(diǎn)都有左右子葉且葉子結(jié)點(diǎn)都處在最底層的二叉樹(shù)

滿二叉樹(shù)的一個(gè)特性是:葉子數(shù)=2^(深度-1),因此我們可以根據(jù)這個(gè)特性來(lái)判斷二叉樹(shù)是否是滿二叉樹(shù)。

/**
 *  是否滿二叉樹(shù)
 *  滿二叉樹(shù):除了葉結(jié)點(diǎn)外每一個(gè)結(jié)點(diǎn)都有左右子葉且葉子結(jié)點(diǎn)都處在最底層的二叉樹(shù)
 *
 *  @param rootNode 根節(jié)點(diǎn)
 *
 *  @return YES:滿二叉樹(shù),NO:非滿二叉樹(shù)
 */
+ (BOOL)isFullBinaryTree:(BinaryTreeNode *)rootNode {
    if (!rootNode) {
        return NO;
    }
    
    //二叉樹(shù)深度
    NSInteger depth = [self depthOfTree:rootNode];
    //二叉樹(shù)葉子節(jié)點(diǎn)數(shù)
    NSInteger leafNum = [self numberOfLeafsInTree:rootNode];
    
    //滿二叉樹(shù)特性:葉子數(shù)=2^(深度-1)
    if (leafNum == pow(2, (depth - 1))) {
        return YES;
    }
    return NO;
}

判斷二叉樹(shù)是否平衡二叉樹(shù)

平衡二叉樹(shù)定義為:它是一棵空樹(shù)或它的左右兩個(gè)子樹(shù)的高度差的絕對(duì)值不超過(guò)1,并且左右兩個(gè)子樹(shù)都是一棵平衡二叉樹(shù)。平衡二叉樹(shù)又叫AVL樹(shù)。

/**
 *  是否平衡二叉樹(shù)
 *  平衡二叉樹(shù):即AVL樹(shù),它是一棵空樹(shù)或它的左右兩個(gè)子樹(shù)的高度差的絕對(duì)值不超過(guò)1,并且左右兩個(gè)子樹(shù)都是一棵平衡二叉樹(shù)
 *
 *  @param rootNode 根節(jié)點(diǎn)
 *
 *  @return YES:平衡二叉樹(shù),NO:非平衡二叉樹(shù)
 */
+ (BOOL)isAVLBinaryTree:(BinaryTreeNode *)rootNode {
    static NSInteger height;
    if (!rootNode) {
        height = 0;
        return YES;
    }
    if (!rootNode.leftNode && !rootNode.rightNode) {
        height = 1;
        return YES;
    }
    
    BOOL isAVLLeft = [self isAVLBinaryTree:rootNode.leftNode];
    NSInteger heightLeft = height;
    BOOL isAVLRight = [self isAVLBinaryTree:rootNode.rightNode];
    NSInteger heightRight = height;
    
    height = MAX(heightLeft, heightRight)+1;
    
    if (isAVLLeft && isAVLRight && ABS(heightLeft-heightRight) <= 1) {
        return YES;
    }
    return NO;
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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