求最大子列和問題

方法1:暴力計算法算法

i 表示子列開始索引數組

j 表示子列結束索引ide

k(i<k<j) 輔助計算 i~j之間子列和優化

public int method1(int[] arr) {
        int maxSum = 0;
        for (int i = 0; i < arr.length; i++) {
            for (int j = i; j < arr.length; j++) {
                int tempMax = 0;
                for (int k = i; k <= j; k++) {
                    tempMax += arr[k];
                }
                if (tempMax > maxSum) {
                    maxSum = tempMax;
                }
            }
        }
        return maxSum;
    }

三層for循環,時間複雜度 T(N) = n^3spa

方法2:暴力破解優化3d

由於方法一每次都是從 i 加到 j ,而 j 每次隻日後掃描變化一個,因此直接將 k 循環省略在 j 循環中計算code

public int method2(int[] arr) {
        int maxSum = 0;
        for (int i = 0; i < arr.length; i++) {
            int tempSum = 0;
            for (int j = i; j <= arr.length; j++) {
                tempSum += arr[j];
         
if (tempSum > maxSum) {
            maxSum = tempSum;
          }
        }
     }
     return maxSum;
}

兩層for循環,T(N) = n^2blog

方法3:分治策略,將問題劃分爲更小的問題,解決後再返回來求最優解索引

    /**
     * 爲了與方法1和2有一樣的調用接口
     * @param arr
     * @return
     */
    public int method3(int[] arr) {
        return divideAndConquer(arr, 0, arr.length - 1);
    }

    /**
     * 將數組劃分爲兩部分,先計算左半部分最大子列和,再計算又半部分最大子列和,再從分界線向左向右分別掃描獲取最大子列和取得跨界最大子列和
     *
     * @param arr
     * @param left
     * @param right
     * @return
     */
    private int divideAndConquer(int[] arr, int left, int right) {
        /**
         * 若是索引相等,表示已經劃分該部分爲最小問題,元素個數爲1
         * 若該元素大於0,則對計算下一步運算有幫助,返回原值
         * 若該元素小於0,則不管向左向右加都會減少相鄰子列的和,因此捨棄該元素,返回0
         */
        if (left == right) {
            if (arr[left] > 0) {
                return arr[left];
            } else {
                return 0;
            }
        }

        /* 下面是"分"的過程 */
        int mid = (left + right) / 2;
        /* 求最大左子列和 */
        int maxLeftSum = divideAndConquer(arr, left, mid);
        /* 求最大右子列和 */
        int maxRightSum = divideAndConquer(arr, mid + 1, right);

        /* 求跨界最大子列和 */
        int maxLeftBorderSum = 0;
        int maxRightBorderSum = 0;
        int leftBorderSum = 0;
        int rightBorderSum = 0;

        /* 從中線向左掃描 */
        for (int i = mid; i >= left; i--) {
            leftBorderSum += arr[i];
            if (leftBorderSum > maxLeftBorderSum) {
                maxLeftBorderSum = leftBorderSum;
            }
        }

        /* 從中線向右掃描 */
        for (int i = mid + 1; i < right; i++) {
            rightBorderSum += arr[i];
            if (rightBorderSum > maxRightBorderSum) {
                maxRightBorderSum = rightBorderSum;
            }
        }
        
        /* 返回該分段中最左,最右,跨界三者中最大數做爲該分段最大子列和 */
        return max3(maxLeftSum, maxRightSum, maxLeftBorderSum + maxRightBorderSum);
    }

    /**
     * 求三整數最大值
     * 計算順序
     * a > b ? (a > c ? a : c) : (b > c ? b : c)
     * @param a
     * @param b
     * @param c
     * @return
     */
    private int max3(int a, int b, int c) {
        return a > b ? a > c ? a : c : b > c ? b : c;
    }

T(N) = T(N/2) + cN接口

求解得時間複雜度 T(N) = nlogn

方法4:在線處理方法,掃描一個元素解決當前階段最大子列和

/**
     * 在線處理
     *
     * @param arr
     */
    public int method4(int[] arr) {
        int maxSeqSum = 0;
        int tempMax = 0;
        for (int i = 0; i < arr.length; i++) {
            tempMax += arr[i]; // 向右累加
            if (tempMax > maxSeqSum) {
                maxSeqSum = tempMax; // 發現更大的和則更新當前結果
            } else if (tempMax < 0) { // 若是當前子列和爲負
                tempMax = 0; // 則不可能使後面的部分和增大,拋棄之
            }
        }
        return maxSeqSum;
    }

每一個元素最少都要掃描一遍,時間複雜度T(N) = n

 

運行代碼

