买卖股票的最佳时机之最大子序和

导语

问题是这样的:
给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。若是你最多只容许完成一笔交易(即买入和卖出一支股票),设计一个算法来计算你所能获取的最大利润。
注意: 你不能在买入股票前卖出股票。
算法

例如:输入: [7,1,5,3,6,4]
输出: 5
解释: 在第 2 天(股票价格 = 1)的时候买入,在第 5 天(股票价格 =
6)的时候卖出,最大利润 = 6-1 = 5 。
注意利润不能是 7-1 = 6, 由于卖出价格须要大于买入价格。
数组

思路

你固然但愿 低价买进,高价卖出,这样即可以最大化利益。但遗憾的是,在一段给定时期内,可能没法作到。好比——最高价在前,最低价在后函数

1. 暴力求解方法

咱们能够很容易地设计出一个暴力方法来求解问题。简单尝试每对可能的买卖日期组合,只要卖出日期在买入日期以后就能够。很明显须要2层嵌套的循环来遍历全部组合,所以时间复杂度O(n2)spa

2. 问题变换 : 最大子数组

为了设计出一个运行时间更快的算法。咱们从不一样角度来看待输入数据。咱们的目的是寻找一段日期,使得从第一天到最后一天的股票价格增量最大。所以,咱们不在从每日价格的角度是看待数据。而是看每日价格变化。第i天的价格定义为第i天和第i-1天的价格,那么问题就转换为寻找 股票数组的最大子数组问题 咋一看,这种变化对问题求解并无陌生帮助。
虽然计算一个子数组之和所须要的时间是线性的,但计算全部子数组时,咱们能够从新组织新的计算方式。从而利用以前计算出的子数组和来计算当前子数组的和。时间仍然是O(n2)设计

2.1 分治法

咱们考虑如何用分治法来解决最大子数组问题。使用分治法意味着忙完要将子数组分为俩个规模尽可能相等的的子数组。也就是找到中央位置。好比mid,而后考虑求解 A[low , mid] 和 A[mid + 1 , high ] ,任何连续子数组A[i , j ] 都必然是如下三种状况.code

  • 彻底位于子数组A[low , high ] 所以 low <= i <= j <= high
  • 彻底位于子数组A[mid + 1 , high ] , 所以 mid < i <= j <= high
  • 跨过了中点,所以low<=i<mid<j <=high

跨过中点的最大子数组

function find_max_crossing_subArray(arr , low , mid , high) {
let sum1=  0 ;
let left_sum = -Infinity ;
for(let i = mid ; i >= low  ; i--) {
    sum1 = sum1 + arr[i] ;
    if(sum1 > left_sum ) {
        left_sum = sum1 ;
    }
}
let right_sum = -Infinity ;
let sum2 = 0 ;
for(let j = mid + 1 ; j <= high   ; j++ ) {
    sum2 = sum2 + arr[j] ;
    if(sum2 > right_sum ) {
        right_sum = sum2 ;
    }
}
//返回最大子序列的   最大值
return  left_sum + right_sum  ;
复制代码

}it

可知道这个函数的时间复杂度是O(n)
如今咱们就能够设计一个求解最大子数组的方法了。io

function find_maximun_subArray (arr , low , high) {
 
// 若是数组中只有一个元素 ,  则返回那个元素
if( low === high ) return  arr[low]  ;
else {
    let mid = Math.floor((low + high)/2) ;
    
    var left_sum =
        find_maximun_subArray(arr , low , mid ) ;
    var right_sum  =
        find_maximun_subArray(arr , mid + 1 , high) ;
    var cross_sum =
        find_max_crossing_subArray(arr , low , mid , high) ;
} ;
return Math.max(left_sum , Math.max(right_sum , cross_sum)) ;
复制代码

}function

总结:分治法的时间复杂度为 O(nlogn)class

相关文章
相关标签/搜索