当前位置:   article > 正文

Python-Level5-day03:线性回归变种之加正则项,模型保存与加载,线性回归案例,决策树实现回归与波士顿房价案例,决策树剪枝_回归问题中加入正则矩阵

回归问题中加入正则矩阵

三、线性回归模型变种

1. 正则化

1)什么是正则化

过拟合还有一个常见的原因,就是模型参数值太大,所以可以通过抑制参数的方式来解决过拟合问题.如下图所示,右图产生了一定程度过拟合,可以通过弱化高次项的系数(但不删除)来降低过拟合.

补充:超参数即提前设置好的参数,例如学习率与迭代次数等,依赖于经验值。

模型参数即模型里面参数,y=wx+b中的w与b,是需要通过训练得到。

几对中括号就几维数组,因为不能排除一个样本可能有多个特征值给你预测结果。img

例如,可以通过在\theta_3, \theta_4的系数上添加一定的系数,来压制这两个高次项的系数,这种方法称为正则化. 但在实际问题中,可能有更多的系数,我们并不知道应该压制哪些系数,所以,可以通过收缩所有系数来避免过拟合.

2)正则化的定义

正则化是指,在目标函数后面添加一个范数(正则项),来防止过拟合的手段,这个范数定义为:

||x||p=(i=1N|x|p)1p

当p=1时,称为L1范数(即所有系数绝对值之和):

||x||1=(i=1N|x|)

当p=2是,称为L2范数(即所有系数平方之和再开方):

||x||2=(i=1N|x|2)12

通过对目标函数添加正则项,整体上压缩了参数的大小,从而防止过拟合.

2. Lasso回归与岭回归

Lasso 回归和岭回归(Ridge Regression)都是在标准线性回归的基础上修改了损失函数的回归算法. Lasso回归全称为 Least absolute shrinkage and selection operator,又译“最小绝对值收敛和选择算子”、”套索算法”,‘入’表示正则强度。其Lasso 回归损失函数如下所示:

E=1ni=1N(yiyi)+λ||w||1

岭回归损失函数为:

E=1ni=1N(yiyi)+λ||w||2

从逻辑上说,Lasso回归和岭回归都可以理解为通过调整损失函数,减小函数的系数来实现损失函数最小化同时,也从而避免过于拟合于样本,降低偏差较大的样本的权重和对模型的影响程度.冒头就打。实际中岭回归用的多一点,效果好一点。

以下关于Lasso回归于岭回归的sklearn实现对比:考虑几个异常点情况,但也不能完全考虑。就用这两种线性变种回归。

# Lasso回归和岭回归示例
import numpy as np
# 线性模型
import sklearn.linear_model as lm
# 模型性能评价模块
import sklearn.metrics as sm
import matplotlib.pyplot as mp
​
x, y = [], []  # 输入、输出样本
with open("abnormal.txt", "rt") as f:
    for line in f.readlines():
        data = [float(substr) for substr in line.split(",")]
        x.append(data[:-1])
        y.append(data[-1])
​
x = np.array(x)  # 二维数据形式的输入矩阵,一行一样本,一列一特征
y = np.array(y)  # 一维数组形式的输出序列,每个元素对应一个输入样本
# print(x)
# print(y)
​
# 创建线性回归器
model = lm.LinearRegression()
# 用已知输入、输出数据集训练回归器
model.fit(x, y)
# 根据训练模型预测输出
pred_y = model.predict(x)
​
# 创建岭回归器并进行训练
# Ridge: 第一个参数为正则强度,该值越大,异常样本权重就越小
model_2 = lm.Ridge(alpha=200, max_iter=1000)  # 创建对象, max_iter为最大迭代次数
model_2.fit(x, y)  # 训练
pred_y2 = model_2.predict(x)  # 预测
​
# lasso回归
model_3 = lm.Lasso(alpha=0.5,  # L1范数相乘的系数
                   max_iter=1000)  # 最大迭代次数
