2021年4月15日,京东算法面试题4道

323 阅读5分钟

这是我参与11月更文挑战的第24天,活动详情查看:2021最后一次更文挑战

1.介绍逻辑回归,逻辑回归是一个分类算法,那么它是在回归什么呢?

逻辑回归是在数据服从伯努利分布的假设下,通过极大似然的方法,运用梯度下降法来求解参数,从而达到将数据二分类的目的。

逻辑回归就是一种减小预测范围,将预测值限定为[0,1]间的一种广义线性回归模型,解决的是分类问题。

2.编程题:颜色分类(leetcode 75)

思路一:单指针

对数组进行两次遍历,考虑使用单指针 ptr 进行遍历,第一次遍历中需要把所有的 0 交换到数组的头部,每交换一次,ptr 向右移动一位,直到遍历结束,此时 ptr 之前的元素都为 0;第二次遍历从 ptr 开始遍历,将所有的 1 交换到中间位置,每交换一次,ptr 向后移动一位,直到遍历结束,此时 ptr 之后(包括ptr)的元素都为2,排序完成。

代码:

class Solution:
    def sortColors(self, nums: List[int]) -> None:
        """
        Do not return anything, modify nums in-place instead.
        """
        ptr = 0
        for i in range(len(nums)):
            if nums[i] == 0:
                nums[i],nums[ptr] = nums[ptr],nums[i]
                ptr +=1
        for i in range(ptr,len(nums)):
            if nums[i] == 1:
                nums[i],nums[ptr] = nums[ptr],nums[i]
                ptr +=1
        return nums

时间复杂度:O(n),其中 nn 是数组 nums 的长度。

空间复杂度:O(1)。

思路二:双指针

相比单指针只需要一次遍历即可完成。需要指针 p0 来交换等于 0 的元素,指针 p1 来交换等于 1 的元素,需要特别注意的如下:

先判断元素是否等于 1,满足等于1 就进行交换,并将 p1 + 1,再判断是否等于0 ,如果等于 0 也相应进行交换,另外需要判断 p0 和 p1 的关系,如果满足 p0 < p1,还需要再次进行交换,完成后将 p0 和 p1 同时 +1。

代码如下:

class Solution:
    def sortColors(self, nums: List[int]) -> None:
        """
        Do not return anything, modify nums in-place instead.
        """
        p0 = p1 = 0
        for i in range(len(nums)):
            if nums[i] == 1:
                nums[i],nums[p1] = nums[p1],nums[i]
                p1 +=1
            elif nums[i] == 0:
                nums[i],nums[p0] = nums[p0],nums[i]
                if p0 < p1:
                    nums[i],nums[p1] = nums[p1],nums[i]
                p0 += 1
                p1 += 1
        return nums

时间复杂度:O(n),其中 nn 是数组 nums 的长度。

空间复杂度:O(1)。

3.GBDT了解吗?基分类器用的什么?分类时也是用的那个吗?

GBDT是梯度提升决策树,是一种基于Boosting的算法,采用以决策树为基学习器的加法模型,通过不断拟合上一个弱学习器的残差,最终实现分类或回归的模型。关键在于利用损失函数的负梯度在当前模型的值作为残差的近似值,从而拟合一个回归树。

GBDT的基分类器用的是决策树,分类时也是用的决策树。

对于分类问题:常使用指数损失函数;对于回归问题:常使用平方误差损失函数(此时,其负梯度就是通常意义的残差),对于一般损失函数来说就是残差的近似。

更多请看七月在线题库里的这题:www.julyedu.com/questions/w…

4.XGBoost相对GBDT原理上有哪些改进。

改进主要为以下方面:

  • 传统的GBDT以CART树作为基学习器,XGBoost还支持线性分类器,这个时候XGBoost相当于L1和L2正则化的逻辑斯蒂回归(分类)或者线性回归(回归);
  • 传统的GBDT在优化的时候只用到一阶导数信息,XGBoost则对代价函数进行了二阶泰勒展开,得到一阶和二阶导数;
  • XGBoost在代价函数中加入了正则项,用于控制模型的复杂度。从权衡方差偏差来看,它降低了模型的方差,使学习出来的模型更加简单,放置过拟合,这也是XGBoost优于传统GBDT的一个特性;
  • shrinkage(缩减),相当于学习速率(XGBoost中的eta)。XGBoost在进行完一次迭代时,会将叶子节点的权值乘上该系数,主要是为了削弱每棵树的影响,让后面有更大的学习空间。(GBDT也有学习速率);
  • 列抽样:XGBoost借鉴了随机森林的做法, 支持列抽样, 不仅防止过 拟合,还能减少计算;
  • 对缺失值的处理: 对于特征的值有缺失的样本,XGBoost还可以自动 学习出它的分裂方向;
  • XGBoost工具支持并行。Boosting不是一种串行的结构吗?怎么并行 的?注意XGBoost的并行不是tree粒度的并行,XGBoost也是一次迭代完才能进行下一次迭代的(第t次迭代的代价函数里包含了前面t-1次迭代的预测值)。XGBoost的并行是在特征粒度上的。我们知道,决策树的学习最耗时的一个步骤就是对特征的值进行排序(因为要确定最佳分割点),XGBoost在训练之前,预先对数据进行了排序,然后保存为block结构,后面的迭代 中重复地使用这个结构,大大减小计算量。这个block结构也使得并行成为了可能,在进行节点的分裂时,需要计算每个特征的增益,最终选增益最大的那个特征去做分裂,那么各个特征的增益计算就可以开多线程进行。