牛客题目——买卖股票的最好时机(一)、(二)、(三)、设计LRU缓存结构


题目1——买卖股票的最好时机(一)

假设你有一个数组prices,长度为n,其中prices[i]是股票在第i天的价格,请根据这个价格数组,返回买卖股票能获得的最大收益。

  1. 你可以买入一次股票和卖出一次股票,并非每天都可以买入或卖出一次,总工只能买入和卖出一次,且买入必须在卖出的前面的某一天。
  2. 如果不能获取到任何利润,请返回0。
  3. 假设买入卖出均无手续费。

要求:空间复杂度O(1),时间复杂度O(n)。
示例
输入:[8,9,2,5,4,7,1]
输出:5(第3天的时候买入,第6天的时候卖出)

解题思路

想要利润最大化,肯定会选股票价格最低的那天买入,然后在选择之后价格最高的那天卖出。
所以我们可以循环遍历数组,找到最低价格,并且遍历过程中更新最大利润。

代码实现

import java.util.*;
public class Solution {
    public int maxProfit (int[] prices) {
        int max = 0;
        int a = prices[0];
        int b = 0;
        for(int i=1;i<prices.length;i++){
            b = prices[i];
            if(b-a > 0){
                max = Math.max(b-a,max);
            }
            else
                a = b;
        }
        return max;
    }
}

题目2——买卖股票的最好时机(二)

假设你有一个数组prices,长度为n,其中prices[i]是某只股票在第i天的价格,请根据这个价格数组,返回买卖股票能获得的最大收益。

  1. 你可以多次买卖该只股票,但是再次购买前必须卖出之前的股票。
  2. 如果不能获取到任何利润,请返回0。
  3. 假设买入卖出均无手续费。

要求:空间复杂度O(n),进阶为O(1),时间复杂度O(n)。
示例
输入:[8,9,2,5,4,7,1]
输出:7(第1天买入,第2天卖出,第3天买入,第4天卖出,第5天买入,第6天卖出,总获利1+3+3=7)

解题思路

使用贪心思想,找出整体当中给的每个局部子结构的最优解,并且将所有这些局部最优解结合起来形成整体上的一个最优解,只要数组中后一天价格比前一天价格更大,就可以有收益,将收益累加,就能获得最终结果。

代码实现

import java.util.*;
public class Solution {
    public int maxProfit (int[] prices) {
        int max = 0;
        int a = prices[0];
        int b;
        for(int i=1;i<prices.length;i++){
            b = prices[i];
            if(b > a){
                max += (b-a);
                a = prices[i];
            }
            else a = b;
        }
        return max;
    }
}

题目2——买卖股票的最好时机(三)

假设你有一个数组prices,长度为n,其中prices[i]是某只股票在第i天的价格,请根据这个价格数组,返回买卖股票能获得的最大收益。

  1. 你最多可以对该股票有两笔交易操作,一笔交易代表着一次买入与一次卖出,但是再次购买之前必须卖出之前的股票。
  2. 如果不能获取到任何利润,请返回0。
  3. 假设买入卖出均无手续费。

要求:空间复杂度O(n),进阶为O(1),时间复杂度O(n)。
示例
输入:[8,9,3,5,1,3]
输出:4(第三天买入,第四天卖出,第五天买入,第六天卖出,获收益为2+2=4)

解题思路

动态规划,每天的持股情况可以分为5种,两次都没交易,买一次还没卖出,交易一次,买两次卖一次,交易两次,所以我们可以相应地写出状态转移方程。
时间复杂度O(n),空间复杂度O(n)。

代码实现

//动态规划
import java.util.*;
public class Solution {
    public int maxProfit (int[] prices) {
        int n = prices.length;
        //dp[i][0]表示到第i天为止没有买卖过股票的最大收益
        //dp[i][1]表示到第i天为止买过一次股票还没有卖出的最大收益
        //dp[i][2]表示到第i为止买卖过一次股票的最大收益
        //dp[i][3]表示到第i天为止买了两次卖出一只的最大收益
        //dp[i][4]表示到第i天为止买卖了两次股票的最大收益
        int[][] dp = new int[n][5];
        Arrays.fill(dp[0],-10000);
        dp[0][0] = 0;
        dp[0][1] = -prices[0];
        for(int i=1;i<n;i++){
            dp[i][0] = dp[i-1][0];
            dp[i][1] = Math.max(dp[i-1][1],dp[i-1][0]-prices[i]);
            dp[i][2] = Math.max(dp[i-1][2],dp[i-1][1]+prices[i]);
            dp[i][3] = Math.max(dp[i-1][3],dp[i-1][2]-prices[i]);
            dp[i][4] = Math.max(dp[i-1][4],dp[i-1][3]+prices[i]);
        }
        return Math.max(dp[n-1][2],Math.max(0,dp[n-1][4]));
    }
}
//时间复杂度O(n),空间复杂度O(1)
import java.util.*;
public class Solution {
    public int maxProfit (int[] prices) {
        int n = prices.length;
        int buy1 = -prices[0]; //第一次购买股票
        int sell1 = 0; //第一次交易最大收益
        int buy2 = -prices[0];//第一次交易完成后第二次购买股票
        int sell2 = 0; //第二次交易最大收益
        for(int i=1;i<n;i++){
            buy1 = Math.max(buy1,-prices[i]);
            sell1 = Math.max(sell1,buy1+prices[i]);
            buy2 = Math.max(buy2,sell1-prices[i]);
            sell2 = Math.max(sell2,buy2+prices[i]);
        }
        return sell2;
    }
}

题目4——设计LRU缓存结构

设计LRU(最近最少使用)缓存结构,该结构在构造时确定大小,假设大小为capacity,操作次数是n,并有如下功能:

  1. Solution(int capactiy):以正整数作为容量capacity初始化LRU缓存。
  2. get(key):如果关键字key存在于缓存中,则返回key对应的value值,否则返回-1。
  3. set(key,value):将记录(key,value)插入该结构,如果关键字key已经存在,则变更其数据值value,如果不存在,则向缓存中插入该组key-value,如果key-value的数量超过capacity,弹出最久未使用的key-value。

示例
输入:[“set”,“set”,“get”,“set”,“get”,“set”,“get”,“get”,“get”],[[1,1],[2,2],[1],[3,3],[2],[4,4],[1],[3],[4]],2
输出:[“null”,“null”,“1”,“null”,“-1”,“null”,“-1”,“3”,“4”] (set操作会返回null)

解题思路

用LinkedHashMap来实现,LinkedHashMap是HashMap的子列,但是内部还有一个双向链表维护键值对的顺序,每个键值对既位于哈希表中,也位于双向链表中,对一个键执行get/put操作后,其对应的键值会移动到链表末尾,所以末尾的是最近访问的。

也可以自己实现Node结点,然后采用pre和next指针维护键值对的顺序,那么我们应该考虑插入或者访问时,将该结点移到头节点,这样尾结点就是最久未使用的结点。

代码实现

//使用LinkedHashMap实现
import java.util.*;
public class Solution {
    Map<Integer,Integer> map;
    int capacity;
    public Solution(int capacity) {
        this.capacity = capacity;
        map = new LinkedHashMap<>(capacity);
    }

    public int get(int key) {
        Integer value = map.get(key);
        if(value != null){
            if(map.size()>1){
                map.remove(key);
                map.put(key,value);
            }
        }else{
            return -1;
        }
        return value;
    }

    public void set(int key, int value) {
         if(map.containsKey(key)){
             map.remove(key);
         }else if(map.size()>=capacity){
             Integer first = map.keySet().iterator().next();
             map.remove(first);
         }
        map.put(key,value);
    }
}
//实现Node结点
import java.util.*;
class Node{
    int key;
    int val;
    Node pre;
    Node next;
    public Node(int key,int val){
        this.key = key;
        this.val = val;
        this.pre = null;
        this.next = null;
    }
}
public class Solution {
    private int capacity;
    private Map<Integer,Node> map;
    private Node head;
    private Node tail;
    private int size;
    public Solution(int capacity) {
        this.capacity = capacity;
        map = new HashMap<>();
        this.size = 0;
    }

    public int get(int key) {
        if(!map.containsKey(key))
            return -1;
        makeRecently(key);
        return map.get(key).val;
    }
    //将最近访问过的键值对放到链表头
    public void makeRecently(int key){
         Node t = map.get(key);
        if(t != head){
            if(t == tail){
                //如果t是尾结点,那么t变为头结点,t的前一个结点成为尾结点
                tail = tail.pre;
                tail.next = null;
            }else{
                t.pre.next = t.next;
                t.next.pre = t.pre;
            }
            t.pre = null;
            t.next = head;
            head.pre = t;
            head = t;
        }
    }
    public void set(int key, int value) {
        //如果哈希表中存在key,就直接返回值,然后调整该结点为头结点
       if(map.containsKey(key)){
           map.get(key).val = value;
           makeRecently(key);
           return ;
       }
        //否则检查是否超出容量
        if(this.size == capacity){
            //超出则哈希表中移除尾结点
            map.remove(tail.key);
            //更新尾结点
            tail = tail.pre;
            tail.next = null;
            this.size--;
        }
        //没有超出,则检查头结点是否为空,若为空,则构造头节点
        if(head == null){
            head = new Node(key,value);
            tail = head;
        }//否则插入结点到最开始
        else{
            Node temp = new Node(key,value);
            head.pre = temp;
            temp.next = head;
            head = temp;
        }
        map.put(key,head);
        this.size++;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值