笔记-数据结构之二叉树(iOS)

预备知识

定义

树是一种数据结构,它是由N(N >= 1)个有限结点组成一个具有层次关系的集合。
特点:

  • 每个结点有0个或者多个子结点
  • 没有父结点的结点称为根结点
  • 每一个非根结点有且只有一个父结点
  • 除了根结点外,每个子结点可以分为多个不相交的子树

基本术语

结点n的高度: n结点到叶子结点所有路径上包含结点个数的最大值。叶子结点的高度为1,往上依次递增。
结点的深度: 从根结点到结点n唯一路径的长,根结点的深度为1(有的书上此基数为0,两种记法都没有错)
层数: 根结点为第一层,往下依次递增。
结点的度: 结点拥有的子树个数,度为0的结点称为叶子结点。
叶子: 度为零的结点。
树的度: 树中结点的最大的度。
树的高度: 树中结点的最大层次(在基数为1时,树的深度 = 树的高度 = 最大层数)。

二叉查找树

二叉查找树:

  • 若左子树不空,则左子树上所有结点的值均小于它根结点的值。
  • 若右子树不空,则右子树上所有结点的值均大于它根结点的值。
  • 左、右子树也分别为二叉查找树。
  • 没有键值相等的结点

OC语言的实现

如果想要更加透彻的理解二叉查找树相关的知识,就自己尝试的去敲一下下面的代码

定义

二叉树的实现是定义了一个工具类

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface BinaryTreeNode : NSObject
 
@property (nonatomic, assign) NSInteger keyValue;           // 值
@property (nonatomic, strong) BinaryTreeNode *leftNode;     // 左结点
@property (nonatomic, strong) BinaryTreeNode *rightNode;    // 右结点

@end

NS_ASSUME_NONNULL_END

创建二叉查找树

/**
创建二叉排序树

@param values 数组
@return 二叉树结点
*/
+ (BinaryTreeNode *)createBinaryTreeNodeWithValues:(NSArray *)values {
    BinaryTreeNode *rootNode = nil;
    for (NSInteger i = 0; i < values.count; i ++) {
        NSInteger value = [[values objectAtIndex:i] integerValue];
        rootNode = [BinaryTreeNode addBinaryTreeNode:rootNode andValue:value];
    }
    return rootNode;
}

/**
向树结点添加结点

@param treeNode 根结点
@param value 结点值
@return 根结点
*/
+ (BinaryTreeNode *)addBinaryTreeNode:(BinaryTreeNode *)treeNode andValue:(NSInteger)value {
    if (!treeNode) {
        // 结点不存在,创建新结点
        treeNode = [[BinaryTreeNode alloc]init];
        treeNode.keyValue = value;
    }else if (value < treeNode.keyValue) {
        // 生成左子树
        treeNode.leftNode = [BinaryTreeNode addBinaryTreeNode:treeNode.leftNode andValue:value];
    }else {
        // 生成右子树
        treeNode.rightNode = [BinaryTreeNode addBinaryTreeNode:treeNode.rightNode andValue:value];
    }
    return treeNode;
}

查找二叉树中某个位置的结点

/**
 查找二叉树某个位置的结点
 
 @param index 按层次便利树是的位置(从0开始)
 @param rootNode 树根结点
 @return 结点
 */
 + (BinaryTreeNode *)findTreeNodeAtIndex:(NSInteger)index withTree:(BinaryTreeNode *)rootNode {
    // 结点不存在,查找的位置不符合规范
    if (!rootNode || index < 0) {
        return nil;
    }
    
    NSMutableArray *queueArray = [NSMutableArray arrayWithCapacity:0];
    // 压入根结点
    [queueArray addObject:rootNode];
    while (queueArray.count > 0) {
    BinaryTreeNode *node = [queueArray firstObject];
    if (index == 0) {
         return node;
    }
    [queueArray removeObjectAtIndex:0];     // 仿照队列FIFO,t移除最前面的结点
    index--;
    
    // 按照从左到右依次压入结点
    if (node.leftNode) {
        [queueArray addObject:node.leftNode];
    }
    if (node.rightNode) {
        [queueArray addObject:node.rightNode];
    }
    }
    
    //  遍历完,还没有找到位置,返回nil
    return nil;
}

寻找最小结点

/**
寻找最小结点

@param treeNode 树
@return 树中最小的结点
*/
+ (BinaryTreeNode *)findMinTreeNode:(BinaryTreeNode *)treeNode {
   if (!treeNode) {
       return nil;
   }
   if (!treeNode.leftNode) {
       return treeNode;
   }else {
       return [BinaryTreeNode findMinTreeNode:treeNode.leftNode];
   }
}

先序遍历

先访问根,再遍历左子树,最后遍历右子树(递归)

/**
先序遍历
先访问根,再遍历左子树,最后遍历右子树(递归)

@param rootNode 根结点
@param handler 访问结点处理函数
*/
+ (void)preOrderTraverseBinaryTree:(BinaryTreeNode *)rootNode handle:(void(^)(BinaryTreeNode *treeNode))handler {
 if (rootNode) {
   if (handler) {
       handler(rootNode);
   }
  
  [self preOrderTraverseBinaryTree:rootNode.leftNode handle:handler];
  [self preOrderTraverseBinaryTree:rootNode.rightNode handle:handler];
 }
}

