r语言随机森林回归预测_从零实现回归随机森林

585f47af6eededdf1551cd553419e89f.png

一、前言

回归随机森林作为一种机器学习和数据分析领域常用且有效的算法,对其原理和代码实现过程的掌握是非常有必要的。为此,本文将着重介绍从零开始实现回归随机森林的过程,对于随机森林和决策树的相关理论原理将不做太深入的描述。本文的目的只是为了演示回归随机森林主要功能的具体实现过程,在实现过程中不会考虑代码性能,会更加注重代码可读性。

实现语言:Python

依赖:pandas, numpy

二、原理介绍

随机森林属于Bagging类算法,而Bagging 又属于集成学习一种方法(集成学习方法大致分为Boosting和Bagging方法,两个方法的不同请参考[10]),集成学习的大致思路是训练多个弱模型打包起来组成一个强模型,强模型的性能要比单个弱模型好很多(三个臭皮匠顶一个诸葛亮。注意:这里的弱和强是相对的),其中的弱模型可以是决策树、SVM等模型,在随机森林中,弱模型选用决策树。

在训练阶段,随机森林使用bootstrap采样从输入训练数据集中采集多个不同的子训练数据集来依次训练多个不同决策树;在预测阶段,随机森林将内部多个决策树的预测结果取平均得到最终的结果。本文主要介绍回归随机森林从零实现的过程,实现的RFR(回归随机森林)具有以下功能:

  • 模型训练
  • 模型数据预测
  • 计算feature importance

2.1 模型训练

2.1.1 基础理论

