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

本文解析了五个IT技术面试中的经典问题,包括数组轮转、股票买卖最佳时机、动态规划应用、跳跃游戏等,介绍了多种算法实现和优化策略。
摘要由CSDN通过智能技术生成

189. 轮转数组

原题链接 189. 轮转数组

最容易想到的,开辟一个新数组来暂存最后结果,全部轮转完后再复制回去。

class Solution {
public:
    void rotate(vector<int>& nums, int k) {
        int len = nums.size();
        int dist[len];
        for(int i = 0;i < len;i++) {
            dist[(i+k)%len] = nums[i];
        }
        for(int i = 0;i < len;i++) {
            nums[i] = dist[i];
        }
    }
};

有两种原地轮转的方法:第一种就是使用一个变量进行暂存,不断地将nums[a]交换到nums[(a+k)%len],可以通过理论分析证明,这样做每一趟能排列 l c m ( l e n , k ) / k lcm(len,k)/k lcm(len,k)/k,共需要 g c d ( l e n , k ) gcd(len,k) gcd(len,k)
使用代码实现时,不需要考虑最大公约数啥的,只需要使用变量count来对已经遍历到的元素进行计数,每一趟的判断就是是否回到了本趟的起点

class Solution {
public:
    void rotate(vector<int>& nums, int k) {
        int len = nums.size();
        int count = 0;
        int start = 0;
        while(count < len) {
            int cur = start;
            int prev = nums[cur];
            do {
                int next = (cur+k)%len;
                int temp = prev;
                prev = nums[next];
                nums[next] = temp;
                cur = next;
                count++;
            }while(cur != start);
            start++;
        }
    }
};

通过观察,可以发现结果可以由三步翻转来进行组合

class Solution {
public:
  void rotate(vector<int> &nums, int k) {
    k %= nums.size();
    reverse(nums.begin(), nums.end());
    reverse(nums.begin(), nums.begin() + k);
    reverse(nums.begin() + k, nums.end());
  }
};

121. 买卖股票的最佳时机

原题链接 121. 买卖股票的最佳时机

第一次做的想法:在每一天,其实只要看看在当天之后股票价格最高是多少即可。
遍历两边,第一遍做辅助数组 e n d m a x [ i ] = m a x { p r i c e s [ i . . . e n d ] } endmax[i] = max\{prices[i...end]\} endmax[i]=max{prices[i...end]},从后向前遍历
第二遍用辅助数组计算在每一天能获得的最大利润,并更新整体的最大利润,从前向后遍历

class Solution {
public:
    int maxProfit(vector<int>& prices) {
        int len = prices.size();
        int endmax[len];
        int t_max = -1;
        for(int i = len-1; i>=0; i--){
            if(t_max < prices[i]) {
                t_max = prices[i];
            }
            endmax[i] = t_max;
        }
        int result = 0;
        for(int i = 0;i<len-1;i++) {
            int sold = endmax[i+1] - prices[i];
            if(sold >result) {
                result = sold;
            }
        }
        return result;
    }
};

第二遍想,其实遍历一趟就行,同时维护前缀最大值和最小值。只不过当前缀最大值所在的下标已经“落后于”当前前缀最小值时,需要强行更新最大值和最大值下标,来保证最大值至少“不落后于”最小值,符合买卖股票的逻辑

class Solution {
public:
    int maxProfit(vector<int>& prices) {
        int len = prices.size();
        int high_index = 0, low_index = 0;
        int high = -1;
        int low = prices[0];
        int result = 0;
        for(int i = 0;i < len;i++) {
            if(prices[i] > high) {
                high_index = i;
                high = prices[i];
            }
            if(prices[i] < low) {
                low_index = i;
                low = prices[i];
            }
            if(high_index > low_index) {
                result = max(result,high-low);
            } 
            if(high_index < low_index) {
                high_index = low_index;
                high = low;
            }
        }
        return result;
    }
};

122. 买卖股票的最佳时机 II

原题链接 122. 买卖股票的最佳时机 II