中序遍历

先遍历左子树,再访问根,最后遍历右子树(递归)

/**
中序遍历
先遍历左子树,再访问根,最后遍历右子树(递归)

@param rootNode 根节点
@param handler 访问节点处理函数
*/
+ (void)inOrderTraverseBinaryTree:(BinaryTreeNode *)rootNode handle:(void(^)(BinaryTreeNode *treeNode))handler {
   if (rootNode) {
       [self inOrderTraverseBinaryTree:rootNode.leftNode handle:handler];
       
       if (handler) {
           handler(rootNode);
       }
       
       [self inOrderTraverseBinaryTree:rootNode.rightNode handle:handler];
   }
}

后序遍历

先遍历左子树,再遍历右子树,最后访问根(递归)

/**
 后序遍历
 先遍历左子树,再遍历右子树,最后访问根(递归)

 @param rootNode 根节点
 @param handler 访问节点处理函数
 */
+ (void)postOrderTraverseBinaryTree:(BinaryTreeNode *)rootNode handle:(void(^)(BinaryTreeNode *treeNode))handler {
    if (rootNode) {
        [self postOrderTraverseBinaryTree:rootNode.leftNode handle:handler];
        [self postOrderTraverseBinaryTree:rootNode.rightNode handle:handler];
        
        if (handler) {
            handler(rootNode);
        }
    }
}

层次遍历

/**
 层次遍历(广度优先)

 @param rootNode 树根节点
 @param handler 访问节点处理函数
 */
