给出一棵二叉树,寻找一条路径使其路径和最大,路径可以在任一节点中开始和结束(路径和为两个节点之间所在路径上的节点权值之和)

背包问题常见的有两种,01背包问题和完全背包问题,实现起来比较简单,论证过程需要一定的数学推理知识,本文给出基本实现过程,关于推理可以看一下网上的文章.

参考

自动释放池的前世今生 —- 深入解析 Autoreleasepool

你真的懂iOS的autorelease吗?

@autoreleasepool-内存的分配与释放

样例给出一棵二叉树:

一个背包总容量为V,现在有N个物品,第i个
物品体积为weight[i],价值为value[i],现在往背包里面装东西,怎么装能使背包的内物品价值最大?每件物品只能选择取或不取.

Autorelease Pool是什么

int main(int argc, char * argv[]) { @autoreleasepool { return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); }}

新建一个工程,总会见到这样的几行代码,这行代码将所有的事件、消息全部交给了UIApplication来处理。

同时也说明,整个iOS应用,是包含在一个自动释放池block中的。

编译的时候,这段代码会被转换成

{ __AtAutoreleasePool __autoreleasepool;}

其中,出现的结构体__AtAutoreleasePool

struct __AtAutoreleasePool { __AtAutoreleasePool() {atautoreleasepoolobj = objc_autoreleasePoolPush();} ~__AtAutoreleasePool() {objc_autoreleasePoolPop(atautoreleasepoolobj);} void * atautoreleasepoolobj;};

这表明,我们的main函数实际执行了