model_3.fit(x, y)  # 训练
pred_y3 = model_3.predict(x)  # 预测
​
# 可视化回归曲线
mp.figure('Linear & Ridge & Lasso', facecolor='lightgray')
mp.title('Linear & Ridge & Lasso', fontsize=20)
mp.xlabel('x', fontsize=14)
mp.ylabel('y', fontsize=14)
mp.tick_params(labelsize=10)
mp.grid(linestyle=':')
mp.scatter(x, y, c='dodgerblue', alpha=0.8, s=60, label='Sample')
sorted_idx = x.T[0].argsort()
​
mp.plot(x[sorted_idx], pred_y[sorted_idx], c='orangered', label='Linear')  # 线性回归
mp.plot(x[sorted_idx], pred_y2[sorted_idx], c='limegreen', label='Ridge')  # 岭回归
mp.plot(x[sorted_idx], pred_y3[sorted_idx], c='blue', label='Lasso')  # Lasso回归
​
mp.legend()
mp.show()

以下是执行结果:img

四、模型保存与加载

可以使用Python提供的功能对模型对象进行保存.实际保存模型(例如线性表达式)的训练出来的参数,用于提供给别人用,或者下次接着训练(即增量训练),这样之前训练成果得到保留,如果训练过程需要改学习率等参数就可以用这种增量训练方法。模型保存加载使用方法如下:

import pickle
# 保存模型
pickle.dump(模型对象, 文件对象)   
# 加载模型
model_obj = pickle.load(文件对象)

保存训练模型应该在训练完成或评估完成之后,完整代码如下:

# 模型保存示例
import numpy as np
import sklearn.linear_model as lm # 线性模型
import pickle
​
x = np.array([[0.5], [0.6], [0.8], [1.1], [1.4]])  # 输入集
y = np.array([5.0, 5.5, 6.0, 6.8, 7.0])  # 输出集
​
# 创建线性回归器
model = lm.LinearRegression()
# 用已知输入、输出数据集训练回归器
model.fit(x, y)
​
print("训练完成.")
​
# 保存训练后的模型:线性模型保存的是w0与w1
with open('linear_model.pkl', 'wb') as f:
    pickle.dump(model, f)
    print("保存模型完成.")

执行完成后,可以看到与源码相同目录下多了一个名称为linear_model.pkl的文件,这就是保存的训练模型.使用该模型代码:

# 模型加载示例
import numpy as np
import sklearn.linear_model as lm  # 线性模型
import sklearn.metrics as sm  # 模型性能评价模块
import matplotlib.pyplot as mp
import pickle
​
x = np.array([[0.5], [0.6], [0.8], [1.1], [1.4]])  # 输入集
y = np.array([5.0, 5.5, 6.0, 6.8, 7.0])  # 输出集
​
# 加载模型
with open('linear_model.pkl', 'rb') as f:
    model = pickle.load(f)
    print("加载模型完成.")
​
# 根据加载的模型预测输出
pred_y = model.predict(x)
​
# 可视化回归曲线
mp.figure('Linear Regression', facecolor='lightgray')
mp.title('Linear Regression', fontsize=20)
mp.xlabel('x', fontsize=14)
mp.ylabel('y', fontsize=14)
mp.tick_params(labelsize=10)
mp.grid(linestyle=':')
mp.scatter(x, y, c='blue', alpha=0.8, s=60, label='Sample')
​
mp.plot(x, pred_y, c='orangered', label='Regression')
​
mp.legend()
mp.show()

执行结果和训练模型预测结果一样.

五、总结

(1)什么是线性模型:线性模型是自然界最简单的模型之一,反映自变量、因变量之间的等比例增长关系

(2)什么时候使用线性回归:线性模型只能用于满足线性分布规律的数据中

(3)如何实现线性回归:给定一组样本,给定初始的w和b,通过梯度下降法求最优的w和b

六、练习

(1)说明线性回归的特点、过程:适用于数据简单线性分布分布情况,先给初始线性回归模型,给初始值,用损失函数去不断优化到最小,然后用这个模型去预测。

(2)列举生活中常见的线性回归应用场景

(3)什么是梯度下降?梯度下降的作用是什么?为什么要使用梯度下降?:沿着损失函数的负方向优化调整参数方式叫梯度下降。目的是找到损失函数最小值来确定最优参数,直接算不出来,只能梯度下降进行迭代。

(4)已知某公司电视广告投入和营业收入之间的关系如下表格所示:

广告投入销售收入
230.122.1
44.510.4
17.29.3
151.518.5
180.812.9
57.511.8
120.213.2
8.64.8

编写代码,使用线性回归模型预测,电视广告投入300/400/500,销售收入为多少?计算并绘图.

# 04_exercise_01.py
# 利用线性回归,预测不同广告投入的销售收入
# 利用LinearRegression实现线性回归
import numpy as np
import sklearn.linear_model as lm  # 线性模型# 线性模型
import sklearn.metrics as sm  # 模型性能评价模块
import matplotlib.pyplot as mp
​
train_x = np.array([[230.1],
                    [44.5],
                    [17.2],
                    [151.5],
                    [180.8],
                    [57.5],
                    [120.2],
                    [8.6]])  # 输入集
train_y = np.array([22.1, 10.4, 9.3, 18.5, 12.9,
                    11.8, 13.2, 4.8])  # 输出集
​
# 创建线性回归器
model = lm.LinearRegression()
# 用已知输入、输出数据集训练回归器
model.fit(train_x, train_y)
​
# 根据测试数据模型预测输出
test_x = np.array([[0.5], [300], [400], [500]])#0.5为了画线从原点附近出发
pred_y = model.predict(test_x)  # 执行预测
print("预测结果:", pred_y)
​
print("coef_:", model.coef_)  # 系数
print("intercept_:", model.intercept_)  # 截距
​
# 可视化回归曲线
mp.figure('Linear Regression', facecolor='lightgray')
mp.title('Linear Regression', fontsize=20)
mp.xlabel('x', fontsize=14)
mp.ylabel('y', fontsize=14)
mp.tick_params(labelsize=10)
mp.grid(linestyle=':')
​
# 绘制样本点
mp.scatter(train_x, train_y, c='blue', alpha=0.8, s=60, label='Sample')
​
# 绘制拟合直线
mp.plot(test_x,  # x坐标数据
        pred_y,  # y坐标数据
        c='orangered', label='Regression')
​
mp.legend()
mp.show()

(5)编写代码,实现以下功能:

  • 给出10个x值,计算y, y=2x

  • 在计算出的y值上加入噪声值(噪声值范围1~3之间)

  • 使用第一步、第二步产生的x,y作为样本数据,执行线性回归

  • 可视化原始样本、预测结果

# 05_exercise_02.py
import numpy as np
import sklearn.linear_model as lm
import matplotlib.pyplot as mp
import random
​
# 定义样本
x = np.array([[1], [2], [3], [4], [5],
              [6], [7], [8], [9], [10]])
y = 2.0 * x # 计算y
noise = np.random.rand(len(x), 1) * 3#产生噪声
print(noise)
y += noise # 给y添加噪声
​
# 定义模型
model = lm.LinearRegression()
model.fit(x, y)
test_x = np.array([[0.5], [12]])
pred_y = model.predict(test_x) # 使用测试数据预测
​
# 可视化
mp.figure("Linear")
mp.title("Linear")
mp.xlabel("x")
mp.ylabel("y")
mp.grid(linestyle=":")
mp.scatter(x, y, c="blue", label="samples")
mp.plot(test_x, pred_y, c="red", label="linear")
mp.legend()
mp.show()

补充知识

1. R2系数详细计算

R2系数详细计算过程如下:

若用y_i表示真实的观测值,用\bar{y}表示真实观测值的平均值,用\hat{y_i}表示预测值则,有以下评估指标:

  • 回归平方和(SSR)

SSR=i=1n(yi^y¯)2

估计值与平均值的误差,反映自变量与因变量之间的相关程度的偏差平方和.

  • 残差平方和(SSE)

SSE=i=1n(yiyi^)2

即估计值与真实值的误差,反映模型拟合程度.

  • 总离差平方和(SST)

SST=SSR+SSE=i=1n(yiy¯)2

即平均值与真实值的误差,反映与数学期望的偏离程度.

  • R2_score计算公式

R2_score,即决定系数,反映因变量的全部变异能通过回归关系被自变量解释的比例.计算公式:

R2=1SSESST

即:

R2=1i=1n(yiy^i)2i=1n(yiy¯)2

进一步化简为:

R2=1i(yiyi)2/ni(yiy^)2/n=1RMSEVar

分子就变成了常用的评价指标均方误差MSE,分母就变成了方差,对于R^2可以通俗地理解为使用均值作为误差基准,看预测误差是否大于或者小于均值基准误差.

R2_score = 1,样本中预测值和真实值完全相等,没有任何误差,表示回归分析中自变量对因变量的解释越好.

R2_score = 0,此时分子等于分母,样本的每项预测值都等于均值.

2. 线性回归损失函数求导过程

线性函数定义为:

y=w0+w0x1

采用均方差损失函数:

loss=12(yy)2

其中,y为真实值,来自样本;y'为预测值,即线性方程表达式,带入损失函数得:

loss=12(y(w0+w1x1))2

将该式子展开:

loss=12(y22y(w0+w1x1)+(w0+w1x1)2)12(y22yw02yw1x1+w02+2w0w1x1+w12x12)

对w_0求导:

lossw0=12(02y0+2w0+2w1x1+0)=12(2y+2w0+2w1x1)=122(y+(w0+w1x1))=(y+y)=(yy)

对w_1求导:

lossw1=12(002yx1+0+2w0x1+2w1x12)=12(2yx1+2w0x1+2w1x12)=122x1(y+w0+w1x1)=x1(y+y)=x1(yy)

推导完毕.

一、决策树

1. 什么是决策树

决策树是一种常见的机器学习方法,其核心思想是相同(或相似)的输入产生相同(或相似)的输出,通过树状结构来进行决策,其目的是通过对样本不同属性的判断决策,将具有相同属性的样本划分到一个叶子节点下,从而实现分类或回归. 以下是几个生活中关于决策树的示例.

【示例1】img

男生看女生与女生看男生的决策树模型

【示例2】img

挑选西瓜的决策树模型

在上述示例模型中,通过对西瓜一系列特征(色泽、根蒂、敲声等)的判断,最终我们得出结论:这是否为一个好瓜. 决策过程中提出的每个判定问题都是对某个属性的“测试”,例如“色泽=?”,“根蒂=?”. 每个测试的结果可能得到最终结论,也可能需要进行下一步判断,其考虑问题的范围是在上次决策结果限定范围之内. 例如若在“色泽=青绿”之后再判断“根蒂=?”.

2. 决策树的结构

一般来说,一棵决策树包含一个根节点、若干个内部节点和若干个叶子节点. 叶子节点对应最终的决策结果,其它每个节点则对应与一个属性的测试. 最终划分到同一个叶子节点上的样本,具有相同的决策属性,可以对这些样本的值求平均值来实现回归,对这些样本进行投票(选取样本数量最多的类别)实现分类.img

3. 如何构建决策树

1)构建决策树算法

决策树的构建,就是不断选取好的特征作为决策节点,构建一颗泛化能力较强的树结构,其基本算法描述如下:img

显然,决策树的构建是一个递归的过程,核心是以下两个问题:

  • 如何选取特征. 决策树构建的每一步,应该挑选最优最强的特征,进行决策对数据集划分效果最好;

  • 决定何时停止分裂子节点.

2)如何选择特征

① 信息熵

信息熵(information entropy)是度量样本集合纯度的常用指标,该值越大,表示该集合纯度越低(或越混乱),该值越小,表示该集合纯度越高(或越有序). 信息熵定义如下:

H=i=1nP(xi)log2P(xi)

其中,P(x_i)表示集合中第i类样本在总的类数所占比例,当P(x_i)为1时(只有一个类别,比例为100%), log_2P(x_i)的值为0,整个系统信息熵为0;当类别越多,则P(x_i)的值越接近于0,log_2P(x_i)趋近去负无穷大,整个系统信息熵就越大.以下代码,展示了类别数量从1...10的集合信息熵变化:

# 信息熵计算演示
import math
import numpy as np
import matplotlib.pyplot as mp
​
class_num = 10  # 类别最大数量
​
​
def entropy_calc(n):
    p = 1.0 / n  # 计算每个类别的概率
    entropy_value = 0.0  # 信息熵
​
    for i in range(n):
        p_i = p * math.log(p)
        entropy_value += p_i
​
    return -entropy_value  # 返回熵值
​
​
entropies = []
for i in range(1, class_num + 1):
    entropy = entropy_calc(i)  # 计算类别为i的熵值
    entropies.append(entropy)
​
print(entropies)
​
# 可视化回归曲线
mp.figure('Entropy', facecolor='lightgray')
mp.title('Entropy', fontsize=20)
mp.xlabel('Class Num', fontsize=14)
mp.ylabel('Entropy', fontsize=14)
mp.tick_params(labelsize=10)
mp.grid(linestyle='-')
x = np.arange(1, 11, 1)
print(x)
mp.plot(x, entropies, c='orangered', label='entropy')
​
mp.legend()
mp.show()

