【问题标题】:Buy and Sell Stock, with O( n log n ) time complexity买卖股票,时间复杂度为 O(n log n)
【发布时间】:2015-09-21 23:28:25
【问题描述】:

编辑:
我感谢你们俩的帮助,但我必须遵守 O(n log n) 时间完整性的界限,并且必须使用二进制递归的分而治之技术。我在最初的帖子中没有说得很清楚

我有一个未排序的整数数组,我必须在第 i 天买入股票并在第 j 天卖出以获得最大利润,其中第 i 天(较小的值)必须在第 j 天(较大的值) 日。到目前为止,我找到了一个解决方案,可以返回购买和销售天数(数组的索引值)和最大利润,但它的时间复杂度为 O(n^2),我很难达到 O(n log n) 时间复杂度和实现分而治之

public static Profit BestProfit( int[] a, int i, int j )
{
   Profit bestProfit = new Profit();

   int n = j; 
   int maxProfit = 0;
   for(i = 0; i < n; i++)
   {
      for(j = i; j < n; j++)
      {
         if(a[j] - a[i] > maxProfit)
         {
            maxProfit = a[j] - a[i];
            bestProfit.setBuy( i );
            bestProfit.setSell( j );
            bestProfit.setMaxProfit( maxProfit );
         }
      }
   return bestProfit;
   } 

参数 i 是数组的开头, j 是数组的末尾 Profit 类是我创建的用于保存买入、卖出和利润值的类。

我发现我需要考虑的三种情况是数组前半部分的利润最大,数组后半部分的利润最大,最小值在前半部分的情况数组的最大值和最大值在数组的第二半(我已经用一个简单的 min/max 函数完成了这部分问题,解决了最后的情况)。

我被困住了,任何有关分而治之实施或技巧提示的帮助将不胜感激!

【问题讨论】:

  • 我编辑了聚合分而治之的解决方案。

标签: java divide-and-conquer


【解决方案1】:

在 O(n) 中,非常简单:

public static Profit bestProfit(int[] a, int begin, int end) {
    Profit bestProfit = new Profit();
    int min = a[begin];
    int max = a[begin];
    int index = begin;
    int buy = 0;
    int sell = 0;
    int minIndex = begin;
    int maxIndex;
    int maxProfit = 0;
    for (int i = begin; i < end; i++) {
        int n = a[i];
        if (n < min) {
            minIndex = index;
            min = n;
            max = n;
        } else if (max < n) {
            max = n;
            maxIndex = index;
            if (maxProfit < (max - min)) {
                maxProfit = max - min;
                buy = minIndex;
                sell = maxIndex;
            }
        }
        index++;
    }
    bestProfit.setBuy(buy);
    bestProfit.setSell(sell);
    bestProfit.setMaxProfit(maxProfit);
    return bestProfit;
}

已编辑:分而治之:

public static int divideAndConquer(int[] a, int i, int j, Profit profit, int min) {
    int minResult;
    if (i+1 >= j) {
        minResult = Math.min(a[i], min);
        if (a[i] - min > profit.getMaxProfit()) {
            profit.setBuy(min);
            profit.setSell(a[i]);
            profit.setMaxProfit(a[i] - min);
        }
    } else {
        int n = (j+i)/2;
        minResult = divideAndConquer(a, i, n, profit, min);
        minResult = divideAndConquer(a, n, j, profit, minResult);
    }
    return minResult;
}

public static void main(String[] args) {
    int[] prices = {20, 31, 5, 7, 3, 4, 5, 6, 4, 0, 8, 7, 7, 4, 1,10};
    Profit profit =new Profit();
    divideAndConquer(prices, 0, prices.length, profit, Integer.MAX_VALUE);
    System.out.println(profit);
}

【讨论】:

  • 感谢你们俩的帮助,但我必须遵守 O(n log n) 时间完整性,并且必须使用二进制递归的分而治之技术。我在最初的帖子中没有说得很清楚。
  • 已编辑:分治法和二进制递归。
【解决方案2】:

你可以只用三个循环将其改进为 O(n):

  • 构建最小数组的第一个循环
  • 构建最大数组的第二个循环
  • 寻找最大利润的第三个循环

或多或少:

public static void main(String[] args) {

    int[] stockPrices = {2, 9, 5, 7, 3, 4, 5, 6, 4, 0, 8, 7, 7, 4, 1};

    int[] mins = new int[stockPrices.length - 1];
    int[] minsIndex = new int[stockPrices.length - 1];
    int[] maxs = new int[stockPrices.length - 1];
    int[] maxsIndex = new int[stockPrices.length - 1];

    int minIndex = -1;
    int min = Integer.MAX_VALUE;
    for (int i = 0; i < stockPrices.length - 1; i++) {
        if (stockPrices[i] < min) {
            min = stockPrices[i];
            minIndex = i;
        }
        mins[i] = min;
        minsIndex[i] = minIndex;
    }

    System.out.println("mins idx: " + Arrays.toString(minsIndex));
    System.out.println("mins: " + Arrays.toString(mins));

    int maxIndex = -1;
    int max = -1;
    for (int i = stockPrices.length - 1; i > 0; i--) {
        if (stockPrices[i] > max) {
            max = stockPrices[i];
            maxIndex = i;
        }
        maxs[i - 1] = max;
        maxsIndex[i - 1] = maxIndex;
    }

    System.out.println("maxs idx: " + Arrays.toString(maxsIndex));
    System.out.println("maxs: " + Arrays.toString(maxs));

    int maxProfit = -1;
    int buyIndex = -1;
    int sellIndex = -1;
    for (int i = 0; i < stockPrices.length - 1; i++) {
        int profit = maxs[i] - mins[i];
        if (profit > maxProfit) {
            maxProfit = profit;
            buyIndex = minsIndex[i];
            sellIndex = maxsIndex[i];
        }
    }

    System.out.println("buy at: " + buyIndex + " sell at: " + sellIndex + " profit: " + maxProfit);
}

【讨论】:

  • 感谢你们俩的帮助,但我必须遵守 O(n log n) 时间完整性,并且必须使用二进制递归的分而治之技术。我在最初的帖子中没有说得很清楚。
【解决方案3】:

好吧,既然你表达了使用分而治之的需要,那我再给出一个答案。

假设股票价格在一个数组中定义:[p0, p1, ..., pn]。

我们可以把这个问题分解成这个定义的子问题。

max profit = max(maxprofit([p0], [p1..pn]), maxprofit([p0..p1], [p2..pn]), ..., maxprofit([p0..pn-1], [pn]))

maxprofit 的第一个参数是买价数组,第二个参数是卖价数组。

查看第一个子问题

maxprofit([p0], [p1..pn])

我们可以进一步划分:

maxprofit([p0], [p1..pn]) = max(maxprofit([p0], [p1]), maxprofit([p0],[p2..pn]))

我们可以解决max([p0], [p1]),因为这是一个利润 = p1-p0 的基础问题。现在我们保留结果,并缓存它。继续分解maxprofit(([p0], [p2..pn]) 并继续缓存所有解决方案。

查看第二个子问题

这就是问题所在:

maxprofit([p0..p1], [p2..pn])

可以分解为:

maxprofit([p0..p1], [p2..pn]) = max(maxprofit([p0], [p2..pn]), maxprofit([p1], [p2..pn]))

有趣的是:您不必分解maxprofit([p0], [p2..pn]),因为您在处理第一个子问题时已经在缓存中。因此只需要分解第二个子子问题。

我想此时您已经了解了它的发展方向。基本上,您需要不断分解问题,直到遇到基本问题或问题已被缓存。

【讨论】:

    猜你喜欢
    • 2011-07-09
    • 1970-01-01
    • 1970-01-01
    • 2015-06-12
    • 1970-01-01
    • 2015-05-25
    • 1970-01-01
    • 2022-10-24
    • 1970-01-01
    相关资源
    最近更新 更多