动态规划,状态设计 d p ( d a y = i , w h e t h e r    h o l d s = 0 / 1 ) dp(day=i, whether \;holds = 0/1) dp(day=i,whetherholds=0/1),表示在第i天,手里是否持有股票时,能获得的最大利益
状态转移

  • 手里不持有股票:要么与前一天保持一致,要么是前一天持有,今天卖掉了 d p ( i , 0 ) = m a x { d p ( i − 1 , 0 ) , d p ( i − 1 , 1 ) + p r i c e i } dp(i,0)=max\{dp(i-1,0),dp(i-1,1)+price_i\} dp(i,0)=max{dp(i1,0),dp(i1,1)+pricei}
  • 手里持有股票:要么与前一天保持一致,要么是前一天不持有,今天买了一支 d p ( i , 1 ) = m a x { d p ( i − 1 , 1 ) , d p ( i − 1 , 0 ) − p r i c e i } dp(i,1)=max\{dp(i-1,1),dp(i-1,0)-price_i\} dp(i,1)=max{dp(i1,1),dp(i1,0)pricei}

注意到每一天的状态都只与前一天有关,所以第一维可以被抛弃

class Solution {
public:
    int maxProfit(vector<int>& prices) {
        int dp[2];
        dp[0] = 0;
        dp[1] = -prices[0];
        for(int i = 1;i < prices.size();i++) {
            int dp0 = max(dp[0],dp[1]+prices[i]);
            int dp1 = max(dp[1],dp[0]-prices[i]);
            dp[0] = dp0;
            dp[1] = dp1;
        }
        return dp[0];
    }
};

55. 跳跃游戏

原题链接 55. 跳跃游戏

考虑一个范围bound,代表能跳到的最大距离,让人物在bound内一格一格跳,如果人物在抵达数组尾前先抵达了bound,则说明不能到达结尾

class Solution {
public:
    bool canJump(vector<int>& nums) {
        int bound = 0;
        int i = 0;
        do {
            bound = max(bound,i+nums[i]);
            i++;
        }while(i < nums.size() && i <= bound );
        return i >= nums.size();
    }
};

45. 跳跃游戏 II

原题链接: 45. 跳跃游戏 II

第一遍做,想到的类似dp的做法。step[i]表示跳到位置i最少需要几步,所以自然想到从前向后遍历,在位置i更新step[i+1 ... i+nums[i]],更新方式step[j] = min(step[i] + 1,step[j])

class Solution {
public:
    int jump(vector<int>& nums) {
        int len = nums.size();
        int step[len];
        memset(step,0x7f,sizeof(step));
        step[0] = 0;
        for(int i = 0; i < len;i++) {
            for(int j = i+1;j < len && j <= i+nums[i];j++) {
                step[j] = min(step[i] + 1,step[j]);
            }
        }
        return step[len-1];
    }
};

题解中有一个 O ( N ) O(N) O(N)的做法,leetcode上给出的解释我没看懂,在这里给出我的思考。先看代码

class Solution {
public:
    int jump(vector<int>& nums) {
        int len = nums.size();
        int prev_bound = 0, bound = 0;
        int step = 0;
        for(int i = 0;i < len-1;i++) {
            bound = max(bound,i+nums[i]);
            if(i == prev_bound) {
                prev_bound = bound;
                step++;
            }
        }
        return step;
    }
};

我认为可以解释为我第一遍做法的优化

  • 第一点,step数组可以被优化成一个单独的量,毕竟最后只需要step[len-1]
  • step数组的更新,变得十分简单,这基于以下观察
    • step数组是 到达位置i最少的跳跃次数
      • 其实最后step数组的形状是,几个连续的区间,区间之间相差1
      • 这些区间的分界线,其实就是在55. 跳跃游戏中我们的各个bound
    • 所以我们需要记录的只有,step,一个老bound,一个新bound,其暗含的意义其实是arr_step[prev_bound ... bound] = step

受题解【跳跃游戏 II】别想那么多,就挨着跳吧 II启发

  • 12
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值