C#中求數組的子數組之和的最大值


《編程之美》183頁,問題2.14——求子數組的字數組之和的最大值。(整數數組)

我開始以為可以從數組中隨意抽調元素組成子數組,於是就有了一種想法,把最大的元素抽出來,判斷是大於0還是小於等於0,如果大於0就對除了這個最大值外剩下的數組部分進行遞歸:

using System;
using System.Collections.Generic;
using System.Linq;


namespace MaxSumSubArray
{
    class Program
    {
        static void Main(string[] args)
        {
            List<int> array = new List<int>() { -1, -2, 1, 2, -1, 1, -3, 6 - 3, -4, 1 };
            List<int> subArray = new List<int>();
            if (array.Max() > 0)
            {
                MaxSumSubArray(array, subArray);
                PrintArray(subArray);
            }
            else
            {
                Console.Write("The max sub-array is {" + array.Max() + "}");
            }
            Console.ReadLine();
        }

        private static void PrintArray(List<int> subArray)
        {
            Console.WriteLine("The max-sum sub-array is:");
            foreach (int num in subArray)
            {
                Console.Write(num);
                Console.Write(" ");
            }
        }

        private static void MaxSumSubArray(List<int> array, List<int> subArray)
        {
            if (array.Max() > 0)
            {
                subArray.Add(array.Max());
                array.Remove(array.Max());
                MaxSumSubArray(array,subArray);
            }
        }
    }
}
View Code

這樣做其實就是我想麻煩了,因為最后的結果證明,這樣做和遍歷數組把大於0的元素都抽出來是一樣的,根本用不着遞歸:

如果數組中沒有正整數,那就變成求數組中最大值的問題了。T.T

后來看答案發現,題中說的子數組還必須得是連續的,不能隨便抽調。於是開始重新想問題……

我發現,連續子數組的和是不是最大的,要把所有的子數組都找出來以后才能確定。

那沒什么好辦法了,只能遍歷了,首先從第一個元素開始,第一個子數組就是第一個元素本身,第二個子數組就是第一個元素和第二個元素組成的,第三個……

然后從第二個元素開始……

等把這些子數組都列出來以后,分別加和,比較一下哪個最大哪個就是目標子數組了。

代碼如下:

using System;
using System.Collections.Generic;
using System.Linq;

namespace MaxSumSubArray
{
    class Program
    {
        static void Main(string[] args)
        {
            List<int> array = new List<int>() { -2, 5, 3, -6, 4, -8, 6 };
            List<List<int>> subArrays = new List<List<int>>();
            int subSum = 0;
            int origin = 0;
            for (int k = 1; k <= array.Count(); k++)
            {
                for (int i = k; i <= array.Count(); i++)
                {
                    subArrays.Add(new List<int>());
                    for (int j = k; j <= i; j++)
                    {
                        subArrays[origin].Add(array[j - 1]);
                        subSum += array[j - 1];
                    }
                    subArrays[origin].Add(subSum);
                    origin++;
                    subSum = 0;
                }
            }
            int max = subArrays[0][subArrays[0].Count() - 1];
            int destination = 0;
            for (int i = 0; i < subArrays.Count(); i++)
            {
                int sumIndex = subArrays[i].Count() - 1;
                if (subArrays[i][sumIndex] > max)
                {
                    max = subArrays[i][subArrays[i].Count() - 1];
                    destination = i;
                }
            }
            PrintArray(subArrays[destination]);
            Console.ReadLine();
        }

        private static void PrintArray(List<int> array)
        {
            Console.WriteLine("The max-sum sub-array is:");
            for (int i = 0; i < array.Count() - 1; i++)
            {
                Console.Write(array[i]);
                Console.Write(" ");
            }
            Console.WriteLine();
            Console.WriteLine("The sum of the array is:" + array[array.Count() - 1]);
        }
    }
}

這里我用了泛型數組的嵌套,因為泛型數組是動態的,所以嵌套的泛型數組就相當於一個動態的二維數組:

List<List<int>> subArrays = new List<List<int>>();

subArrays數組中每一個元素都是一個整型數組List<int>。

接下來是三層for循環:

最內層的for循環就是從數組的某一個元素一直加到某一個元素;

中層for循環是從數組某一個元素開始,一直到最后一個元素;

最外層的for循環是用來指定子數組的首元素。

通過三層for循環就可以求出所有的子數組以及他們的和,我把每一個子數組都做為一個元素存在List<int>型數組中,並且int型數組中的最后一位元素用來存子數組的和。

接下來我們遍歷所有子數組的末尾元素,比較出所有子數組中和最大的子數組並將其打印。

至此,我們就求出了數組中和最大的子數組:

以上就是經過我的思考所得出的解法。對比書中的解法發現我的解法基本上是書中寫的沒有優化過的解法一,但細節實現不一樣,我的方法不光可以找出最大子數組和,還可以打印對應數組。書中說可以省略一層for循環來避免重復計算對算法進行優化,但是我沒想通,改成書中說的那樣發現測試結果不對。如果哪位高手有好意見,請給出優化代碼。QQQ~!——2015.11.18


2015.12.29我又在網上看到了這個題,於是又經過一番思考,得出了一種新的解法,思路如下:

1、如果數組中全是非正數,則最大子數組就是該數組的最大值。這種情況下,根本就不需要繼續遍歷,極大的減少了計算量,直接得出答案。

2、如果數組中全是非負數,則最大字數組就是該數組本身。這種情況下,根本就不需要繼續遍歷,極大的減少了計算量,直接得出答案。

3、數組中有正數也有負數,則最大子數組的開頭肯定是非負數,結尾也肯定為非負數!也就是說,子數組的核心成員就鎖定在那些非負數上。我要找出數組中所有的非負數,記錄他們在數組中的位置。最終的目標是計算每兩個非負數在數組中的距離。(即兩個非負數以及其之間的數所組成的子數組的和)

代碼如下:

using System;
using System.Collections.Generic;
using System.Linq;

namespace MaxSubarray
{
    class Program
    {
        private static List<int> indexArray = new List<int>();
        private static List<int> results = new List<int>();
        static void Main(string[] args)
        {
            //int[] a = { 1, -2, 3, 10, -4, 7, 2, -5 };
            int[] a = { -2, 5, 3, -6, 4, -8, 6 };
            //Select the ones >=0.
            for (int i = 0;i< a.Length;i++)
            {
                if (a[i] > 0)
                {
                    indexArray.Add(i);
                }
            }
            //Calc all the sums.
            CalcResults(a);
            //Output the final result.
            Console.WriteLine(results.Max());
            Console.Read();
        }

        private static void CalcResults(int[] array)
        {
            int sum;
            for (int i = 0; i < indexArray.Count(); i++)
            {
                for (int j = 0; j <= i; j++)
                {
                    sum = CalcSum(indexArray[j], indexArray[i], array);
                    results.Add(sum);
                }
            }
        }

        private static int CalcSum(int from, int to, int[] array)
        {
            int sum = 0;
            for (int i = from; i <= to; i++)
            {
                sum += array[i];
            }
            return sum;
        }
    }
}

這里主要用到了三個方法:

1、方法一:計算數組中兩點之間元素所組成的子數組的和;

2、方法二:計算出所有的非負數元素間的組合情況,並用方法一求出兩個非負數元素之間所組成的子數組的和;

3、方法三:找出這些和中最大的值就是最大子數組的和。

該方法主要的思想是縮小了搜索范圍,分況治之。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM