• 牛客题目——买卖股票的最好时机(一)、(二)、(三)、设计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;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    题目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;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    题目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]));
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    //时间复杂度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;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    题目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);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    //实现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++;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
  • 相关阅读:
    广州虚拟动力数字人实时驱动解决方案,赋能虚拟IP、虚拟直播、品牌发布会...
    安全加固是什么?服务流程是怎么样的?
    91 多边形的面积
    计算机二级C语言经典资料汇总
    架构师必须了解的 5 种最佳软件架构模式
    Dubbo入门实例
    如何在Microsoft Exchange 2013上安装https证书
    Oracle 用户 profile 属性
    实现微服务:匹配系统(中)
    FastGpt流程
  • 原文地址:https://blog.csdn.net/zhangzhang_one/article/details/126171176