算法-股票交易日

题目描述

这是美团的2016招聘的笔试题。


题目描述


在股市的交易日中,假设最多可进行两次买卖(即买和卖的次数均小于等于2),规则是必须一笔成交后进行另一笔(即买-卖-买-卖的顺序进行)。给出一天中的股票变化序列,请写一个程序计算一天可以获得的最大收益。请采用实践复杂度低的方法实现。

给定价格序列prices及它的长度n,请返回最大收益。保证长度小于等于500。

测试样例:

[10,22,5,75,65,80],6

返回:

87


算法思考


如果是交易一次的话, 也就是先买再卖, 考虑起来会比较简单:

股票最低点买, 最高点卖肯定最赚钱, 但是有时间序列的问题, 差值最大的买入卖出点不一定是股价的最低和最高点。

举例来说:

如果价格序列是 10 20 30 5 , 则利润最大值是20, 10 买入, 30 卖出, 最小点的价格是5, 不在最小点买入

如果价格序列是 10 20 30 5 28,则利润最大值是20, 5 买入, 28 卖出, 最大点的价格是30, 不在最大点卖出

30 10 28 5 20 ,则利润最大值是18, 10 买入, 28 卖出, 最大点的价格是30, 最小点的价格是5,不在最小点买入也不在最大点卖出。


目标是: 后面的价格减去前面价格的差值的最大值。

算法: 将所有的后面价格减去前面价格的情况都列举出来, 找一个最大值, 穷尽所有状况, 一定能找到解。


现在又增加难度, 两次交易, 每次必须先买后卖。

看起来循环的话又增加了一维。而且要控制第一次卖完之后第二次才能买。


灵机一动想到了一个办法, 将价格序列一分为二。

两次交易在两个分开的序列中进行, 找到各自的最大值再加总。

序列拆分时,要确保每个序列都有大于两个的价格, 确保可以买卖

基于此的话, 如果是三次, 四次也可以依次推广。


java实现代码


以下是笔者使用Java实现的代码, 仅供参考:

/**
 * @Title: Stock.java
 * @Package com.oscar999.algorithm
 * @Description: TODO
 * @author oscar999
 * @date Feb 26, 2018 1:44:57 PM
 * @version V1.0
 */

package com.oscar999.algorithm;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @ClassName: Stock
 * @Description: TODO
 * @author oscar999
 */

public class Stock {

    /**
     * @Title: main
     * @Description: TODO
     * @param args
     */

    public static void main(String[] args) {
        List<Integer> priceList = new ArrayList<Integer>(Arrays.asList(10, 22, 5, 75, 65, 80));
        Stock stock = new Stock();
        List<Map<String, Integer>> list = stock.getMaxProfixInfo(priceList);
        if (list != null && list.size() > 0) {
            int maxProfit = 0;
            for (int i = 0; i < list.size(); i++) {
                Map<String, Integer> map = list.get(i);
                if (map.get("profit") != null) {
                    int iProfit = Integer.valueOf(map.get("profit"));
                    if (iProfit > 0) {
                        maxProfit += iProfit;
                        int ibuy = Integer.valueOf(map.get("buy"));
                        int isale = Integer.valueOf(map.get("sale"));
                        System.out.println("Buy: Position=" + (ibuy + 1) + ";Price=" + priceList.get(ibuy));
                        System.out.println("Sale: Position=" + (isale + 1) + ";Price=" + priceList.get(isale));
                    }
                }
            }
            System.out.println("Max Profit=" + maxProfit);
        } else {
            System.out.println("There is some error, can't get max profit.please check.");
        }
    }

    /**
     * divice Separate price to two list, get each max , and then count
     * 
     * @Title: getMaxProfixInfo
     * @Description: TODO
     * @param fPrices
     * @return
     */
    private List<Map<String, Integer>> getMaxProfixInfo(List<Integer> priceList) {
        List<Map<String, Integer>> list = new ArrayList<Map<String, Integer>>();
        int iLength = priceList.size();
        int maxProfit = 0;
        Map<String, Integer> sub1MaxInfoFinal = null;
        Map<String, Integer> sub2MaxInfoFinal = null;
        for (int iSep = 2; iSep < iLength - 2; iSep++) {
            List<Integer> sub1PriceList = priceList.subList(0, iSep);
            List<Integer> sub2PriceList = priceList.subList(iSep, iLength);
            Map<String, Integer> sub1MaxInfo = getMaxProfixInfoByPrices(sub1PriceList, 0);
            Map<String, Integer> sub2MaxInfo = getMaxProfixInfoByPrices(sub2PriceList, iSep);
            int subProfit = 0;
            int sub1Profit = Integer.valueOf(sub1MaxInfo.get("profit"));
            int sub2Profit = Integer.valueOf(sub2MaxInfo.get("profit"));
            if (sub1Profit > 0) {
                subProfit += sub1Profit;
            }
            if (sub2Profit > 0) {
                subProfit += sub2Profit;
            }
            if (subProfit > maxProfit) {
                maxProfit = subProfit;
                if (sub1Profit > 0) {
                    sub1MaxInfoFinal = sub1MaxInfo;
                } else {
                    sub1MaxInfoFinal = null;
                }
                if (sub2Profit > 0) {
                    sub2MaxInfoFinal = sub2MaxInfo;
                } else {
                    sub2MaxInfoFinal = null;
                }
            }
        }
        if (sub1MaxInfoFinal != null) {
            list.add(sub1MaxInfoFinal);
        }
        if (sub2MaxInfoFinal != null) {
            list.add(sub2MaxInfoFinal);
        }
        return list;
    }

    /**
     * 
     * @Title: getMaxProfixInfoByPrices
     * @Description: TODO
     * @param priceList
     * @param iBasePosition
     *            , sub list postion should add.
     * @return
     */
    private Map<String, Integer> getMaxProfixInfoByPrices(List<Integer> priceList, int iBasePosition) {
        Map<String, Integer> map = null;
        if (priceList != null && priceList.size() > 1) {
            int iLength = priceList.size();
            Integer maxProfit = 0;
            int iBuy = 0;
            int iSale = 0;
            for (int i = 0; i < iLength; i++) {
                for (int j = i + 1; j < iLength; j++) {
                    Integer profit = priceList.get(j) - priceList.get(i);
                    if (profit > maxProfit) {
                        maxProfit = profit;
                        iBuy = i;
                        iSale = j;
                    }
                }
            }
            if (maxProfit > 0 && iSale > iBuy) {
                map = new HashMap<String, Integer>();
                map.put("buy", iBuy + iBasePosition);
                map.put("sale", iSale + iBasePosition);
                map.put("profit", maxProfit);
            }
        }
        return map;
    }

}


寄语


最后, 欢迎有更好算法或解法的同道雅正

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

oscar999

送以玫瑰,手留余香

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值