执行结果:img

② 信息增益

决策树根据属性进行判断,将具有相同属性的样本划分到相同节点下,此时,样本比划分之前更加有序(混乱程度降低),信息熵的值有所降低。用划分前的信息熵减去划分后的信息熵,就是决策树获得的信息增益。可以用以下表达式表示:

Gain(D,a)=Ent(D)v=1V|Dv||D|Ent(Dv)

其中,D表示样本集合,a表示属性,v表示属性可能的取值{v^1, v^2,...,v^n}, \frac{|D^v|}{|D|}表示权重,样本越多的分支对分类结果影响更大,赋予更高的权重, Gain(D, a)表示在样本集合D上使用属性a来划分子节点所获得的信息增益. 以下是一个关于信息增益计算的示例.img

说明:

  • 香蕉占2/5,所以P_1=0.4;梨占1/5,所以P_2 = 0.2;黄瓜占1/5,所以P_3 = 0.4

  • 根节点信息熵:-(0.4 * log_2 0.4 + 0.2 * log_2 0.2 + 0.4 * log_2 0.4) \approx 1.522

  • 根据颜色特征划分后:黄色分支信息熵-(\frac{2}{3} * log_2 \frac{2}{3} + \frac{1}{3} * log_2 \frac{1}{3}) \approx 0.918;绿色分支信息熵-(1.0 * log_2 1.0) = 0;整个第二层信息熵为0.6 * 0.918 + 0.4 * 0 \approx 0.55

  • 根据颜色划分后的信息增益:1.522 - 0.55 \approx 0.97

由以上示例可知,经过对样本按颜色进行类别划分,划分后的信息熵比原来下降了,下降的值就是信息增益。一般来说,信息增益越大,以该属性划分所获得的“纯度提升”越大. 著名的ID3决策树学习算法就是以信息增益为准则来划分属性.

③ 增益率

增益率不直接采用信息增益,而采用信息增益与熵值的比率来作为衡量特征优劣的标准. C4.5算法就是使用增益率作为标准来划分属性. 增益率定义为:(上面的例子增益率为0.97/1.522)

Gain_ratio(D,a)=Gain(D,a)IV(a)

其中

IV(a)=v=1V|Dv||D|log2|Dv||D|

④ 基尼系数

基尼系数定义为:

Gini(p)=k=1kpk(1pk)=1k=1kpk2

直观来说,基尼系数反映了从数据集D中随机抽取两个样本,类别标记不一致的概率(例如10个球里面放5白5红,或者1白9红). 因此,基尼系数越小,数据集的纯度越高. CART决策树(Classification And Regression Tree)使用基尼系数来选择划分属性,选择属性时,选择划分后基尼值最小的属性作为最优属性. 采用和上式相同的符号表示,数据集D下属性a的基尼系数定义为:

Gini_index(D,a)=v=1V|Dv||D|Gini(Dv)

3)如何停止分裂

以下几种情况会停止决策树子节点的构建:

  • 当前节点所有样本属于同一个类别,无需划分,例如例子中已经都是黄瓜就不要划分了,香蕉和梨子还能根据形状再分

  • 当前属性集为空,或者所有样本取值相同,无法划分,没有特征值供你再分了。

  • 当前节点包含的样本集合为空,不能划分,即划分到节点下面没东西了。

  • 当前节点样本数量少于指定数量,防止过拟合。都是香蕉就不需要再划分了。

4. 如何实现决策树

scikit-learn中决策树相关API:

# 模型
model = st.DecisionTreeRegressor(max_depth=4)  # 决策树回归器 深度太深容易过拟合
# 训练
model.fit(train_x, train_y)
# 预测
pre_test_y = model.predict(test_x)

【案例】波士顿房价预测

决策树可以说是一个万能模型,既可以父类也可以回归,例如没有线性分布限制,决策树根据不同特征属性判断,将具有不同属性数据划分到不同子节点下,对样本落到不同子节点下求平均值,产生回归的预测结果,按照少数服从多数产生分类结果,依据相同或者相似的输入产生相同相似的输出,同因同果。根据13个特征连续值去预测房价连续值,13个特征值对房价的影响程度通过决策树算法整出来后可视化呈现。

  • 数据集介绍

    该数据集为一个开放房价数据集,包含506笔样本,每个样本包含13个特征和1个标签(即输出值房价),具体如下所示:img

  • 代码实现

