node2-^_prev - no del;
cout ? no del. use__count () ? endl; cout ? node2. use__count () ? endl; return 0;
no del和no de2两个智能指针对象指向两个节点,引用计数变为1,我们不需要手动 delete nodel的_next指向node2, node2的丄工邮指向no del,引用计数变成2
no del和no de2析构,引用计数减到1,但是_mxt还指向下一个节点,_prev指向上一 个节点
也就是说一next析构了,no de2释放了 也就是说_prev析构了,no del释放了
但是一next属于node的成员,no del释放了,_mxt才会析构,而rodel由_prevW理, _?工印属于1101^2成员,所以这就叫循环引用,谁都不会释放
n—1想餐桥
皆.戦安 尢断中的富如.
it■先桥构心3中第 Kiu.就这#峪入r死
解决方案
在引用计数的场景下,把sharedjtr换成weak_ptr就可以了
原理就是,nodel->_next = node2;和 node2->_prev = no del;时 weak_ptr 的 _next 和口通卩不会増加nodel和node2的引用计数
struct ListNode [
int _data;
weak_ptr cout ? \ r int main。 [ shared_ptr node2~^_prev - no del; cout ? no del. use__count () ? endl; cout ? node2. use__count () ? endl; return 0; } 如果不是new出来的空间如何用智能指针管理呢? 其实shared_ptr设计了 一个删除器来解决这个问题 //仿函数的删除器 template'Cclass T> struct FreeFunc { void operator () (T* ptr) [ cout ? \ free (ptr); I }; template'Cclass T> struct DeleteAxrayFunc { void operator () (T* ptr) [ cout ? \ delete[] ptr; } }; int main() [ FreeFunc shared_ptr shared_ptr } 37. 如何访问非法内存区? 1、在下列程序中,i和*Pi都是未初始化的变量,它们的值都是不确定的。而Pi指向 的是 未知位置,不属于程序所拥有的存儲单元,该指针变量称为野指针 #include main。 [ int i,*pi; *pi=5; printf (°%d\\n°, i, *pi); return 0; 2、 使用已经释放过后的指针 堆空间用空闲链表法来组织,释放后的地址返回链表中,可能其他函数申请了该地址处 的空间。如果写了其他函数使用的空间,可能导致其他程序出错。malloc 3、 指针所指向的变量在指针之前被销毁 例如,指针指向了某个函数中的局部变量,当函数返回后,局部变量被销毁,如果栈空 间又被使用,再使用该指针可能就会出错。 38. 决策树是如何解决过拟合问题的? 产生过度拟合数据问题的原因 原因1:样本问题 (1) 样本里的噪音数据干扰过大,大到模型过分记住了噪音特征,反而忽略了真 实 的输入输出间的关系5(什么是噪音数据?) (2) 样本抽职错误,包括(但不限于)样本数量太少,抽样方法错误,抽样时没 有足 够正确考虑业务场景或业务特点,等等导致抽出的样本数据不能有效足够代表业务 逻辑或业务场景; (3) 建模时使用了样本中太多无关的输入变量。 原因2:构建决策树的方法问题 在决策树模型搭建中,我们使用的算法对于决策树的生长没有合理的限制和修萸的 话,决策树的自由生长有可能每片叶子里只包含单纯的事件数据或非事件数据,可以想 象,这种决策树当然可以完美匹配(拟合)训练数据,但是一旦应用到新的业务真实数 据时,效果是一塌糊'涂。 二.如何解决过度拟合数据问题 针对原因1的解决方法: 合理、有效地抽样,用相对能够反映业务逻辑的训练集去产生决策树; 针对原因2的解决方法(主荽): 萸枝:提前停止树的増长或者对已经生成的树按照一定的规则进行后萸枝。 萸枝的方法 萸枝是一个简化过拟合决策树的过程。有两种常用的萸枝方法: (1)先萸枝(prepruning):通过提前停止树的构建而对树\萸枝”,一旦停止, 节点 就成为树叶。该树叶可以持有子集元组中最频繁的类; 先萸枝的方法 有多种不同的方式可以让决策树停止生长,下面介绍几种停止决策树生长的方法: 限制决策树的高度和叶子结点处样本的数目 ―1.定义一个高度,当决策树达到该高度时就可以停止决策树的生长,这是一种最为 简单的 方法; 2. 达到某个结点的实例具有相同的特征向量,即使这些实例不属于同一类,也可以 停 止决策树的生长。这种方法对于处理数据中的数据冲突问题非常有效: 3. 定义一个阚值,当达到某个结点的实例个数小于该阚值时就可以停止决策树的生 长; 4. 定义一个阚值,通过计算每次扩张对系统性能的増益,并比较増益值与该阚值的 大 小来决定是否停止决策树的生长。 ⑵后萸枝(postpruning):它首先构造完整的决策树,允许树过度拟合训练数据, 然后对那些置信度不够的结点子树用叶子结点来代替,该叶子的类标号用该结点子树中 最频繁的类标记。后萸枝的萸枝过程是删除一些子树,然后用其叶子节点代替,这个叶 子节点所标识的类别通过大多数原则(majority class criterion)确定。所谓大多数原 则,是指萸枝过程中,将一些子树删除而用叶节点代替,这个叶节点所标识的类别用这 棵子树中大多数训练样本所属的类别来标识,所标识的类称为majority class .相比于 先萸枝,这种方法更常用,正是因为在先萸枝方法中精确地估计何时停止树増长很困难。 后萸枝的方法 DEEP方法是一种比较简单的后萸枝的方法,在该方法中,可用的数据被分成两 个样例 集合:一个训练集用来形成学习到的决策树,一个分离的验证集用来评估这个决 策树在后续数据上的精度,确切地说是用来评估修萸这个决策树的影响。这个方法的动 机是:即使学习器可能会被训练集中的随机错误和巧合规律所误导,但验证集合不大可 能表现出同样的随机'波动。所以验证集可以用来对过度拟合训练集中的虚假特征提供防 护检燈。 该萸枝方法考虑将书上的每个节点作为修萸的候选对象,决定是否修萸这个结点有 如下步骤组成: 1:删除以此结点为根的子树 2:使其成为叶子结点 3:赋予该结点关联的训练数据的最常见分类 4:当修萸后的树对于验证集合的性能不会比原来的树差时,才真正删除该结点 因为训练集合的过拟合,使得验证集合数据能够对其进行修正,反复进行上面的操 作,从底向上的处理结点,删除那些能够最大限度的提高验证集合的精度的结点,直到 进一步修萸有害为止(有害是指修萸会减低验证集合的精度)。 REP是最简单的后萸枝方法之一,不过由于使用独立的测试集,原始决策树相比, 修改 后的决策树可能偏向于过度修萸。这至因为一些不会再测试集中出现的很稀少的训 练集实例所対应的分枝在荆枝辿如果训练集段小,通常不岑虑釆用KEP算法。 尽管REP有这个缺点,不过REP仍然作为一种基准来评价其它萸枝算法的性能。它 对于两阶段决策树学习方法的优点和缺点提供了了一个很好的学习思路。由于验证集合 没有参与决策树的创建,所以用REP萸枝后的决策树对于测试样例的偏差要好很多,能 够解决一定程度的过拟合问题。 2)PEP,悲观错误萸枝,悲观错误萸枝法是根据萸枝前后的错误率来判定子树的修 萸。该 方法引入了统计学上连续修正的概念弥补REP中的缺陷,在评价子树的训练错误 公式中添加了一个常数,假定每个叶子结点都白动对实例的某个部分进行错误的分类。 它不需要像REP (错误率降低修萸)样,需要用部分样本作为测试数据,而是完全使用训 练数据来生成决策树,又用这些训练数据来完成萸枝。决策树生成和萸枝都使用训练集, 所以会产生错分。 把一棵子树(具有多个叶子节点)的分类用一个叶子节点来替代的话,在训练集上 的误判率肯定是上升的,但是在测试数据上不一定,我们需要把子树的误判计算加上一 个经验性的惩罚因子,用于估计它在测试数据上的误判率。对于一棵叶子节点,它覆盖 了 N个样本,其中有E个错误,那么该叶子节点的错误率为(E+0.5)/No这个0.5就 是惩罚因子,那么对于该棵子树,假设它有L个叶子节点,则该子树的误判率估计为: (£&+0.5*乙)/少, 萸枝后该子树内部节点变成了叶子节点,该叶子结点的误判个数J同样也需要 加上 一个惩罚因子,变成Jb. 5。那么子树是否可以被萸枝就取决于萸枝后的错误5 在 工 Ei + 0.5 * L 的标准误差内。对于样本的误差率e,我们可以根据经验把它估计成伯努利分布,那么 可以估计出该子树的误判次数均值和标准差 E^subtree.err.count) = N * e var^subtree-err.count) = \\/N * e * (1 — e) 使用训练数据,子树总是比替换为一个叶节点后产生的误差小,但是使用校正的 误 差计算方法却并非如此。萸枝的条件:当子树的误判个数大过对应叶节点的误判个数 —个标准差之后,就决定萸枝: E^subtree.err.count) — var(subtree-err.count) > E(leaf.err.count) 这个条件就是萸枝的标准。当然并不一定非要大一个标准差,可以给定任意的 萤信 区间,我们设定一定的显著性因子,就可以估算出误判次数的上下界。 39. 什么是GBDT算法? GBDT (Gradient Boosting Decision Tree)梯度提升迭代决策树。GBDT 也是 Boosting算法的 一种,但是和.WaBoost算法不同(AdaBoost算法上一篇文章已经介 绍);区别如下:AdaBoost算法是利用前一轮的弱学习器的误差来更新样本权重值, 然后一轮一轮的迭代;GBDT也是迭代,但是GBDT要求弱学习器必须是CART模型, 而且GBDT在模型训练的时候,是要求模型预测的样本损失尽可能的小。 每一轮预测和实际值有残差,下一轮根据残差再进行预测,最后将所有预测相加,就是 结果。