+ (void)levelTraverseBinaryTree:(BinaryTreeNode *)rootNode handle:(void(^)(BinaryTreeNode *treeNode))handler {
    if (!rootNode) {
        return;
    }
    
    NSMutableArray *queueArray = [NSMutableArray arrayWithCapacity:0];      // 数组当作队列
    [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)depthOfBinaryTree:(BinaryTreeNode *)rootNode {
    if (!rootNode) {
        return 0;
    }
    if (!rootNode.leftNode && !rootNode.rightNode) {
        return 1;
    }
    
    // 左子树的深度
    NSInteger leftDepth = [self depthOfBinaryTree:rootNode.leftNode];
    // 右子树的深度
    NSInteger rightDepth = [self depthOfBinaryTree:rootNode.rightNode];
    
    return MAX(leftDepth, rightDepth) + 1;
}

二叉树的宽度

/**
 二叉树的宽度

 @param rootNode 根节点
 @return 二叉树的宽度
 */
+ (NSInteger)widthOfBinaryTree:(BinaryTreeNode *)rootNode {
    if (!rootNode) {
        return 0;
    }
    
    NSMutableArray *queueArray = [NSMutableArray arrayWithCapacity:0];
    [queueArray addObject:rootNode];
    NSInteger maxWidth = 1;     // 初始化最大宽度 1
    NSInteger curWidth = 0;     // 当前层数的宽度
    
    while (queueArray.count > 0) {
        curWidth = queueArray.count;
        
        for (int 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)numberOfBinaryTree:(BinaryTreeNode *)rootNode {
    if (!rootNode) {
        return 0;
    }
    
    NSInteger leftNumber = [self numberOfBinaryTree:rootNode.leftNode];
    NSInteger rightNumber = [self numberOfBinaryTree:rootNode.rightNode];
    
    // 节点数 = 左子树节点数 + 右子树节点数 + 1(根节点)
    return leftNumber + rightNumber + 1;
}

二叉树某层的结点数

/**
 二叉树中某层中的节点数

 @param level 层数
 @param rootNode 根节点
 @return 层中的节点数
 */
+ (NSInteger)numberOfBinaryTreeNodesOnLevel:(NSInteger)level inBinaryTree:(BinaryTreeNode *)rootNode {
    if (!rootNode || level < 1) {
        return 0;
    }
    if (level == 1) {   // 根节点
        return 1;
    }
    
    // 递归:level层节点数 = 左子树level-1层节点数 + 右子树level-1层节点数
    NSInteger leftNodes = [self numberOfBinaryTreeNodesOnLevel:level - 1 inBinaryTree:rootNode.leftNode];
    NSInteger rightNodes = [self numberOfBinaryTreeNodesOnLevel:level - 1 inBinaryTree:rootNode.rightNode];
    
    return leftNodes + rightNodes;
}

翻转二叉树

/**
 翻转二叉树(镜像)

 @param rootNode 根节点
 @return 翻转后的树的根节点(就是原二叉树的根节点)
 */
+ (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;
}

翻转二叉树(非递归)

/**
 翻转二叉树(非递归)

 @param rootNode 根结点
 @return 翻转后的二叉树
 */
+ (BinaryTreeNode *)invertBinaryTreeNotRecursive:(BinaryTreeNode *)rootNode {
    if (!rootNode) {
        return nil;
    }
    if (!rootNode.leftNode && !rootNode.rightNode) {
        return rootNode;
    }
    
    NSMutableArray *queueArray = [NSMutableArray arrayWithCapacity:0];
    [queueArray addObject:rootNode];
    
    while (queueArray.count > 0) {
        BinaryTreeNode *node = [queueArray firstObject];
        [queueArray removeObjectAtIndex:0];
        
        BinaryTreeNode *tempNode = node.leftNode;
        node.leftNode = node.rightNode;
        node.rightNode = tempNode;
        
        if (node.leftNode) {
            [queueArray addObject:node.leftNode];
        }
        if (node.rightNode) {
            [queueArray addObject:node.rightNode];
        }
    }
    return rootNode;
}

判断是否是完全二叉树

/**
 判断是否是完全二叉树
 完全二叉树:若设二叉树的高度为h,除第h层外,其它各层的节点数都达到最大个数,第h层有叶子节点,并且叶子节点都是从左到右依次排布

 @param rootNode 根节点
 @return 是否是完全二叉树
 */
+ (BOOL)isCompleteBinaryTree:(BinaryTreeNode *)rootNode {
    if (!rootNode) {
        return NO;
    }
    if (!rootNode.leftNode && !rootNode.rightNode) {
        return YES;
    }
    if (!rootNode.leftNode && rootNode.rightNode) {
        return NO;
    }
    /**
     按层次遍历节点,找到满足完全二叉树的条件:
     条件1:如果某个节点的右子树不为空,则它的左子树必须不为空
     条件2:如果某个节点的右子树为空,则排在它后面的节点必须没有孩子节点
     排在该节点后面的节点有2种:1)同层次的后面的节点 2)同层次的前面的节点的孩子节点
     */
    NSMutableArray *queueArray = [NSMutableArray arrayWithCapacity:0];
    [queueArray addObject:rootNode];
    BOOL isComplete = NO;   // 是否是完全二叉树
    
    while (queueArray.count > 0) {
        BinaryTreeNode *node = [queueArray firstObject];
        [queueArray removeObjectAtIndex:0];
        
        // 左子树为空且右子树不为空,则不是完全二叉树
        if (!node.leftNode && node.rightNode) {
            return NO;
        }
        
        // 前面的节点已满足完全二叉树,如果还有孩子节点,则不是完全二叉树
        if (isComplete && (node.leftNode || node.rightNode)) {
            return NO;
        }
        
        // 右子树为空,则已满足完全二叉树
        if (!node.rightNode) {
            isComplete = YES;
        }
        
        if (node.leftNode) {
            [queueArray addObject:node.leftNode];
        }
        if (node.rightNode) {
            [queueArray addObject:node.rightNode];
        }
    }
    return isComplete;
}

判断是否是满二叉树

/**
 是否是满二叉树
 满二叉树:除了叶节点外每一个节点都有左右子叶且叶节点都处在最底层的二叉树

 @param rootNode 根节点
 @return 是否是满二叉树
 */
+ (BOOL)isFullBinaryTree:(BinaryTreeNode *)rootNode {
    if (!rootNode) {
        return NO;
    }
    
    // 深度
    NSInteger depth = [self depthOfBinaryTree:rootNode];
    // 二叉树叶子节点数
    NSInteger leafNum = [self numberOfBinaryTree:rootNode];
    
    // 是否满足:叶子树 = 2^(深度 - 1)
    if (leafNum == pow(2, depth - 1)) {
        return YES;
    }
    return NO;
}

判断是否是平衡树(AVL树)

/**
 是否是AVL树(平衡二叉树)
 AVL树:是一颗空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一颗平衡二叉树
 @param rootNode 根节点
 @return 是否是AVL树
 */
+ (BOOL)isAVLBinaryTree:(BinaryTreeNode *)rootNode {
    //static修饰局部变量时,在程序中只有一份内存,并延长其生命周期
    static NSInteger height;
    if (!rootNode) {
        height = 0;
        return YES;
    }
    if (!rootNode.leftNode && !rootNode.rightNode) {
        height = 1;
        return YES;
    }
    
    // 左子树是否是AVL树
    BOOL isAVLLeftTree = [self isAVLBinaryTree:rootNode.leftNode];
    NSInteger heightLeft = height;
    // 右子树是否是AVL树
    BOOL isAVLRightTree = [self isAVLBinaryTree:rootNode.rightNode];
    NSInteger heightRight = height;
    
    height = MAX(heightLeft, heightRight) + 1;
    
    // 左右两颗子树的高度差的绝对值不超过1且两个子树都是AVL树
    if (isAVLLeftTree && isAVLRightTree && ABS(heightLeft - heightRight) <= 1) {
        return YES;
    }
    return NO;
}

更多相关资料:
二叉查找树之图文解析和C语言的实现
AVL树之图文解析和C语言的实现

参考网上资料:
二叉树-你必须要懂!
二叉树的各种问题

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 220,063评论 6 510
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,805评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 166,403评论 0 357
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,110评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,130评论 6 395
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,877评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,533评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,429评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,947评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,078评论 3 340
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,204评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,894评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,546评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,086评论 0 23
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,195评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,519评论 3 375
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,198评论 2 357

推荐阅读更多精彩内容