int main(int argc, const char * argv[]) { { void * atautoreleasepoolobj = objc_autoreleasePoolPush(); // do whatever you want objc_autoreleasePoolPop(atautoreleasepoolobj); } return 0;}

结论 @autoreleasepool
只是帮助我们少写了这两行代码而已,让代码看起来更美观,然后要根据上述两个方法来分析自动释放池的实现。

图片 1Paste_Image.png

图片 2bag.jpg

Autorelease Pool的主要结构

每一个autorelease pool都是由一系列的AutoreleasePoolPage组成

class AutoreleasePoolPage { magic_t const magic; //完整性检验 id *next; pthread_t const thread; //保存当前的进程 AutoreleasePoolPage * const parent; //双线链表使用 指向父节点 AutoreleasePoolPage *child; //双向链表使用 指向子结点 uint32_t const depth; uint32_t hiwat;};

可见,自动释放池的AutoreleasePoolPage是以双向链表的结构连接起来的

而在自动释放池的内存中,AutoreleasePoolPage被以栈结构存储起来,如下图

图片 3AutoreleasePoolPage在内存中的结构

更多详细实现,请移步自动释放池的前世今生 —- 深入解析 Autoreleasepool

返回 6

核心实现代码:<pre><code>`
init(maxW:Int,value:[Int],weight:[Int]) {maxWeight = maxWvalues =
valueweights = weightlet temp:[Int] = [Int].init(repeating: 0,
count: maxWeight + 1)calculate = [[Int]].init(repeating: temp,
count: values.count)}

MRC与ARC时代的Autorelease

MRC(Mannul Reference Counting)和ARC(Automatic Reference
Counting),分别对应着手动引用计数和自动引用计数。

对!是计数,不是“ GC、垃圾回收
”什么的,就是说,在Objective-C的开发中,ARC不代表像Java那样有GC做垃圾回收,所以本质上还是要“手动”管理内存的。也就是说,我们在ARC环境下写的代码,不用自己手动插入“
retainrelease这些消息
”,ARC会在编译时为我们在合适的位置插入,释放不必要的内存。

@autoreleasepool 就跟对象的release密切相关。

MRC时代,如果我们想先retain一个对象,但是并不知道在什么时候可以release它,我们可以像下面这么做:

NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];NSString* str = [[[NSString alloc] initWithString:@"tutuge"] autorelease];//use str...[pool release];//str is released

就是说,我们可以在创建对象的时候给对象发送“ autorelease”消息,然后当
NSAutoreleasePool结束的时候,“标记过”autorelease的对象都会被“
release”掉,也就是会被释放掉。

但是在ARC时代,我们不用手动发送autorelease消息,ARC会自动帮我们加。而这个时候,@autoreleasepool做的事情,跟
NSAutoreleasePool就一模一样了。

这道题关于二叉树的最大路径问题,显然需要用递归解决。由于这道题中所求路径是可以通过根节点的,从任意节点开始和结束的最大路径和。显然,通过根节点的最大路径=根节点的值,左子树的最大路径+右子树的最大路径所以我们可以递归求解左右子树的最大路径,最后把他们加到一起就是整个树的最大路径。

func solveMaxValue()->Int { for i in 1..<values.count { // 注意起始位置 for j in 1...maxWeight { if weights[i] <= j { calculate[i][j] = maxCompare(a: values[i] + calculate[i - 1][j - weights[i]], b: calculate[i-1][j]) } else { calculate[i][j] = calculate[i - 1][j] } } } return calculate[values.count - 1][maxWeight]}`</code></pre>

Autorelease对象的释放时机

实践内容可以参考 你真的懂iOS的autorelease吗?

在上面引用的文章中,笔者用__weak id指针指向一个autorelease对象,在不增加引用的情况下观察autorelease对象的释放情况。

  • 首先在viewDidLoad方法中初始化一个Array对象
  • 分别尝试在viewWillAppearviewDidAppeare方法中观测Array的值

发现,在viewWillAppear方法中,对象未被释放,而到了viewDidAppear中就被释放了,发现Array对象并非超出作用域就马上被释放。得出结论,autorelease并不是根据对象的作用域来决定释放时机。

实际上,autorelease释放对象的依据是Runloop,简单说,runloop就是iOS中的消息循环机制,当一个runloop结束时系统才会一次性清理掉被autorelease处理过的对象,其实本质上说是在本次runloop迭代结束时清理掉被本次迭代期间被放到autorelease pool中的对象的。至于何时runloop结束并没有固定的duration。

既然由runloop来决定对象释放时机而不是作用域,那么,在一个{}内使用循环大量创建对象就有可能带来内存上的问题,大量对象会被创建而没有及时释放,这时候就需要靠我们人工的干预autorelease的释放了。

上文有提到autorelease pool,一旦一个对象被autorelease,则该对象会被放到iOS的一个池:autorelease pool,其实这个pool本质上是一个stack,扔到pool中的对象等价于入栈。我们把需要及时释放掉的代码块放入我们生成的autorelease pool中,结束后清空这个自定义的pool,主动地让pool清空掉,从而达到及时释放内存的目的。以上述图片处理的例子为例,优化如下:

for (int i = 0; i <= 1000; i ++) { //创建一个自动释放池 NSAutoreleasePool *pool = [NSAutoreleasePool new];//也可以使用@autoreleasePool{domeSomething}的方式 NSString *filePath = [[NSBundle mainBundle] pathForResource:@"test" ofType:@"PNG"]; UIImage *image = [[UIImage alloc] initWithContentsOfFile:filePath]; UIImage *image2 = [image imageByScalingAndCroppingForSize:CGSizeMake]; [image release]; //将自动释放池内存释放,它会同时释放掉上面代码中产生的临时变量image2 [pool drain];}

这样,每次循环结束时,可以及时的释放临时对象的内存,其中,对自动释放池的操作可以用上文提到的方法来替代。

@autoreleasePool{ //domeSomeThing;}
/** * Definition of TreeNode: * public class TreeNode { * public int val; * public TreeNode left, right; * public TreeNode { * this.val = val; * this.left = this.right = null; * } * } */public class Solution { /** * @param root: The root of binary tree. * @return: An integer. */ public int maxPathSum(TreeNode root) { if(root == null) return 0; ArrayList<Integer> res = new ArrayList<>(); res.add(Integer.MIN_VALUE); helper(root, res); return res.get; } private int helper(TreeNode root, ArrayList<Integer> res) { if(root == null) return 0; int left = helper(root.left,res); int right = helper(root.right,res); int cur = root.val + (left>0?left:0) + (right>0?right:0); if(cur>res.get res.set; return root.val + Math.max(left, Math.max); } }

可以优化为一维数组:<pre><code>` func
solveMaxValue2()->Int {calculate2 = [Int].init(repeating: 0, count:
maxWeight + 1)

什么时候用@autoreleasepool

根据 Apple的文档 ,使用场景如下:

  • 写基于命令行的的程序时,就是没有UI框架,如AppKitCocoa框架时。
  • 写循环,循环里面包含了大量临时创建的对象。
  • 创建了新的线程。(非Cocoa程序创建线程时才需要)
  • 长时间在后台运行的任务。
 for i in 1..<values.count { for j in (1...maxWeight).reversed() { if weights[i] <= j { calculate2[j] = maxCompare(a: calculate2[j], b: (calculate2[j-weights[i]]+values[i])) } } } return calculate2[maxWeight]}`</code></pre>

什么对象会加入Autoreleasepool中

  1. 使用allocnewcopymutableCopy的方法进行初始化时,由系统管理对象,在适当的位置release
  2. 使用array会自动将返回值的对象注册到Autoreleasepool
  3. __weak修饰的对象,为了保证在引用时不被废弃,会注册到Autoreleasepool中。
  4. id的指针或对象的指针,在没有显示指定时会被注册到Autoleasepool中。

发表评论

电子邮件地址不会被公开。 必填项已用*标注