面试题复习(0902-0909)

1. 完全背包问题

和01背包唯一的区别是,每件物品都有无限个(也就是可以放入背包多次)

代码和01唯一的区别在于j的循环是从小到大,不是从大到小。ij谁在外谁在内层区别不大。

#include <bits/stdc++.h>
using namespace std;

int main(){
    int N,V;
    cin>>N>>V;
    vector<int> values(N), weights(N);
    for(int i =0;i<N;i++){
        cin>>weights[i]>>values[i];
    }
    vector<int> dp(V+1);
   for(int j=0;j<=V;j++ ){
    for(int i =0;i<N;i++)
        {
            if(j-weights[i]>=0)
            dp[j] = max(dp[j], dp[j-weights[i]]+values[i]);
        }
    }
    cout<<dp[V]<<endl;
    return 0;
}

2. 零钱兑换二

    int change(int amount, vector<int>& coins) {
        vector<int> dp(amount+1,0);
        dp[0] = 1; // 当总金额为0时,有一种组合方法就是0
        int n = coins.size();
        for(int i=0;i<n;i++){
            for(int j =coins[i];j<=amount;j++){
                dp[j] += dp[j-coins[i]];
            }
        }
        for(int i = 0;i<amount+1;i++){
            cout<<dp[i]<<endl;
        }
        return dp[amount];
    }
};

组合数:要进行初始化,并且知道每一种组合等于把所有硬币遍历后的上一种组合加起来。(不用再加一)

时间复杂度:amount*coins的数量

j从coins[i]开始!其实从0开始也行,但是就要加个判断j大于coins[i]才能做呀,不然就是负数了

注意i,j的遍历顺序与求排列数还是组合数有关:

如果求组合数就是外层for循环遍历物品,内层for遍历背包

如果求排列数就是外层for遍历背包,内层for循环遍历物品

3. 零钱兑换

与上一题的区别在于dp不是表示组合数个数,而是表示凑足总额为j所需钱币的最少个数为

就是从+= 变成了min?

这里就要+1了,因为每次遍历多加了一个硬币,值就要+1

    int coinChange(vector<int>& coins, int amount) {
        vector<int> dp(amount+1,INT_MAX);
        dp[0] = 0; // 当总金额为0时,有一种组合方法就是0
        int n = coins.size();
        for(int i=0;i<n;i++){
            for(int j =coins[i];j<=amount;j++){
                if(dp[j-coins[i]] != INT_MAX)
                    dp[j] =min(dp[j], dp[j-coins[i]]+1);
            }
        }
        if(dp[amount] == INT_MAX) return -1;
        return dp[amount];
    }

4. TOPK数的快排做法

注意这个topk指的是从大到小排序后的第k个!所以实际上并不是真正的想象中从小到大排序后的第k个!可以直接转换成第n-k+1大的数就行

    int quick_sort(vector<int>& nums,int left, int right,int k){
        if(left >= right) return nums[left];
        int i = left-1, j = right+1;
        int mid = (left+right)/2;
        int x = nums[mid];
        while(i <j){
            do i++; while(nums[i] < x);
            do j--; while(nums[j] > x);
            if(i<j) swap(nums[i],nums[j]);
        }
        if(j-left+1 >= k) return quick_sort(nums,left,j,k);
        else return quick_sort(nums,j+1,right,k-(j-left+1));
    }
    int findKthLargest(vector<int>& nums, int k) {
        int res =  quick_sort(nums,0,nums.size()-1, nums.size()-k+1);
        return res;
    }

代码和快排差不多,但是要想明白,每次是找哪边继续索引。如果排完序后的长度比k大,说明k在左边,就往左边回溯,如果比k小,就回溯右边,注意回溯右边的时候就不是找第k大了,而是k减去左边区间的长度

为什么时间复杂度是On啊??

还是没懂,反正就是n,快速排序的复杂度是nlogn

4. SVM的核函数和高阶是具体如何实现的?

其中,线性核适合线性可分的数据,多项式核适合非线性模型,高斯核适合那种不清楚数据分布的非线性情况。

映射到高维空间不是说把每个样本做映射,而是在计算距离的时候,用高斯核计算两个x之间的距离。

5. GBDT树

梯度提升决策树。

树的流程从 决策树-》随机森林-》梯度提升决策树->Xgboost, 随机森林是bagging,GBDT和xgb都是boosting。

梯度提升是一种集成学习的一种方法,决策树是基学习器。为啥要这两个结合呢,是因为决策树是if,else的,速度快,适合非线性关系,不需要对输入数据做标准化,特征工程少,也可以处理特征缺失的情况。决策树能自动组合多个特征。

但是,决策树的缺点就是过拟合。抑制单颗树的复杂程度,再通过梯度提升方法继承多个决策树,就能达到平衡。

抑制复杂度的方法有:(1)树的最大深度(2)限制叶子结点的最少样本数量,有的特征缺失太多了就不放了(3)限制节点分裂时的最少样本数量(4)对训练样本采样,单个决策树学习时只使用一部分训练样本,这是bagging的思想(5)随机森林的思想,学习单颗决策树时只使用一部分特征(6)在目标函数中添加正则项惩罚

使用最小化损失函数(如均方误差或对数损失)来优化模型性能。每个弱学习器的训练都是在前一轮模型的残差(即预测值与真实值之间的差)基础上进行的。

具体优化方式: 前向分布算法

GBDT 算法可以看成是由 K 棵树组成的加法模型:

使用前向分布算法,每一步只学习一个基学习器,逐步逼近优化目标函数。

在第t步的目标函数为:

注意!每一颗树只拟合上一棵树的结果与目标之间的残差,不是去拟合目标

6. 数组中有个数的数量超过一半,如何最快找出这个数?

这是一种抵消的思路,就是如果两个数不同就会抵消为0,如果这个数超过了一半,说明它与其他数不同的时候被抵消完了以后,数量还是会>0,这样On就找出来了

或者是排序求中间值,这么看似乎是可以用快速选择的?只要找到一个数,刚好在中间位置,说明他就是众数。

还有一种方法,就是利用哈希map。

7. 每日温度

好难好难好难。。实际上是一种栈的方法,如果这个数比上一个数小就压栈,大就可以取出栈中的元素,并计算坐标之差

只要大,就把小的都取出来。我这里用了两个栈,分别存索引和值,实际上只存索引就行,值从数组里取。

    vector<int> dailyTemperatures(vector<int>& temperatures) {
        stack<int> idxs, values;
        int cur=0;
        vector<int> res(temperatures.size(),0);
        for(int i = 0;i<temperatures.size();i++){
            while (!idxs.empty() && values.top() < temperatures[i]){
                res[idxs.top()] = i-idxs.top();
                values.pop();
                idxs.pop();
            }
            idxs.push(i);
            values.push(temperatures[i]);

        }
        return res;
    }

8. 二叉树展开为链表

就是判断一下,没啥好说的 

    TreeNode * findr(TreeNode * cur){
        while(cur->right != nullptr){
            cur =  cur->right;
        }
        return cur;
    }
    void flatten(TreeNode* root) {
        TreeNode * cur = root;
        while(cur!= nullptr){
            if(cur->left != nullptr){
                TreeNode * r = cur->right;
                cur->right = cur->left;
                TreeNode * nextr = findr(cur);
                nextr->right = r;
                cur->left = nullptr;
            }
            cur = cur->right;
        }
        return ;
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值