用OC和Swift一起说说二叉树

时间:2022-08-26 17:57:12

前言:

   一:在计算机科学中,二叉树是每个节点最多有两个子树的树结构。通常子树被称作“左子树”(left subtree)和“右子树”(right subtree)。二叉树常被用于实现二叉查找树和二叉堆。
 
   二:二叉树的每个结点至多只有二棵子树(不存在度大于2的结点),二叉树的子树有左右之分,次序不能颠倒。二叉树的第i层至多有2^{i-1}个结点;深度为k的二叉树至多有2^k-1个结点;对任何一棵二叉树T,如果其终端结点数为n_0,度为2的结点数为n_2,则n_0=n_2+1。
 
   三:一棵深度为k,且有2^k-1个节点称之为满二叉树;深度为k,有n个节点的二叉树,当且仅当其每一个节点都与深度为k的满二叉树中,序号为1至n的节点对应时,称之为完全二叉树。
 
   四:二叉树遍历: 先序遍历、中序遍历、后序遍历、层次遍历 、下面答案很精辟;
 

用OC和Swift一起说说二叉树

二✘树的OC创建源码:

/**
创建二叉树
@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 这个参数其实是一个局部变量,不管里面嵌套调用多少次AddTreeNode这个方法,这里每一次返回的一直都是它的最原始的根节点!!这点对创建过程的 理解很重要,但如果返回值你写成全局变量就不一样了,它返回的就是最后赋给它的值。
这里简单说一下,局部变量是存储在栈中的,全局变量是存储在静态存储区的!每存储一个局部变量,编译器就会开辟一块栈区域来保存
方法第一次传递的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 ")
_ = AddTreeNode(node: &node!.leftNode, value)
}else{ //print("----- to right ")
_ = 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)

这个结果的话大家可以把上面的打印注释打开自己看看结果,是没问题的,这里在给大家看看这样一个警告:

用OC和Swift一起说说二叉树

就这个返回值没有使用的警告,这警告有两种办法消除:

/*
一:就像上面的加 _ = 在调用的函数前面
二:在函数声明的前面加上 @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)!)
}
}
}
}

用OC和Swift一起说说二叉树的更多相关文章

  1. iOS代码规范(OC和Swift)

    下面说下iOS的代码规范问题,如果大家觉得还不错,可以直接用到项目中,有不同意见 可以在下面讨论下. 相信很多人工作中最烦的就是代码不规范,命名不规范,曾经见过一个VC里有3个按钮被命名为button ...

  2. IOS --- OC与Swift混编

    swift 语言出来后,可能新的项目直接使用swift来开发,但可能在过程中会遇到一些情况,某些已用OC写好的类或封装好的模块,不想再在swift 中再写一次,哪就使用混编.这个在IOS8中是允许的. ...

  3. iOS OC和Swift进行互相调用

    有时候 ,我们会涉及到双向混合编程,特别是OC和swift的互相引用. swift调用oc的方法: 1.桥接文件,一般是swift工程,在创建一个oc文件时,系统自动添加(不用改名,直接默认即可) 2 ...

  4. 关于C、OC、C&plus;&plus;、OC&plus;&plus;、Swift的一些常识

    关于C.OC.C++.OC++.Swift的一些常识 OC是C语言的一个超集,是一门面向对象的语言,因为苹果的崛起而火,API主要是cocoa(OSX)和cocoatouch(iOS),GCC 和 C ...

  5. Swift语言学习之OC和Swift混编

    本文转自http://www.helloswift.com.cn/swiftbase/2015/0112/3469.html iOS OC和Swift混编 1.创建一个swift或者oc的工程:我这里 ...

  6. 【转】IOS --- OC与Swift混编

    群里大神发的网址,感觉有用就先收录了,暂时没时间看SWIFT,感觉代码简洁,但是可阅读性不是太高,有些代码让系统去判断类型,同样的,我们看代码的时候也得自己去判断类型,或许看多就习惯了,有时间再说吧, ...

  7. OC 和 swift 小结

    1 什么是 OC 语言? OC 语言即面向对象语言,它扩展了 ANSI C 语言,将 SmallTalk 式的消息传递机制加入到 ANSI C 中.它是苹果 OS 和 iOS 以及相关的 API,Co ...

  8. OC与Swift的区别四&lpar;条件语句&rpar;

    12.条件语句的区别,此处只写区别,没有指出区别的其他方面oc与swift基本一致 12.1 oc中for if switch语句体如果只有一行代码,则{}可以省略 swift中for if swit ...

  9. OC调用Swift 整理步骤!总结别人的!方便自己查找!

    1. 2. 上面的修改了一个配置项,有一个Product Module Name在后面会使用. 在工程里面点击File/New/File…,选择iOS/Source/Cocoa Touch Class ...

随机推荐

  1. python 入门(一)矩阵处理

    numpy 使用 1.使用 array 定义矩阵 dataSet = array([[1.0,1.1],[1.0,1.0],[0.0,0.0],[0,0.1]]) 2.使用 shape 返回矩阵的行数 ...

  2. js的url解析函数封装

    在实际开发中,有些通过get方式与后台交换数据的时候,需要用到的数据在url中,因此就需要我们来获取到url中有用的信息,下面封装的函数已经可以将url解析的很彻底了,可以拿来直接用的: functi ...

  3. &lbrack;diango&rsqb;理解django视图工作原理

    前言:正确理解django视图view,模型model,模板的概念及其之间的关联关系,才能快速学习并上手使用django制作网页 本文主要讲解自己在学习django后对视图view的理解 在进入正文之 ...

  4. CStringArray用法

    CStringArray使用之前先设置数组尺寸SetSize,才能使用SetAt                  CStringArray m_strScrkRfid ;               ...

  5. Google Glass应用开发探索

    摘要:2012年6月的Google开发者大会上,作者有幸预定到了Google Glass.8个月后,她收邀参加了Google纽约总部举行的Google Glass Foundry开发大赛.在为期两天的 ...

  6. centos VM 识别U盘

    在VM设置选项中,选择 USB Controller 选项,设置相关参数即可

  7. Codeforces Round &num;524 &lpar;Div&period; 2&rpar; F

    题解: 首先这个东西因为强制在线区间查询 所以外面得套线段树了 然后考虑几条线段怎么判定 我们只需要按照右端点排序,然后查询的时候查找最右节点的前缀最大值就可以了 然后怎么合并子区间信息呢 (刚开始我 ...

  8. 我的mac下有关php扩展的安装

    之前安装yaf和mcrypt扩展一直失败,今天终于找到原因了.那是因为./configure的时候没有指定php版本,所以用了默认的php的版本,正确的姿势应该是:./configure --with ...

  9. Concept Drift&lpar;概念漂移&rpar;

    Introdution concept drift在机器学习.时间序列以及模式识别领域的一种现象.如果是在机器学习领域中,这个概念指的就是一个模型要去预测的一个目标变量,概念漂移就是这个目标变量随着时 ...

  10. 使用RestTemplate测试视频上传的Post请求

    以往多用RestTemplate处理接口的调用以及与Ribbon/Feign配合使用调用微服务接口,近日写了一个处理Post文件上传的解决方案,其实就是将后台所需的MultipartFile,在请求P ...