【面试150题】

双指针26、删除有序数组中的重复项I

在这里插入图片描述
我的思路:指针遍历,如果相同则原地erase,继续遍历

int removeDuplicates(vector<int>& nums) {
        vector<int>::iterator it=nums.begin()+1;
        while (it!=nums.end()){
            if (*it == *(it-1)){
                nums.erase(it);
            }
            else it+=1;
        }
        return nums.size();
    }

双指针:一快一慢遍历数组,快指针所指的值若非慢指针所指的值,则慢指针后移一位,将此值加入

int removeDuplicates(vector<int>& nums) {
        int p=0, q=1;
        while (q!=nums.size()){
            if (nums[q++]!=nums[p]){
                nums[++p] = nums[q-1];
            }
        }
        return p+1;
    }

双指针80、删除有序数组中的重复项II

在这里插入图片描述
在这里插入图片描述
我的思路:指针遍历,如果连续三个相同则原地erase,继续遍历

int removeDuplicates(vector<int>& nums) {
        if (nums.size()<3) return nums.size();
        vector<int>::iterator it=nums.begin()+2;
        while (it!=nums.end()){
            if (*it == *(it-1) && *it == *(it-2)){
                nums.erase(it);
            }
            else it += 1;
        }
        return nums.size();
    }

双指针:一快一慢遍历数组,快指针所指的值若非慢指针-2所指的值,则慢指针后移一位,将此值加入

int removeDuplicates(vector<int>& nums) {
        int n=nums.size(), slow=2, fast=2;
        if (n<3) return n;
        while (fast != n){
            if (nums[fast]!=nums[slow-2]) {
                nums[slow] = nums[fast];
                slow += 1;
            }
            fast += 1;
        }
        return slow;
    }

双向遍历135、分发糖果

在这里插入图片描述
在这里插入图片描述
思路:初始化全部分发1个糖果,从左向右遍历:若比左邻大则更新糖果数量;从右向左遍历:若比右邻大且糖果数小于等于其糖果数,则更新糖果数量。

 int candy(vector<int>& ratings) {
        int n=ratings.size();
        //初始化
        int res[n], sum=0;
        for (int i=0;i<n;i++) res[i] = 1;
        //从左侧扫一次,判断是否多
        for (int i=1;i<n;i++){
            if (ratings[i]>ratings[i-1]) 
                res[i]=res[i-1]+1;
        }
        //从右侧扫一次,判断是否多
        for (int i=n-2;i>-1;i--){
            if (ratings[i]>ratings[i+1] && res[i]<=res[i+1])
                res[i]=res[i+1]+1;
        }
        //记录总数
        for (int i=0;i<n;i++) sum+=res[i];
        return sum;
    }

贪心55、跳跃游戏I

在这里插入图片描述我的思路:遍历时更新可达到的最远位置,直到边界或无法扩充

bool canJump(vector<int>& nums) {
        int n = nums.size();
        int now = 0, maxindex = nums[now];
        while (maxindex<n && now<maxindex){ //没访问到n-1并且指针可以继续前进
            now += 1;
            maxindex = max(maxindex, now+nums[now]);
        }
        if (maxindex >= n-1) return true; 
        return false;
    }

贪心45、跳跃游戏II

在这里插入图片描述
在这里插入图片描述
思路1:遍历时更新可跳范围的最小跳跃次数

int jump(vector<int>& nums) {
        int n = nums.size();
        int dp[n];
        int now=0;

        for (int i=0;i<n;i++) dp[i]=1e5;
        dp[0] = 0;
        while (now<n){ //遍历数组
            for (int i=now+1;i<=now+nums[now]&&i<n;i++){
                dp[i] = min(dp[i], dp[now]+1); //取最小跳跃次数
            }
            now += 1;
        }
        return dp[n-1];
    }

思路2:每次跳跃有一个范围,边更新范围边增加最少跳跃次数

