• 求最大子列和问题


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

    运行结果

  • 相关阅读:
    Java 标识符
    Java 关键字详解
    Java 语言的主要特性
    redis学习
    垃圾回收
    JVM内存结构
    sql总结(DML)
    sql总结(DDL)
    加密算法
    《数据结构》 定长顺序串常用操作代码集合
  • 原文地址:https://www.cnblogs.com/GG-Bond/p/11402523.html
Copyright © 2020-2023  润新知