數組:最長子序列問題四種解法
問題描述:
給定一個整數數組 nums ,找到一個具有最大和的連續子數組(子數組最少包含一個元素),返回其最大和。
示例 1 :
輸入: [-2,1,-3,4,-1,2,1,-5,4],
輸出: 6
解釋: 連續子數組 [4,-1,2,1] 的和最大,爲 6。
解法一、三重for循環
解題思路: 通過前兩層for循環找出數組的所有組合,第三層for循環計算該組合的sum,如果大於max,則賦值給max。
時間複雜度是O(N3),空間複雜度是O(1)。
public int maxSubArray(int[] nums) {
int max_res = Integer.MIN_VALUE;
for(int i = 0; i < nums.length; i++) {
for(int j = i; j < nums.length; j++) {
int thisSum = 0;
for(int k = i; k <= j; k++) {
thisSum += nums[k];
}
if(max_res < thisSum) {
max_res = thisSum;
}
}
}
return max_res;
}
額外補充:Java基本類型最大最小值的表示:
fmax = Float.MAX_VALUE;
fmin = Float.MIN_VALUE;
dmax = Double.MAX_VALUE;
dmin = Double.MIN_VALUE;
bmax = Byte.MAX_VALUE;
bmin = Byte.MIN_VALUE;
cmax = Character.MAX_VALUE;
cmin = Character.MIN_VALUE;
shmax = Short.MAX_VALUE;
shmin = Short.MIN_VALUE;
imax = Integer.MAX_VALUE;
imin = Integer.MIN_VALUE;
lmax = Long.MAX_VALUE;
lmin = Long.MIN_VALUE;
解法二、二重for循環
解題思路: 該方法是用兩層循環,不用特地用第三層循環計算,不然會導致大量的重複計算。
時間複雜度是O(N2),空間複雜度是O(1)。
public int maxSubArray(int[] nums) {
int max_res = Integer.MIN_VALUE;
for(int i = 0; i < nums.length; i++) {
int thisSum = 0;
for(int j = i; j < nums.length; j++) {
thisSum += nums[j];
if(max_res < thisSum) {
max_res = thisSum;
}
}
}
return max_res;
}
解法三、分治策略
解題思路: 將數組從中間分爲兩部分,容易看出來,最長子序列和可能存在在三個地方:左邊,右邊或者是中間,通過遞歸的方式就可以求出最長子序列和了。
時間複雜度是O(NlogN),空間複雜度是O(1)。
static int maxSum(int[] nums, int left, int right) {
if(left == right) {
return nums[left];
}
int mid = (left + right) / 2;
//求左邊的最長子序和
int maxLeftSum = maxSum(nums, left, mid);
//求右邊的最長子序和
int maxRightSum = maxSum(nums, mid+1, right);
//求中間到兩邊的最長子序列和
//因爲最小的數不知道多小,所以一開始初試爲INT的最小值
int maxLeftBorderSum = Integer.MIN_VALUE;
int maxRightBorderSum = Integer.MIN_VALUE;
int temp = 0;
//左邊那塊從右邊開始找
for(int i = mid; i >= left; i--) {
temp += nums[i];
if(maxLeftBorderSum < temp) {
maxLeftBorderSum = temp;
}
}
temp = 0;
//右邊那塊從左邊開始找
for(int i = mid + 1; i <= right; i++) {
temp += nums[i];
if(maxRightBorderSum < temp) {
maxRightBorderSum = temp;
}
}
System.out.println(maxLeftBorderSum + maxRightBorderSum+" "+maxRightSum+" "+maxLeftSum);
//計算三者的最大值
return Math.max(maxLeftBorderSum + maxRightBorderSum, Math.max(maxRightSum, maxLeftSum));
}
public int maxSubArray(int[] nums) {
return maxSum(nums, 0, nums.length-1);
}
解法四、規律
解題思路: 一次循環,只對數據進行一次掃描,開始累加,一開始是正的,如果加上這個數變成了負數,說明該數是負的,則歸零。
時間複雜度是O(N),空間複雜度是O(1)。
public int maxSubArray(int[] nums) {
int maxSum = Integer.MIN_VALUE;
int thisSum = 0;
for(int i = 0; i < nums.length; i++) {
thisSum += nums[i];
if(maxSum < thisSum) {
maxSum = thisSum;
}
if(thisSum < 0) {
thisSum = 0;
}
}
return maxSum;
}
總結:解決一道題有多種解法,只要你耐心去做就會收穫很大,有的解法雖然能夠通過,但面對大量輸入時,可能就是無法應用,無論是空間複雜度還是時間複雜度,都值得我們考慮。