int jump(vector<int>& nums) {
        int n = nums.size();
        if (n<2) return 0; //不需要跳
        int now=0, right=0, cnt=0, maxindex=0;
        while (now<n-1){ //遍历数组
            maxindex = max(maxindex, now+nums[now]);
            if (now == right){ //走到上次的边界了
                right = maxindex;
                cnt += 1;
            }
            now += 1;
        }
        return cnt;
    }

数学题274、H指数I

在这里插入图片描述

数学题275、H指数II

在这里插入图片描述
我的思路:非降序数组,比较大于等于该因子的论文数量和该因子的大小,取较小的数作为当前索引的h值,遍历取最大即可

int hIndex(vector<int>& citations) {
        sort(citations.begin(), citations.end()); //有序则不用sort
        int n = citations.size(), h=0;
        for (int i=0;i<n;i++){
            h = max(min(n-i, citations[i]), h);
        }
        return h;
    }

数学题238、除自身以外数组的乘积

在这里插入图片描述
我的思路:构造前缀乘积和后缀乘积列表,最后用一次遍历得到答案

vector<int> productExceptSelf(vector<int>& nums) {
        int n = nums.size();
        int left[n], right[n];
        left[0] = right[n-1] = 1;
        vector<int> res;
        for (int i=1;i<n;i++){ //构造前缀连乘
            left[i] = left[i-1]*nums[i-1];
        }
        for (int j=n-2;j>-1;j--){ //构造后缀连乘
            right[j] = right[j+1]*nums[j+1]; 
        }
        for (int i=0;i<n;i++){
            res.push_back(left[i]*right[i]);
        }
        return res;
    }

O(1)空间复杂度的思路:仅构造前缀乘积,后缀乘积用一个变量代替

vector<int> productExceptSelf(vector<int>& nums) {
        int n = nums.size();
        int right = 1;
        vector<int> res;
        res.push_back(1);
        for (int i=1;i<n;i++){ //构造前缀连乘
            res.push_back(res[i-1]*nums[i-1]);
        }
        for (int j=n-2;j>-1;j--){ //构造后缀连乘
            right *= nums[j+1];
            res[j] = res[j]*right;
            
        }
        return res;
    }

数学题134、加油站

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
我的思路:构造净收入数组,将每个元素作为起始位置进行求和&判断,时间复杂度O(n^2)

    int canCompleteCircuit(vector<int>& gas, vector<int>& cost) {
        int n = gas.size();
        int gain[n], sum=0, positive=-1;
        for (int i=0;i<n;i++){ //计算纯收入
            gain[i] = gas[i]-cost[i];
            sum += gain[i];
            if (gain[i]>0 && positive==-1) positive=i;
        }
        if (sum<0) return -1; //没办法跑完一圈
        if (positive==-1) return 0; //全部元素都是0
        else{
            int i, j;
            for (i=positive;i<n;i++){
                sum=gain[i];
                for (j=i+1;j!=n+i&&sum>=0;j++){
                    sum += gain[j%n];
                }
                if (j==n+i && sum>=0) return i; 
            }
            return 0;
        }
    }

O(n)思路:假如当前一段路无法走下去了,就该放弃 换个新的起点了——这个起点最多只能到这,从这段路的任何地方重新开始都到达不了更远的地方。因为放弃前走的路都是帮你的,不然早就无法走下去了。因此 起点只能选在下一站,错的不是你,是你的起点和路。

  int canCompleteCircuit(vector<int>& gas, vector<int>& cost) {
        int n = gas.size();
        int now=0;
        while (now<n){ //遍历搜索起始位置
            int sum = gas[now]-cost[now]; //初始存油量
            if (sum>=0){
                int i;
                for (i=now+1;i<n+now&&sum>=0;i++){
                    sum += gas[i%n]-cost[i%n]; //继续向前跑
                }
                if (sum<0) now=i; //用下个位置作为起始
                else return now;
            }
            else now += 1;
        }
        return -1;
    }

数学题189、轮转数组

在这里插入图片描述
我的思路:利用取余操作和另外一个数组空间

void rotate(vector<int>& nums, int k) {
        vector<int> res=nums;
        int n=nums.size();
        for (int i=0;i<n;i++){
            res[(i+k)%n] = nums[i];
        }
        nums = res;
    }