public class SearchMaxQueueSum {
    public static void main(String[] args) {
        int[] arr = new int[]{-1, 3, -2, 4, -6, 1, 6, -1};
        SearchMaxQueueSum searchMaxQueueSum = new SearchMaxQueueSum();
        int seqSumFromMethod1 = searchMaxQueueSum.method1(arr);
        int seqSumFromMethod2 = searchMaxQueueSum.method3(arr);
        int seqSumFromMethod3 = searchMaxQueueSum.method3(arr);
        int seqSumFromMethod4 = searchMaxQueueSum.method4(arr);

        System.out.println("method1: "+seqSumFromMethod1);
        System.out.println("method2: "+seqSumFromMethod2);
        System.out.println("method3: "+seqSumFromMethod3);
        System.out.println("method4: "+seqSumFromMethod4);

    }

    public int method1(int[] arr) {
        int maxSum = 0;
        for (int i = 0; i < arr.length; i++) {
            for (int j = i; j < arr.length; j++) {
                int tempMax = 0;
                for (int k = i; k <= j; k++) {
                    tempMax += arr[k];
                }
                if (tempMax > maxSum) {
                    maxSum = tempMax;
                }
            }
        }
        return maxSum;
    }

    public int method2(int[] arr) {
        int maxSum = 0;
        for (int i = 0; i < arr.length; i++) {
            int tempSum = 0;
            for (int j = i; j <= arr.length; j++) {
                tempSum += arr[j];
                if (tempSum > maxSum) {
                    maxSum = tempSum;
                }
            }

        }
        return maxSum;
    }

    /**
     * 爲了與方法1和2有一樣的調用接口
     * @param arr
     * @return
     */
    public int method3(int[] arr) {
        return divideAndConquer(arr, 0, arr.length - 1);
    }

    /**
     * 將數組劃分爲兩部分,先計算左半部分最大子列和,再計算又半部分最大子列和,再從分界線向左向右分別掃描獲取最大子列和取得跨界最大子列和
     *
     * @param arr
     * @param left
     * @param right
     * @return
     */
    private int divideAndConquer(int[] arr, int left, int right) {
        /**
         * 若是索引相等,表示已經劃分該部分爲最小問題,元素個數爲1
         * 若該元素大於0,則對計算下一步運算有幫助,返回原值
         * 若該元素小於0,則不管向左向右加都會減少相鄰子列的和,因此捨棄該元素,返回0
         */
        if (left == right) {
            if (arr[left] > 0) {
                return arr[left];
            } else {
                return 0;
            }
        }

        /* 下面是"分"的過程 */
        int mid = (left + right) / 2;
        /* 求最大左子列和 */
        int maxLeftSum = divideAndConquer(arr, left, mid);
        /* 求最大右子列和 */
        int maxRightSum = divideAndConquer(arr, mid + 1, right);

        /* 求跨界最大子列和 */
        int maxLeftBorderSum = 0;
        int maxRightBorderSum = 0;
        int leftBorderSum = 0;
        int rightBorderSum = 0;

        /* 從中線向左掃描 */
        for (int i = mid; i >= left; i--) {
            leftBorderSum += arr[i];
            if (leftBorderSum > maxLeftBorderSum) {
                maxLeftBorderSum = leftBorderSum;
            }
        }

        /* 從中線向右掃描 */
        for (int i = mid + 1; i < right; i++) {
            rightBorderSum += arr[i];
            if (rightBorderSum > maxRightBorderSum) {
                maxRightBorderSum = rightBorderSum;
            }
        }

        /* 返回該分段中最左,最右,跨界三者中最大數做爲該分段最大子列和 */
        return max3(maxLeftSum, maxRightSum, maxLeftBorderSum + maxRightBorderSum);
    }

    /**
     * 求三整數最大值
     * 計算順序
     * a > b ? (a > c ? a : c) : (b > c ? b : c)
     * @param a
     * @param b
     * @param c
     * @return
     */
    private int max3(int a, int b, int c) {
        return a > b ? a > c ? a : c : b > c ? b : c;
    }

    /**
     * 在線處理
     *
     * @param arr
     */
    public int method4(int[] arr) {
        int maxSeqSum = 0;
        int tempMax = 0;
        for (int i = 0; i < arr.length; i++) {
            tempMax += arr[i]; // 向右累加
            if (tempMax > maxSeqSum) {
                maxSeqSum = tempMax; // 發現更大的和則更新當前結果
            } else if (tempMax < 0) { // 若是當前子列和爲負
                tempMax = 0; // 則不可能使後面的部分和增大,拋棄之
            }
        }
        return maxSeqSum;
    }

}

運行結果

相關文章
相關標籤/搜索