zoukankan      html  css  js  c++  java
  • 求最大子列和问题

    方法1:暴力计算法

    i 表示子列开始索引

    j 表示子列结束索引

    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^3

    方法2:暴力破解优化

    因为方法一每次都是从 i 加到 j ,而 j 每次只往后扫描变化一个,所以直接将 k 循环省略在 j 循环中计算

    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^2

    方法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;
        }
    
    }

    运行结果

  • 相关阅读:
    Android 自定义ListView单击事件失效
    Android Studio无线连调式android手机
    Android 5.0 版本 USB 调试模式打开方法
    Android 加了自定义Application后报错 Unable to instantiate activity ComponentInfo ClassNotFoundException
    Android 模块构建错误不能下载依赖包
    Android ConstraintLayout 布局警告
    Android WebSocket开发
    Android 闪烁动画
    Android开发--Service和Activity通过广播传递消息
    CSS轮廓
  • 原文地址:https://www.cnblogs.com/GG-Bond/p/11402523.html
Copyright © 2011-2022 走看看