方法二:多个环内部移位
在这里插入图片描述
设总共有a个圈,每个过程遍历b个元素,因此an=bk,即an一定为n,k的公倍数;
又因为在第一次回到起点时就结束,所以a要尽可能小,故an就是n,k的最小公倍数;
a=lcm(n,k),b=lcm(n,k)/k,这说明单次遍历会访问到 lcm(n,k)/k个元素;
为了访问到所有的元素,需要进行遍历的次数为
在这里插入图片描述

void rotate(vector<int>& nums, int k) {
        int n=nums.size();
        k=k%n;
        int cnt = gcd(k, n), now, t, next;
        for (int i=0;i<cnt;i++){
            now = i;
            t = nums[i];
            do{ //还没有到环的结尾
                next = (now+k)%n;
                swap(nums[next], t);
                now = next;
            }while (now!=i);
        }
    }

方法三:内部翻转
在这里插入图片描述

    void reverse(vector<int>& nums, int start, int end) {
        while (start < end) {
            swap(nums[start], nums[end]);
            start += 1;
            end -= 1;
        }
    }
    void rotate(vector<int>& nums, int k) {
        k %= nums.size();
        reverse(nums, 0, nums.size() - 1);
        reverse(nums, 0, k - 1);
        reverse(nums, k, nums.size() - 1);
    }

贪心121、买卖股票的最佳时机I

在这里插入图片描述
我的思路:扫描一遍,找当前最小的股票价格和最大的价格差

int maxProfit(vector<int>& prices) {
        int minprice=prices[0], maxprice=0, n=prices.size();
        for (int i=1;i<n;i++){
            maxprice = max(maxprice, prices[i]-minprice); //最大价格差值
            minprice = min(minprice, prices[i]); //最小的股票价格
        }
        return maxprice;
    }

动态规划122、买卖股票的最佳时机II

在这里插入图片描述
在这里插入图片描述
思路:dp0存储当前不持股的最大利润,dp1存储当前持股的最大利润
初始化条件:dp0[0] = 0, dp1[0]=-prices[0];
迭代方程:
(1)手中不持股:要么卖了之前的,要么一直没持股
dp0[i] = max(dp1[i-1]+prices[i], dp0[i-1])
(2)手中持有第i股:要么卖了之前的换今天的,要么一直没持股今天买
dp1[i] = max(dp1[i-1]+prices[i]-prices[i], dp0[i-1]-prices[i])

int maxProfit(vector<int>& prices) {
        int n = prices.size();
        int dp0[n], dp1[n];
        dp0[0] = 0;
        dp1[0] = -prices[0];
        for (int i=1;i<n;i++){
            dp0[i] = max(dp1[i-1]+prices[i], dp0[i-1]); //不持:要么当天卖,要么就没持股
            dp1[i] = max(dp1[i-1]+prices[i]-prices[i], dp0[i-1]-prices[i]); //持第i股:之前没持股 当天买;或者卖了再买
        }
        return dp0[n-1];
    }

动态规划198、打家劫舍

在这里插入图片描述
我的思路:dp0存储不拿当前房屋的最大价值,dp1存储拿当前房屋现金的最大价值
初始化条件:dp0[0] = 0, dp1[0]=nums[i];
迭代方程:
(1)当前房屋不拿:之前没拿和之前拿了,取最大值
dp0[i] = max(dp0[i-1], dp1[i-1])
(2)要拿当前房屋的,之前房屋的现金不能拿:
dp1[i] = dp0[i-1]+nums[i]

int rob(vector<int>& nums) {
        int n = nums.size();
        int dp0[n], dp1[n];
        dp0[0] = 0;
        dp1[0] = nums[0];
        for (int i=1;i<n;i++){
            dp0[i] = max(dp0[i-1], dp1[i-1]); //不拿:取之前最多的现金总和
            dp1[i] = dp0[i-1]+nums[i];
        }
        return max(dp0[n-1], dp1[n-1]);
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值