刷题日记:面试经典 150 题 DAY3

本文解析了编程面试中的五个经典问题,涉及H指数计算、数组乘积、O(1)时间复杂度操作、数据结构设计(如哈希表实现)以及两个字符串问题(加油站和分发糖果)。通过实例代码展示了解决这些问题的思路和算法实现。
摘要由CSDN通过智能技术生成

274. H 指数

原题链接 274. H 指数

重要的是都明白H指数到底是是个啥。注意到如果将引用数从大到小排序,则对于下标i有 引用数 ≥ c i t a t i o n [ i ] 的论文有 i + 1 篇 引用数\geq citation[i]的论文有i+1篇 引用数citation[i]的论文有i+1,注意到此时引用数递减而文章数量递增,所求H指数就是求一个交点。

class Solution {
public:
    int hIndex(vector<int>& citations) {
        sort(citations.begin(), citations.end(),greater<int>());
        int i;
        for(i = 0;i<citations.size() && citations[i]>=(i+1);i++);
        return i;
    }
};

其实时间复杂度为 O ( N ) O(N) O(N)的算法更加符合直觉。我们创建一个新的数组count来记录,count[i]中存储引用数为i的文章有几篇,这里面重要的一步是将引用数大于等于n的篇数存储到count[n] 之中,这是因为对于本题来说,h指数不可能大于n,所以大于n篇数的分别计数就没有意义(该思想经常出现)

class Solution {
public:
    int hIndex(vector<int>& citations) {
        int n = citations.size();
        int count[n+1];
        memset(count,0,sizeof(count));
        for(int cite:citations) {
            if(cite >= n) {
                count[n]++;
            } else {
                count[cite]++;
            }
        }

        int sum = 0,i = 0;
        for(i = n; i>=0;i--) {
            sum += count[i];
            if(sum >= i) {
                break;
            }
        }
        return i;
    }
};

238. 除自身以外数组的乘积

原题链接 238. 除自身以外数组的乘积

题很简单,只需要注意到要处理0,可以分类讨论

  • 不存在0,则只需要算出整体乘积,然后除以当前数字
  • 存在一个0,则除了这个0外剩下位置都是0,该位置是所有其它数乘积
  • 存在两个及以上0,则全是0
class Solution {
public:
    vector<int> productExceptSelf(vector<int>& nums) {
        int count_0 = 0;
        int len = nums.size();
        int total = 1;
        for(int num:nums) {
            if(num == 0) count_0++;
            else total*=num;
        }

        vector<int> result(len,0);
        if(count_0 >= 2) {
            return result;
        }
        if(count_0 == 1) {
            for(int i = 0;i < len;i++) {
                if(nums[i] == 0) {
                    result[i] = total;
                }
            }
            return result;
        }
        for(int i = 0;i < len;i++) {
            result[i] = total/nums[i];
        }
        return result;
    }
};

380. O(1) 时间插入、删除和获取随机元素

原题链接 380. O(1) 时间插入、删除和获取随机元素

数据结构设计题,之前没试过这类题,自己做想到要用哈希(废话),但是感觉脑子不够用,遂去狠狠的学习了【宫水三叶】数据结构运用题

class RandomizedSet {
private:
    unordered_map<int,int> hashmap;
    int nums[200010];
    int idx;
public:
    RandomizedSet() {
        srand((unsigned)time(NULL));
        idx = -1;
    }
    
    bool insert(int val) {
        if(hashmap.count(val) > 0) {
            return false;
        }
        nums[++idx]  = val;
        hashmap[val] = idx;
        return true;
    }
    
    bool remove(int val) {
        if(hashmap.count(val) == 0) {
            return false;
        }
        int tail = nums[idx];
        int rm_idx = hashmap[val];
        swap(nums[rm_idx],nums[idx]);

        hashmap.erase(val);
        if(val != tail) hashmap[tail] = rm_idx;
        idx--;
        return true;
    }
    
    int getRandom() {
        int random_i = rand() % (idx+1);
        return nums[random_i];
    }
};

/**
 * Your RandomizedSet object will be instantiated and called as such:
 * RandomizedSet* obj = new RandomizedSet();
 * bool param_1 = obj->insert(val);
 * bool param_2 = obj->remove(val);
 * int param_3 = obj->getRandom();
 */

结构:

  • 一个定长数组用于存储元素,维护一个idx,表示在数组的[0...idx]中存储着元素
  • 一个哈希表,存储键值对(val,idx),即元素和其所在的下标

插入

  • 直接向哈希表和数组的末尾插入

删除

  • 对哈希表的操作:直接删除其键值对
  • 对数组的操作:将要删除的量交换到最后,然后让idx减去1(经常用到),此时需要再更新哈希表。将key为原数组尾的值改成交换后的下标

134. 加油站

原题链接 134. 加油站

朴素的想法是,我只需要模拟开车的过程,并一个一个尝试哪一个加油站可以作为起点就可以。但是这个方法复杂度有 O ( N 2 ) O(N^2) O(N2)
能在 O ( N ) O(N) O(N)内做出来的方法基于以下重要观察:

  • 如果从 i i i出发能到达 j j j而无法到达 j + 1 j+1 j+1,则从任何的 k ∈ [ i , j ] k \in[i,j] k[i,j]出发都不可能到达 j + 1 j+1 j+1,也就是任何的 k ∈ [ i , j ] k \in [i,j] k[i,j]都不可能作为起点,所以我们直接从 j + 1 j+1 j+1开始检查即可
    • 这是因为到达 k k k时汽车至少有非负的汽油,一定不低于从 k k k出发的汽油

所以我们能在每个元素至多被遍历两遍内获得答案

class Solution {
public:
    int canCompleteCircuit(vector<int>& gas, vector<int>& cost) {
        int len = gas.size();
        int i = 0;
        while(i < len) {
            int cargas = 0;
            int cnt;
            for(cnt = 0;cnt < len;cnt++) {
                int j = (i+cnt)%len;
                cargas += gas[j]-cost[j];
                if(cargas < 0) {
                    break;
                }
            }
            if(cnt == len) {
                return i;
            } else {
                i += cnt+1;
            }
        }
        return -1;
    }
};

135. 分发糖果

原题链接 135. 分发糖果

第一遍做想到一个比较符合直觉的想法:考虑两种打分的分布,分别是:递增,递减

在这里插入图片描述

这样分糖果的策略很简单,就是在单调列中,这个孩子是打分第几低,我就给他几个糖果
实际打分的分布可以看作是多个递增递减列的组合

在这里插入图片描述

此时仅需要考虑中间那个孩子需要多少糖果即可,很简单能想到应该是max(left,right)
最后写成代码:

class Solution {
public:
    int candy(vector<int>& ratings) {
        int len = ratings.size();
        vector<int> uplift(len,0);
        vector<int> downlift(len,0);
        int cd = 0;
        for(int i = 1;i < len;i++){
            if(ratings[i]>ratings[i-1]) {
                cd++;
            } else  {
                cd = 0;
            }
            uplift[i] = cd;
        }
        cd = 0;
        for(int i = len-2;i >= 0;i--){
            if(ratings[i]>ratings[i+1]) {
                cd++;
            } else {
                cd = 0;
            }
            downlift[i] = cd;
        }
        cd = len;
        for(int i = 0;i < len;i++){
            cd += max(uplift[i],downlift[i]);
        }
        return cd;
    }
};
  • 7
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值