zoukankan      html  css  js  c++  java
  • 用OC和Swift一起说说二叉树

    什么是二叉树 


          二叉树是每个节点最多有两个子树的树结构。通常子树被称作“左子树”(left subtree)和“右子树”(right subtree)。二叉树常被用于实现二叉查找树和二叉堆。二叉树不是树的一种特殊情形,尽管其与树有许多相似之处,但树和二叉树有两个主要差别:

          1、树中结点的最大度数没有限制,而二叉树结点的最大度数为2。

          2、树的结点无左、右之分,而二叉树的结点有左、右之分。

     
    二叉树的遍历

     

          二叉树的遍历先总结下面几种方式:

          1、前序遍历

          2、中序遍历

          3、后序遍历

          4、层次遍历

          下面这张图很好的解释了前三种遍历的正确顺序,其实在理解 前、中、后序遍历的时候我们只需要记住前中后说的是根节点的顺序,不是子节点的顺序,比如下面的 A、B、C三个节点中,前中后我们说的是 A 是前(先),中间、还是最后遍历。理解了这一点我们就比较容易理解正确的前中后序遍历的顺序.

     

    二叉树的创建


    /**
     创建二叉树
     @param Values 传入数组
     @return return value description
     */
    +(ZXTThreeObject * )CreatTreesWithValues:(NSArray * )Values{
        
         __block ZXTThreeObject * rootNode = nil;
        /** 这里顺便提一下这个循环遍历,可能不部分都用的是for循环或者for..in..来循环的 **/
        /** 大家了解一下 NSEnumerator 遍历和Block遍历还有GCD中的dispatch_apply  **/ 
        [Values enumerateObjectsUsingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
            
            int value = [obj intValue];
            rootNode =  [self AddTreeNode:rootNode andValue:value];
        }];
        return rootNode;
    }
    
    /**
     递归的思想
     这里简单说一下,局部变量是存储在栈中的,全局变量是存储在静态存储区的!每存储一个局部变量,编译器就会开辟一块栈区域来保存
     方法第一次传递的node这个变量,编译器就开辟了栈区域保存了它的值,后面要是有嵌套调用了这个方法
     编译器就又开辟新的栈区域保存它们的返回值,但不会影响第一次保存的值,你要调用多次的话,第一个保存的值也就是最后一个返回的**/
    +(ZXTThreeObject * )AddTreeNode:(ZXTThreeObject *)node andValue:(int) value{
    
        if (!node) {
            node = [[ZXTThreeObject alloc]init];
            node.Value = value;
        }else if (value <= node.Value){
           /**值小于节点的值,插入到左节点**/
           node.leftTreeNode = [self AddTreeNode:node.leftTreeNode  andValue:value];
        }else{
           /**值大于节点的值,插入到右节点**/
           node.rightTreeNode = [self AddTreeNode:node.rightTreeNode andValue:value];
        }
        return node;
    } 
    

    正确的结果展示:

    NSArray * array = @[@2,@3,@7,@5,@9,@4,@6,@1,@8];
    /**     
         上面的数组创建的二叉树应该是这样的 * 代表没有值
                                
                         2
         
                   1          3
         
                          *       7
         
                              5         9
         
                           4      6  8       *
     **/   
    [ZXTThreeObject CreatTreesWithValues:array]; 

    二叉树Swift创建代码:

    class ZXTreeObject: NSObject {
    
        var leftNode :ZXTreeObject?
        var rightNode:ZXTreeObject?
        var nodevalue:Int?
        
        func CreatTreesWithValues(Values:[Int]) -> ZXTreeObject {
            
            var rootNode:ZXTreeObject?
            for value in Values {
                
               rootNode = self.AddTreeNode(node: &rootNode,value)
            }
            return rootNode!
        }
        
        /**注意在Swift3中:函数签名中的下划线的意思是
           告诉编译器,我们在调用函数时第一个参数不需要外带标签
           这样,我们可以按照 Swift 2 中的方式去调用函数,还有这个下划线和参数名之间要有空格!必须要有!
         **/
        func AddTreeNode(node: inout ZXTreeObject?, _ value:Int) -> ZXTreeObject {
            
            if (node == nil) {
                
                node = ZXTreeObject()
                node?.nodevalue = value
            }else if (value < (node?.nodevalue)!) {
                
                //print("----- to left ")
                node.leftNode = AddTreeNode(node: &node!.leftNode, value)
            }else{
                
                //print("----- to right ")
                node.rightNode = AddTreeNode(node: &node!.rightNode, value)
            }
            // print("节点:",node!.nodevalue ?? 0)
            return node!
        }
    }
    

    调用 

    // 定义一个数组
    let sortArray:[Int] = [2,3,7,5,9,4,6,1,8]        
     _ = ZXTreeObject().CreatTreesWithValues(Values: sortArray)
    

      

    这个没有使用到返回值的警告有下面两种消除的方式:

    /*
    一:就像上面的加 _ = 在调用的函数前面
    二:在函数声明的前面加上 @discardableResult 如:
    @discardableResult func AddTreeNode(node: inout ZXTreeObject?, _ value:Int) -> ZXTreeObject {
    }*/
    

    计算二叉树的深度


    二叉树的深度 OC

    /**
     树的深度
     三种情况: 
     一:根节点要是不存在就返回0
     二:左节点和右节点都不存在,到这里的前提是根节点存在,返回1
     三:都存在,再递归获取深度,返回最大值!
     @param node 节点
     @return return value description
     */
    // 2017-03-03 14:06:15.248 算法OC版本[22755:262170] 数的深度==5
    +(NSInteger)deepWithThree:(ZXTThreeObject *)node{
    
        if (!node) {
            
            return 0;     
        }else if (!node.leftTreeNode && !node.rightTreeNode){
            
            return 1;
        }else{
        
            NSInteger leftDeep = [self deepWithThree:node.leftTreeNode];
            NSInteger rightDeep = [self deepWithThree:node.rightTreeNode];
            return MAX(leftDeep, rightDeep) + 1;
        }
    }
    

    二叉树的深度 Swift

    // Swift 求二叉树的深度
    func deepWithThree(rootNode: ZXTreeObject?) -> Int {
            
          if ((rootNode) == nil){
               
               return 0   
          }else if((rootNode?.leftNode) == nil && (rootNode?.rightNode) == nil){
            
              return 1
          }else{
               
              let deepLeft = self.deepWithThree(rootNode: rootNode?.leftNode)
              let rightLeft = self.deepWithThree(rootNode: rootNode?.rightNode)
              return  max(deepLeft, rightLeft) + 1
          }
    }
    // 调用
    // 打印 ====== 5
    let deep  =  node.deepWithThree(rootNode: node)
    print("======",deep)
    

    二叉树的宽度


    二叉树的宽度OC

    /*
       二叉树宽度的定义:各层节点数的最大值!
    */
    +(NSInteger)WidthOfTree:(ZXTThreeObject * )RootNode{
        
        // 根节点不存在,就返回 0
        if (!RootNode) {
            return 0;
        }
        NSMutableArray * queeArray = [NSMutableArray array];
        NSInteger currentWidth = 0;
        NSInteger maxWidth = 1;         // 前面 0 的不存在就 肯定有,至少是 1
        [queeArray addObject:RootNode]; // 添加根节点
        while (queeArray.count > 0) {
            
            // 这就是当前的节点的数目,第一层就只有根节点 宽度是1
            // 下一层,按照下面逻辑添加了左右节点就变成了2
            currentWidth = queeArray.count;
            // 遍历该层,删除原始数组中遍历过的节点,添加它下一层的节点。再循环遍历
            for (NSInteger i=0; i<currentWidth; i++) {
                
                ZXTThreeObject * treenode = [queeArray firstObject];
                [queeArray removeObjectAtIndex:0];
                if (treenode.leftTreeNode) {
                    [queeArray addObject:treenode.leftTreeNode];
                }
                if (treenode.rightTreeNode) {
                    [queeArray addObject:treenode.rightTreeNode];
                }
            }
            // 一层一层的遍历的时候去最大值,返回
            maxWidth = MAX(maxWidth, queeArray.count);
        }
        return maxWidth;
    }
    

    二叉树的宽度Swift

    func widthWithThree(rootNode: ZXTreeObject?) -> Int {
        
        if ((rootNode) == nil){
            return 0        
        }else{
               
            var widthDataArray =  Array<ZXTreeObject>()
            widthDataArray.append(rootNode!)
            var maxWidth = 1
            var currentWidth = 0;
    
            while (widthDataArray.count > 0) {
                    
                  currentWidth = widthDataArray.count
                  for _ in 0 ..< currentWidth{
                        
                      let node = widthDataArray.first                    
                      widthDataArray.remove(at: 0)
                      if ((node?.leftNode) != nil) {
                            
                          widthDataArray.append((node?.leftNode)!)
                      }      
                      if ((node?.rightNode) != nil){
                            
                          widthDataArray.append((node?.rightNode)!)
                      }
                  }
                  maxWidth = max(currentWidth, widthDataArray.count)
            }
        return maxWidth            
        }
    }
    

    二叉树的先、中、后序遍历


    OC方法遍历

    // 调用代码
    NSMutableArray *  dataArray =  [NSMutableArray array];
        [ZXTThreeObject preorderTraversal:rootNode andHandle:^(ZXTThreeObject *threeNode) {
            
            NSString * value = [NSString stringWithFormat:@"%ld",threeNode.Value];
            [dataArray addObject:value];
        }];
    NSLog(@"=======%@",dataArray);
    
    // 方法实现代码
    /**
       这里所谓的先序,中序,或者后序说的都是根节点的顺序,这个可以看着上面的那张度娘的图片去好好体会一下。
       handle就是一个回调,node就是根节点,这样就很容易理解记住了。其他的后序和中序就不写代码了,交换顺序就行了。
     **/
    +(void)preorderTraversal:(ZXTThreeObject *)node andHandle:(void(^)(ZXTThreeObject * threeNode))handle{
    
        if (node) {
            // 根
            if (handle) {
                
                handle(node);
            }
            //左
            [self preorderTraversal:node.leftTreeNode andHandle:handle];
            //右
            [self preorderTraversal:node.rightTreeNode andHandle:handle];
        }
    }
    

    Swift方法遍历

    // 定义一个随机数组
    let sortArray:[Int] = [2,3,7,5,9,4,6,1,8] 
    var dataArray = Array<Int>()
    let node = ZXTreeObject().CreatTreesWithValues(Values: sortArray)
    _ = node.preorderTraversal(rootNode:node, handle: {(treeNode) in
            
         dataArray.append((treeNode?.nodevalue)!)
    })
    print(dataArray)
    
    /**  先序遍历  中序和后序就不写了,而上面OC的道理是一样的 **/
    // 这是定义的闭包,注意它的位置和我们OC定义Block类似
    typealias Handle = (_ root:ZXTreeObject?) -> Void;
    func preorderTraversal(rootNode: ZXTreeObject?, handle:@escaping Handle) -> Void {
            
        if ((rootNode) != nil) {
       
            handle(rootNode);            
            self.preorderTraversal(rootNode: rootNode?.leftNode, handle: handle)
            self.preorderTraversal(rootNode: rootNode?.rightNode, handle: handle)
         }
    }
    

    二叉树的层次遍历  (这个遍历方式可以参考我们获取树宽度时候的思路)


    层次遍历OC

    /*
     层次遍历
     层次遍历,就是一层一层的遍历,下面的方法用到了队列的想法。
     */
    +(void)CengCiBLTreeNode:(ZXTThreeObject * ) RootNode handle:(void(^)(ZXTThreeObject * treenode))handle{
        
        if (RootNode) {
            
            NSMutableArray * queeArray=[NSMutableArray array];
            // 添加了根节点进去
            [queeArray addObject:RootNode];
            
            while (queeArray.count>0) {
                
                // 取出数组中的第一个元素
                ZXTThreeObject * rootnode = [queeArray firstObject];
                if (handle) {
                    // 添加这个元素的值到数组中去
                    handle(rootnode);
                }
                // 添加完这个元素的值就把这个元素删除了
                [queeArray removeObjectAtIndex:0];
                // 这个节点要有左节点的话,就添加左节点到数组中去,有右节点的话就添加右节点到数组中去。
                // 建议一步一步研究这个添加顺序,就可以清晰的看到这个是一层一层的遍历到的。
                if (rootnode.leftTreeNode) {
                    [queeArray addObject:rootnode.leftTreeNode];
                }
                if (rootnode.rightTreeNode) {
                    [queeArray addObject:rootnode.rightTreeNode];
                }
            }
        }
    }
    

    层次遍历Swift

    /******* 调用/
    var array = Array<Int>()
    _ = node.preorderTraversal(rootNode:node, handle: {(treeNode) in
                
        array.append((treeNode?.nodevalue)!)
    })
    //array ====== [2, 1, 3, 7, 5, 4, 6, 9, 8]
    print("array ======",array)
    
    /*******  层次遍历/
    func CengCiBLTreeNode(rootNode: ZXTreeObject?, handle:@escaping Handle) -> Void {
            
        if ((rootNode) != nil) {
                
            var dataArray =  Array<ZXTreeObject>()
            dataArray.append(rootNode!)
        
            while dataArray.count > 0 {
                handle(rootNode);
                dataArray.remove(at: 0)
                if ((rootNode?.leftNode) != nil) {
    
                    dataArray.append((rootNode?.leftNode)!)
                }
                if ((rootNode?.rightNode) != nil) {
                    dataArray.append((rootNode?.rightNode)!)
                }
            }
        }
    }
    
  • 相关阅读:
    [ 转载 ] Mysql 远程连接+开放80和3306端口 常用配置
    [ 转载 ] Mysql 数据库常用命令
    [ 转载 ] Centos 安装mysql后启动失败 出现 ERROR 2002 (HY000): Can’t connect to local MySQL server through socket ‘/var/lib/mysql/mysql.sock’
    [2020多校联考]手套
    [2020多校联考]糖果机器
    [2020多校联考]染色相邻的边
    [2020多校联考]四个质数的和
    [2020多校联考]简单题
    [2020多校联考]MC
    [2020多校联考]进化
  • 原文地址:https://www.cnblogs.com/zhangxiaoxu/p/6484532.html
Copyright © 2011-2022 走看看