# 决策树回归示例
# 使用决策树预测波士顿房价
​
import sklearn.datasets as sd
import sklearn.utils as su
import sklearn.tree as st
import sklearn.ensemble as se
import sklearn.metrics as sm
​
​
boston = sd.load_boston()  # 加载boston地区房价数据
print(boston.feature_names)
print(boston.data.shape)
print(boston.target.shape)
​
random_seed = 7  # 随机种子,计算随机值,相同的随机种子得到的随机值一样
x, y = su.shuffle(boston.data, 
                  boston.target, 
                  random_state = random_seed)
# 计算训练数据的数量
train_size = int(len(x) * 0.8) # 以boston.data中80%的数据作为训练数据
# 构建训练数据、测试数据
train_x = x[:train_size]  # 训练输入, x前面80%的数据
test_x = x[train_size:]   # 测试输入, x后面20%的数据
train_y = y[:train_size]  # 训练输出
test_y = y[train_size:]   # 测试输出
​
######## 单棵树进行预测 ########
# 模型
model = st.DecisionTreeRegressor(max_depth=4)  # 决策回归器
​
# 训练
model.fit(train_x, train_y)
# 预测
pre_test_y = model.predict(test_x)
# 打印预测输出和实际输出的R2值
print(sm.r2_score(test_y, pre_test_y))

  • 执行结果

['CRIM' 'ZN' 'INDUS' 'CHAS' 'NOX' 'RM' 'AGE' 'DIS' 'RAD' 'TAX' 'PTRATIO'
 'B' 'LSTAT']
(506, 13)
(506,)
0.8202560889408634 决策树模型属于中庸模型,好不到哪里,差不到哪里

  • 特征重要性

作为决策树模型训练过程中的副产品,根据每个特征划分子表前后信息熵减少量就标志了该特征的重要程度,此即为该特征重要性的指标。训练后得到的模型对象提供了属性feature_importances_来存储每个特征的重要性。在工程应用上,可以对决策树做一些优化,不必让每一个特征都参与子表划分,而只选择其中较重要的(或者说影响因素较大的)的特征作为子表划分依据。特征重要性的评价指标,就是根据该特征划分子表后所带来的信息熵减少量,熵减越大的就越重要,也就越优先参与子表的划分。

在上述示例中加入如下代码:

import matplotlib.pyplot as mp
import numpy as np
fi = model.feature_importances_  # 获取特征重要性
print("fi:", fi)
​
# 特征重要性可视化
mp.figure("Feature importances", facecolor="lightgray")
mp.plot()
mp.title("DT Feature", fontsize=16)
mp.ylabel("Feature importances", fontsize=14)
mp.grid(linestyle=":", axis=1)
x = np.arange(fi.size)
sorted_idx = fi.argsort()[::-1]  # 重要性排序(倒序)
fi = fi[sorted_idx]  # 根据排序索引重新排特征值
mp.xticks(x, boston.feature_names[sorted_idx])
mp.bar(x, fi, 0.4, color="dodgerblue", label="DT Feature importances")
​
mp.legend()
mp.tight_layout()
mp.show()

执行结果:img

5. 决策树的剪枝

剪枝(减少决策树层次)是决策树学习算法对付“过拟合”的主要手段. 在决策树学习中,为了尽可能正确分类训练样本,节点划分过程将不断重复,有时会造成决策树分支过多,这时就可能因训练样本学的“太好了”,以至于把训练集本身的一些特点当做数据所具有的一般性质而导致过拟合. 因此,可通过主动去掉一些分支来降低过拟合风险.

(1)预剪枝(提前将一些没必要的子节点划去). 决策树生成过程中,对每个节点在划分前进行评估,若当前节点不能带来决策树泛化性能的提升,则停止划分并将当前节点标记为叶子节点.

(2)后剪枝(创建一个完整的树后一层一层减少子节点). 先训练为一颗完整的决策树,然后自低向上对非叶子节点进行考察,若将该节点对应的子树替换为叶节点能带来决策树泛化能力提升,则将该子树替换为叶节点.

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Gausst松鼠会/article/detail/333285
推荐阅读
相关标签
  

闽ICP备14008679号