本文实现的RFR是将多个二叉决策树(CART,这也是sklearn,spark内部实现的模型打包组合而成的,训练RFR便是训练多个二叉决策树。在训练二叉决策树模型的时候需要考虑怎样选择切分变量(特征)、切分点以及怎样衡量一个切分变量、切分点的好坏。针对于切分变量和切分点的选择,本实现采用穷举法,即遍历每个特征和每个特征的所有取值,最后从中找出最好的切分变量和切分点;针对于切分变量和切分点的好坏,一般以切分后节点的不纯度来衡量,即各个子节点不纯度的加权和

,其计算公式如下:

(2-1)

其中,

为某一个切分变量,
为切分变量的一个切分值,
分别为切分后左子节点的训练样本个数、右子节点的训练样本个数以及当前节点所有训练样本个数,
分为左右子节点的训练样本集合,
为衡量节点不纯度的函数(impurity function/criterion),分类和回归任务一般采用不同的不纯度函数,在分类和回归常采用的不纯度函数有以下四种:

5f44b63e3efd09d4d3dbf699cc0602ab.png
图 1. 四种不同的impurity function
  • 备注1:

Gini不纯度只适用于分类任务,

为当前节点的训练样本集合,
为当前节点训练样本中目标变量取值k(
)出现的概率。
  • 备注2:

信息熵只适用于分类任务, 其他同备注1。

  • 备注3

MSE只适用于回归任务,

为当前节点样本目标变量的平均值。
  • 备注4

MAE只适用于回归任务,

为当前节点样本目标变量的平均值。
  • 备注5

sklearn内部,DecisionTreeClassifier, RandomForestClassifier等基于决策树的分类模型默认使用'gini'作为impurity function也可通过criterion参数指定为'entropy' DecisionTreeRegressor, RandomForestRegressor等基于决策树的回归模型默认使用'mse'作为impurity function也可通过criterion参数指定为'mae'

决策树中某一节点的训练过程在数学等价于下面优化问题:

(2-2)

即寻找

最小的切分变量和切分点。

在本文实现的回归随机森林中,

选用MSE,即针对某一切分点

(2-3)

2.1.2 训练过程

RFR训练过程示意图如图2所示。

f84922425f38fd8132f4623bec3ae7b8.png
图 2. RFR训练示意图

说明:

  • bootstrap[1]是对输入训练样本集合
    进行
    N次放回重复抽样得到样本集合
    (
    N一般等于
    的大小[2],具体可以查看
    sklearn的实现代码),这暗示了
    中的某些样本会在
    中重复出现多次。在对
    进行bootstrap采样时,
    中每个样本会以
    63.3%的概率被抽中。为什么是63.3%?考虑N次放回重复抽样,每次抽样每个样本被选中的概率为
    ,进行N次抽样,被选中概率为:

(2-4)

具体可以参考[3-4]。

  • subsample会根据输入参数sample_sz的大小从
    中采样
    sample_sz个样本组成subsample样本集合
    (在
    sklearn中,subsample大小就是输入训练样本集合
    的大小N,不可以通过输入参数指定subsample的大小,详见这里)。
  • 为什么要进行bootstrap? 集成学习(包括随机森林)的目的是为了使用多个不同的子模型来增加最终模型预测结果的鲁棒性和稳定性(即减小方差),如果多个子模型都采用同样的数据集训练,那么训练得出的子模型都是相同的,集成学习将变得没有意义,为了能从原输入训练数据集得到多个不同的数据集,需要使用bootstrap来从原数据集采样出不同的子数据集来训练子模型。

图2中n个回归决策树的训练过程如图3所示。

0ab9a31cea77d910ec4988c9a3e38143.png
图 3. 回归决策树训练过程

2.2 模型预测

RFR的预测结果是由内部所有二叉决策树的预测结果取平均值得到的。二叉决策树的预测过程主要分为以下步骤:

  1. 针对某一输入样本,从二叉决策树的根节点起,判断当前节点是否为叶子节点,如果是则返回叶子节点的预测值(即当前叶子中样本目标变量的平均值),如果不是则进入下一步;
  2. 根据当前节点的切分变量的和切分值,将样本中对应变量的值与节点的切分值对比。如果样本变量值小于等于当前节点切分值,则访问当前节点的左子节点;如果样本变量值大于当前节点切分值,则访问当前节点的右子节点;
  3. 循环步骤2,直到访问到叶子节点,并返回叶子节点的预测值。

2.3 计算feature importance

特征的重要性表示特征对预测结果影响程度,某一特征重要性越大,表明该特征对预测结果的影响越大,重要性越小,表明该特征对预测结果越小。RFR中某一特征的重要性,是该特征在内部所有决策树重要性的平均值,而在决策树中,计算某一个特征的重要性可以采用以下三种方法:

方法1

步骤如下:

  • 使用训练数据训练模型;
  • 计算训练数据在模型上依据某一metric的score,记为
    (在回归中,可以选用r2);
  • 遍历训练数据集中的每一个feature,每次在原训练数据集的基础上将对应的feature进行shuffle操作,然后使用模型得到shuffle后数据集的score,记为
    ,最后通过
    计算出第
    个feature的重要性。

方法2

如果一个feature很重要,那么从数据集中去除该feature后,模型的prediction error会增大;相反,如果一个feature不重要,那么从数据集中去除后,模型的prediction error不会变化太大。该方法的步骤如下:

  • 使用原数据集训练模型,并评估模型在训练数据集上的prediction error,记为
    ;
  • 遍历训练数据集中的每一个feature,每次从原训练数据集的基础上去除该feature,然后使用得到数据集训练模型,最终计算出prediction error,记为
    ,最后通过
    计算出第
    个feature的重要性。

该方法和方法1有点类似,但该方法在计算每个feature的重要性的时候都需要重新训练模型,会增加额外的计算量,在实际应用一般不采用该方法。

方法3

该方法是sklearn内部树模型计算feature重要性时采用的方法。即某一节点

的重要性为

(2-5)

其中,

分别为节点
以及其左右子节点中训练样本个数与总训练样本数目的比例,
分为为节点
以及其左右子节点的不纯度。知道每一个节点的重要性之后,即可通过公式(2-6)得出某一feature的重要性。

(2-6)

为了使所有feature的重要性加起来等于1,需要每一feature的重要性进行normalization,即公式(2-7)

(2-7)

方法3在sklearn中的实现,请查看_tree.pyx。

在本文实现的RFR中,同时实现了方法1方法3

三、回归随机森林实现

3.1 代码

代码有点长,不想看的可以直接跳过。

from multiprocessing import Pool, cpu_countclass RFRegressor(object):"""Random Forest Regressor"""def __init__(self, n_trees, sample_sz, min_leaf_sz=5, n_jobs=None, max_depth=None):self._n_trees = n_treesself._sample_sz = sample_szself._min_leaf_sz = min_leaf_szself._n_jobs = n_jobsself._max_depth = max_depthself._trees = [self._create_tree() for i in range(self._n_trees)]def _get_sample_data(self, bootstrap=True):"""Generate training data for each underlying decision treeParameters----------bootstrap: boolean value, True/FalseThe default value is True, it would bootstrap sample from input training data. If False, the exclusive sampling willbe performed.Returns-------idxs: array-like objectReturn the indices of sampled data from input training data"""if bootstrap:idxs = np.random.choice(len(self._X), self._sample_sz)else:idxs = np.random.permutation(len(self._X))[:self._sample_sz]return idxsdef _create_tree(self):"""Build decision treeeReturns-------dtree : DTreeRegressor object"""return DTreeRegressor(self._min_leaf_sz, self._max_depth)def _single_tree_fit(self, tree):"""Fit the single underlying decision treeParameters----------tree : DTreeRegressor objectReturns-------tree : DTreeRegressor object"""sample_idxs = self._get_sample_data()return tree.fit(self._X.iloc[sample_idxs], self._y[sample_idxs])def fit(self, x, y):"""Train a forest regressor of trees from the training set(x, y)Parameters----------x : DataFrame,The training input samples.y : Series or array-like objectThe target values."""self._X = xself._y = yif self._n_jobs:self._trees = self._parallel(self._trees, self._single_tree_fit, self._n_jobs)else:for tree in self._trees:self._single_tree_fit(tree)def predict(self, x):"""Predict target values using trained modelParameters---------x : DataFrame or array-like objectinput samplesReturns-------ypreds : array-like objectpredicted target values"""all_tree_preds = np.stack([tree.predict(x) for tree in self._trees]) return np.mean(all_tree_preds, axis=0)def _parallel(self, trees, fn, n_jobs=1):"""Parallel jobs executionParameters----------trees : list-like objecta list-like object contains all underlying treesfn : function-like objectmap functionn_jobs : integerThe number of jobs.Returns-------result : list-like objecta list-like result object for each call of map function `fn`"""try:workers = cpu_count()except NotImplementedError:workers = 1if n_jobs:workders = n_jobspool = Pool(processes=workers)result = pool.map(fn, trees)return list(result)@propertydef feature_importances_(self):"""Calculate feature importanceReturns-------self._feature_importances : array-like objectthe importance score of each feature """if not hasattr(self, '_feature_importances'):norm_imp = np.zeros(len(self._X.columns))for tree in self._trees:t_imp = tree.calc_feature_importance()norm_imp = norm_imp + t_imp / np.sum(t_imp)self._feature_importances = norm_imp / self._n_treesreturn self._feature_importances@propertydef feature_importances_extra(self):"""Another method to calculate feature importance"""norm_imp = np.zeros(len(self._X.columns))for tree in self._trees:t_imp = tree.calc_feature_importance_extra()norm_imp = norm_imp + t_imp / np.sum(t_imp)norm_imp = norm_imp / self._n_treesimp = pd.DataFrame({'col':self._X.columns, 'imp':norm_imp}).sort_values('imp', ascending=False)return impclass DTreeRegressor(object):def __init__(self, min_leaf_sz, max_depth=None):self._min_leaf_sz = min_leaf_szself._split_point = 0self._split_col_idx = 0self._score = float('inf')self._sample_sz = 0self._left_child_tree = Noneself._right_child_tree = Noneself._feature_importances = []self._node_importance= 0if max_depth is not None:max_depth = max_depth - 1self._max_depth = max_depthdef fit(self, x, y):self._X = xself._y = yself._col_names = self._X.columnsself._feature_importances = np.zeros(len(self._col_names))self._sample_sz = len(self._X)self._val = np.mean(self._y)if self._max_depth is not None and self._max_depth < 2:return selfself._find_best_split()return selfdef _calc_mse_inpurity(self, y_squared_sum, y_sum, n_y):"""Calculate Mean Squared Error impurityThis is just the recursive version for calculating varianceParameters----------y_squared_sum: float or int , the sum  of y squared y_sum: float or int , the sum of y valuen_y: int, the number of samplesReturns-------"""dev = (y_squared_sum / n_y) - (y_sum / n_y) ** 2return devdef _find_best_split(self):for col_idx in range(len(self._col_names)):self._find_col_best_split_point(col_idx)self._feature_importances[self._split_col_idx] = self._node_importanceif self.is_leaf:return left_child_sample_idxs = np.nonzero(self.split_col <= self.split_point)[0]right_child_sample_idxs = np.nonzero(self.split_col > self.split_point)[0]self._left_child_tree = (DTreeRegressor(self._min_leaf_sz, self._max_depth).fit(self._X.iloc[left_child_sample_idxs], self._y[left_child_sample_idxs]))self._right_child_tree = (DTreeRegressor(self._min_leaf_sz, self._max_depth).fit(self._X.iloc[right_child_sample_idxs], self._y[right_child_sample_idxs]))def _find_col_best_split_point(self, col_idx):x_col = self._X.values[:, col_idx]sorted_idxs = np.argsort(x_col)sorted_x_col = x_col[sorted_idxs]sorted_y =  self._y[sorted_idxs]lchild_n_samples = 0lchild_y_sum  = 0.0lchild_y_square_sum = 0.0rchild_n_samples = self._sample_szrchild_y_sum = sorted_y.sum()rchild_y_square_sum = (sorted_y ** 2).sum()node_y_sum = rchild_y_sumnode_y_square_sum = rchild_y_square_sum for i in range(0, self._sample_sz - self._min_leaf_sz):xi, yi = sorted_x_col[i], sorted_y[i]rchild_n_samples -= 1rchild_y_sum -= yirchild_y_square_sum -= (yi ** 2)lchild_n_samples  +=  1lchild_y_sum += yilchild_y_square_sum += (yi ** 2)if i < self._min_leaf_sz  or xi == sorted_x_col[i+1]:continuelchild_impurity = self._calc_mse_inpurity(lchild_y_square_sum,lchild_y_sum, lchild_n_samples)rchild_impurity = self._calc_mse_inpurity(rchild_y_square_sum,rchild_y_sum, rchild_n_samples)split_score  = (lchild_n_samples * lchild_impurity + rchild_n_samples * rchild_impurity) / self._sample_szif split_score < self._score:self._score = split_scoreself._split_point = xiself._split_col_idx = col_idxself._node_importance = (self._sample_sz * (self._calc_mse_inpurity(node_y_square_sum, node_y_sum, self._sample_sz) - split_score))def predict(self, x):if type(x) == pd.DataFrame:x = x.valuesreturn np.array([self._predict_row(row) for row in x])def _predict_row(self, row):if self.is_leaf:return self._valt = (self._left_child_tree if row[self._split_col_idx] <= self.split_point else self._right_child_tree)return t._predict_row(row)def __repr__(self):pr =  f'sample: {self._sample_sz}, value: {self._val}rn'if not self.is_leaf:pr += f'split column: {self.split_name}, split point: {self.split_point}, score: {self._score} 'return prdef calc_feature_importance(self):if self.is_leaf:return self._feature_importancesreturn (self._feature_importances + self._left_child_tree.calc_feature_importance()+ self._right_child_tree.calc_feature_importance())def calc_feature_importance_extra(self):imp = []o_preds = self.predict(self._X.values)o_r2 = metrics.r2_score(self._y, o_preds)for col in self._col_names:tmp_x = self._X.copy()shuffle_col = tmp_x[col].valuesnp.random.shuffle(shuffle_col)tmp_x.loc[:,col] = shuffle_coltmp_preds = self.predict(tmp_x.values)tmp_r2 = metrics.r2_score(self._y, tmp_preds)imp.append((o_r2 - tmp_r2))imp = imp / np.sum(imp)return imp@propertydef split_name(self):return self._col_names[self._split_col_idx]@propertydef split_col(self):return self._X.iloc[:, self._split_col_idx]@propertydef is_leaf(self):return self._score == float('inf')@propertydef split_point(self):return self._split_point

3.2 测试

训练与预测:

e02d630851b460bba5235449108ab47a.png

使用sklearn内部的RandomForestRegressor的结果:

2509a5ac03382b6621f84fc06a294d5b.png

需要注意的是,上面两次训练的样本不同,所以和实际情况有点出入,但大体趋势是对。

feature importance:

RFRegressor采用方法3计算的feature重要性:

3053199af4f88e4532daeee7a68c4adb.png

RandomForestRegressor计算的feature重要性:

7a6d812a2645a1bee59318759c5e2a88.png

RFRegressor采用方法1计算的feature重要性:

7ad074369314912a8c7cfc7c7d55b8f1.png

参考与荐读:

[1] Bootstrap Sample: Definition, Example
[2] Number of Samples per-Tree in a Random Forest
[3] Why on average does each bootstrap sample contain roughly two thirds of observations?
[4] Random Forests out-of-bag sample size
[5] Random Forests Leo Breiman and Adele Cutler
[6] Why is Random Forest with a single tree much better than a Decision Tree classifier?
[7] Winning the bias-variance tradeoff
[8] subsample-bootstrapping
[9] How can SciKit-Learn Random Forest sub sample size may be equal to original training data size?
[10] the difference between bagging and boosting
[11] The Mathematics of Decision Trees, Random Forest and Feature Importance in Scikit-learn and Spark
[12] Sklearn Decision Trees documentation

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/445884.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

openssl编程-基础知识-回调函数

参考内容 OpenSSL编程 赵春平 回调函数 回调函数就是一个通过函数指针调用的函数。如果你把函数的指针&#xff08;地址&#xff09;作为参数传递给另一个函数&#xff0c;当这个指针被用来调用其所指向的函数时&#xff0c;我们就说这是回调函数把一段可执行的代码像参数传递…

openssl编程-基础知识-OpenSSL简介

参考链接 在ubuntu环境下执行openssl编译和安装_MY CUP OF TEA的博客-CSDN博客_openssl ubuntuOpenSSL编程 赵春平 OpenSSL 简介 它提供的主要功能有&#xff1a;SSL协议实现(包括SSLv2、SSLv3和TLSv1)、大量软算法(对称/非对称/摘要)、大数运算、非对称算法密钥生成、ASN.1编…

linux修改文件句柄数生效_linux系统层面调优和常见的面试题

linux系统层面调优和常见的面试题​mp.weixin.qq.com无论对Spark集群&#xff0c;还是Hadoop集群等大数据相关的集群进行调优&#xff0c;对linux系统层面的调优都是必不可少的&#xff0c;这里主要介绍3种常用的调优&#xff1a;1.linux文件句柄linux在整个系统层面和单个进程…

openssl编程-基础知识-OpenSSL堆栈

堆栈介绍 堆栈是一种先进后出的数据结构openssl 大量采用堆栈来存放数据。它实现了一 个通用的堆栈&#xff0c;可以方便的存储任意数据它实现了许多基本的堆栈操作&#xff0c;主要有&#xff1a;堆栈拷贝(sk_dup)、构建新堆栈&#xff08;sk_new_null&#xff0c;sk_new&…

小米用户画像_企鹅智库:高学历用苹果中老年用华为 男性用小米女性用OV

不同手机品牌都有着自己不同的定位人群&#xff0c;在国内市场目前几大非常有名的手机品牌分别被三星、苹果、华为、小米、OV占据&#xff0c;而这些手机品牌的主要购买人群到底是什么样的呢&#xff1f;企鹅智库近日发布了一份手机消费者的调研报告&#xff0c;并且根据消费者…

国密gmtls协议-双证书体系的服务端和客户端通信代码

内容介绍 国密的双证书体系&#xff0c;将证书按照使用目的的不同划分为加密证书和签名证书两种&#xff0c;也就是两对公私钥&#xff0c;二者本质一致&#xff0c;均为SM2密钥对&#xff0c;区别仅体现在用法国密CA体系中&#xff0c;加密密钥对由CA产生&#xff0c;签名密钥…

jwt 私钥_什么是 JSON Web Token(JWT)

有关本文档的快速链接&#xff0c;请参考页面提示。什么是 JSON Web Token(JWT)?JSON Web Token (JWT) 作为一个开放的标准 (RFC 7519) 定义了一种简洁自包含的方法用于通信双方之间以 JSON 对象的形式安全的传递信息。因为有数字签名&#xff0c;所以这些通信的信息能够被校验…

天线下倾角示意图_常用天线和无源器件技术参数汇总

原标题&#xff1a;常用天线和无源器件技术参数汇总一、天线原理天线的定义&#xff1a; 能够有效地向空间某特定方向辐射电磁波或能够有效的接收空间某特定方向来的电磁波的装置。天线的功能&#xff1a; 能量转换-导行波和自由空间波的转换; 定向辐射(接收)-具有一定的方向性…

制作作品图片_不懂人文后期制作流程?来,大师手把手教你

制作前1、处理一张照片思路决定步骤 想要了解学习的老师可以私聊小编fzhdyx222制作后2、2020年最新Camera Raw局部影调和色调的控制想要了解学习的老师可以私聊小编fzhdyx222制作前3、2020年最新Camera Raw基础工具细节性运用想要了解学习的老师可以私聊小编fzhdyx222制作后4、…

xlsx文件打开乱码_Excel 2016 双击无法打开xlsx文件怎么办?

最近我重装了系统(Win10Office2016)&#xff0c;然后发现了一个奇怪的bug&#xff1a;双击xlsx文件&#xff0c;只能打开Excel窗口&#xff0c;但是打不开这个文件&#xff0c;有时候再次双击就能打开了&#xff0c;但有时再次双击也不管用&#xff0c;需要在Excel的菜单中点“…

gmssl使用双证书双向认证的gmtl协议报错crypto/sm2/sm2_sign.c 510: sm2_do_verifySSL3 alert write:fatal:decrypt error

报错内容 crypto/sm2/sm2_sign.c 510: sm2_do_verify SSL3 alert write:fatal:decrypt error SSL_accept:error in error ERROR 140655864152064:error:1417B07B:SSL routines:tls_process_cert_verify:bad signature:ssl/statem/statem_srvr.c:2941: 相关内容 版本&#xf…

纠偏的意思_承压能力和纠偏能力,决定成长的高度

承压能力&#xff0c;包含抗压能力、抵御能力&#xff0c;担当能力&#xff0c;分解能力&#xff0c;消化能力&#xff0c;释放能力&#xff0c;等一系列的精神要素&#xff0c;是一个人生存生活工作中一项重要的素质。有的人&#xff0c;承压能力很强&#xff0c;无论经受什么…

函数指针作为形参进行调用

代码 两个代码均位于namespace作用域之内addOne将传递进来的形参进行加一&#xff0c;然后返回performance_test函数主要是想简化函数调用&#xff0c;两个形参&#xff0c;第一个表示循环的次数&#xff0c;第二个是带参数的函数指针&#xff0c;函数内部初始化start和end两个…

解决吉大正源(身份认证网关|USBKey)和gmssl(server|client)使用gmtl协议交叉互通报错tlsv1 alert decrypt error

报错内容 SSL_connect:error in SSLv3/TLS write finished140057291788288:error:1409441B:SSL routines:ssl3_read_bytes:tlsv1 alert decrypt error:ssl/record/rec_layer_s3.c:1385:SSL alert number 51 报错原因 gmssl库生成 certificate verify 消息时&#xff0c;对自客…

12无法使用otg_12个冷知识:或许只能看看而无法使用,但却真实存在着

12个或许只能看看而无法使用&#xff0c;但却真实存在着。脸红一所有已知动物中&#xff0c;唯一可以脸红的是人类。二有些地区将雨水归类为公共财物&#xff0c;作为公共财物是不允许收集的&#xff0c;违反者将面临处罚。三世界上汽车研发成本最高的一款车是福特蒙迪欧&#…

三目运算符_C语言知识点:运算符的优先级和结合性

运算符是一种告诉编译器执行特定的数学或逻辑操作的符号。C语言内置了丰富的运算符&#xff0c;大体可分为10类&#xff1a;算术运算符、关系运算符、逻辑运算符、位操作运算符、赋值运算符、条件运算符、逗号运算符、指针运算符、求字节数运算符和特殊运算符。根据运算符可操作…

可以直接进行运算么_WORD办公技巧:如何直接在WORD中进行加法、乘法运算?

排版目标下图文档中有一张2020年&#xff11;&#xff0d;&#xff13;月口罩购买情况统计表&#xff0c;数据量并不大&#xff0c;我们想不动用excel表格进行统计&#xff0c;直接利用WORD自带的函数公式计算出表格内空白单元格的数值。其中&#xff0c;金额&#xff1d;单价&…

《剑指Offer》36:二叉搜索树与双向链表

题目 输入一棵二叉搜索树&#xff0c;将该二叉搜索树转换成一个排序的双向链表。要求不能创建任何新的节点&#xff0c;只能调整树中节点指针的指向。比如&#xff0c;输入下图中的二叉搜索树&#xff0c;输出转换之后的排序双向链表。 二叉树节点的定义如下&#xff1a; pub…

窗口位置按钮取消_VBA002:“宏”的保存位置有哪几种方式?

商务合作请加微信 | Allen_Lyq文章投稿 | jiangjunpeng1996126.com微信公众号 | Word和Excel达人先生头条号 | 跟小小筱学办公技能通过上一篇文章的学习&#xff0c;我们已经知道宏的基本用法&#xff0c;在录制宏的过程中&#xff0c;还有几个点需要我们注意下&#xff1a;宏命…

《剑指Offer》38:字符串的排列

题目 输入一个字符串&#xff0c;打印该字符中字符的所有排列。 例如&#xff0c;输入字符串abc&#xff0c;则打印出由字符a、b、c所能排列出来的所有字符串有abc、acb、bac、bca、cab、cba 分析 把一个字符串看成由两部分组成&#xff1a;第一部分是它的第一个字符&#…