菜鸟系列之C/C++经典试题(二)

求子数组的最大和

题目描述: 输入一个整形数组,数组里有正数也有负数。数组中连续的一个或多个整数组成一个子数组,每个子数组都有一个和。求所有子数组的和的最大值。要求时间复杂度为O(n)。

例如输入的数组为1, -2, 3, 10, -4, 7, 2, -5,和最大的子数组为3, 10, -4,7, 2,因此输出为该子数组的和18。

这个问题在各大公司面试中出现频率之频繁,被人引用次数之多,非一般面试题可与之匹敌。ok,下面,咱们来一步一步分析这个题。

分析与解法

解法一

求一个数组的最大子数组和,我想最直观最野蛮的办法便是,三个for循环三层遍历,求出数组中每一个子数组的和,最终求出这些子数组的最大的一个值。令currSum[i, …, j]为数组A中第i个元素到第j个元素的和(其中0 <= i <= j < n),maxSum为最终求到的最大连续子数组的和。

且当全是负数的情况时,我们可以让程序返回0,也可以让程序返回最大的那个负数,这里,我们让程序返回最大的那个负数。

参考代码如下:

int MaxSubArray(int* A, int n)
{
    int maxSum = a[0];  //全负情况,返回最大负数
    int currSum = 0;
    for (int i = 0; i < n; i++)
    {
        for (int j = i; j < n; j++)
        {
            for (int k = i; k <= j; k++)
            {
                currSum += A[k];
            }
            if (currSum > maxSum)
                maxSum = currSum;

            currSum = 0; //这里要记得清零,否则的话sum最终存放的是所有子数组的和。
        }
    }
    return maxSum;
}

解法二

事实上,当我们令currSum为当前最大子数组的和,maxSum为最后要返回的最大子数组的和,当我们往后扫描时,

  • 对第i+1个元素有两种选择:要么放入前面找到的子数组,要么做为新子数组的第一个元素;

·        如果currSum加上当前元素a[j]后currSum变大了,则令currSum加上a[j],否则currSum重新赋值,置为下一个元素,即currSum = a[j]。

  • 同时,当currSum > maxSum,则更新maxSum = currSum,否则保持原值,不更新。

即:

currSum = max(a[j], currSum + a[j])
maxSum = max(maxSum, currSum)

,当输入数组是  1, -2, 3, 10, -4, 7, 2, -5  ,那么,currSum和maxSum相应的变化为:

  • currSum: 0 1 - 1 3 13 9 16 18 13
  • maxSum : 0 1 1 3 13 13 16 18 18

参考代码如下:

//本段代码引自编程之美
int MaxSum(int* A, int n)
{
    int maximum = -INF;
    int sum = 0;
    for (int i = 0; i < n; i++)
    {
        for (int j = i; j < n; j++)
        {
            for (int k = i; k <= j; k++)
            {
                sum += A[k];
            }
            if (sum > maximum)
                maximum = sum;

            sum = 0; //这里要记得清零,否则的话sum最终存放的是所有子数组的和。也就是编程之美上所说的bug。多谢苍狼。
        }
    }
    return maximum;
}

问题扩展

  • 如果数组是二维数组,同样要你求最大子数组的和列?
  • 如果是要你求子数组的最大乘积列?
  • 如果同时要求输出子段的开始和结束列?

举一反三

1 给定整型数组,其中每个元素表示木板的高度,木板的宽度都相同,求这些木板拼出的最大矩形的面积。并分析时间复杂度。

此题类似leetcode里面关于连通器的题,需要明确的是高度可能为0,长度最长的矩形并不一定是最大矩形,还需要考虑高度很高,但长度较短的矩形。如[5,4,3,2,4,5,0,7,8,4,6]中最大矩形的高度是[7,8,4,6]组成的矩形,面积为16。

2、环面上的最大子矩形

《算法竞赛入门经典》 P89 页。

3、最大子矩阵和

一个M*N的矩阵,找到此矩阵的一个子矩阵,并且这个子矩阵的元素的和是最大的,输出这个最大的值。如果所有数都是负数,就输出0。例如:3*5的矩阵:

1 2 0 3 4

2 3 4 5 1

1 1 5 3 0

和最大的子矩阵是:

4 5

5 3

最后输出和的最大值17。

4、允许交换两个数的位置 求最大子数组和。

来源: https://codility.com/cert/view/certDUMWPM-8RF86G8P9QQ6JC8X/details

有误欢迎指出, 分享请标明出处, 谢谢!

菜鸟系列之C/C++经典试题(二),布布扣,bubuko.com

时间: 2024-12-29 01:37:45

菜鸟系列之C/C++经典试题(二)的相关文章

菜鸟系列之C/C++经典试题(一)

这个我写菜鸟系列之C/C++ 的第一篇文章 题目:输入一棵二元查找树,将该二元查找树转换成一个排序的双向链表.要求不能创建任何新的结点,只调整指针的指向. 比如将二元查找树 10 /    \ 6       14 /  \     / \ 4     8  12   16 转换成双向链表 4=6=8=10=12=14=16. 分析:本题是微软的面试题.很多与树相关的题目都是用递归的思路来解决,本题也不例外.下面我们用两种不同的递归思路来分析. 思路一:当我们到达某一结点准备调整以该结点为根结点

菜鸟系列之C/C++经典试题(三)

设计包含min函数的栈 题目:定义栈的数据结构,要求添加一个min函数,能够得到栈的最小元素.要求函数min.push以及pop的时间复杂度都是O(1). 分析:这是2006年google的一道面试题. 我看到这道题目时,第一反应就是每次push一个新元素时,将栈里所有逆序元素排序.这样栈顶元素将是最小元素.但由于不能保证最后push进栈的元素最先出栈,这种思路设计的数据结构已经不是一个栈了. 在栈里添加一个成员变量存放最小元素(或最小元素的位置).每次push一个新元素进栈的时候,如果该元素比

菜鸟系列之C/C++经典试题(四)

题目一:查找最小的k个元素 输入n个整数,输出其中最小的k个. 例如输入1,2,3,4,5,6,7和8这8个数字,则最小的4个数字为1,2,3和4. 分析:这道题最简单的思路莫过于把输入的n个整数排序,这样排在最前面的k个数就是最小的k个数.只是这种思路的时间复杂度为O(nlogn).我们试着寻找更快的解决思路. 我们可以先创建一个大小为k的数据容器来存储最小的k个数字.接下来我们每次从输入的n个整数中读入一个数.如果容器中已有的数字少于k个,则直接把这次读入的整数放入容器之中:如果容器中已有k

菜鸟系列之C/C++经典试题(十)

打印1到最大的n位数 题目:输入数字n,按顺序打印出从1到最大的n位十进制数.比如输入3,则打印1, 2, 3,-,999. 方法一: 这道题一看感觉很简单,首先求的n位数的最大值,然一个从1到这个最大值的循环就搞定了, 如果真的就把这样的答案面试官的话, 后果很是严重.首先, 没有考虑到获得的这个最大的数会不会溢出,如果溢出了, 答案肯定不对.这个代码比较简单, 我就不列出来了. 方法二: 很明显, 这道题我们可以用递归做, 就是每当多添加一位时, 我就把所以的都打印一遍连带刚添加的这个位,

菜鸟系列之C/C++经典试题(八)

计算二进制中1的个数 题目:位运算方面的编程很少遇到,但也是很重的一个只是点,一个比较常见的题目就是计算一个数的二进制表示中的1的个数. 分析:一个1都没有就是0, 我们的思路就是一位一位的运算, 我们很快就想到下面的做法: int countBit1(int val) { register int count = 0; while (val) { if (1 & val) { ++count; } val >>= 1; } return count; } 砟一看这个实现不错, 仔细看

菜鸟系列之C/C++经典试题(九)

寻找数组中出现的唯一重复的一个数 题目:1-1000放在含有1001个元素的数组中,只有唯一的一个元素值重复,其它均只出现一次.每个数组元素只能访问一次,设计一个算法,将它找出来:不用辅助存储空间,能否设计一个算法实现? 方法一:(当N为比较大时警惕溢出)将1001个元素相加减去1,2,3,--1000数列的和,得到的差即为重复的元素. int findRepeat(int *a) { int i; for (i = 0; i < 1001; i++) { a[1000] += a[i]; }

菜鸟系列之C/C++经典试题(六)

含有指针成员的类的拷贝 题目:下面是一个数组类的声明与实现.请分析这个类有什么问题,并针对存在的问题提出几种解决方案. template<typename T> class Array { public: Array(unsigned arraySize) :data(0), size(arraySize) { if (size > 0) data = new T[size]; } ~Array() { if (data) delete[] data; } void setValue(u

菜鸟系列之C/C++经典试题(七)

找含单链表的环入口点 问题1:如何判断单链表中是否存在环(即下图中从结点E到结点R组成的环)? 分析:设一快一慢两个指针(Node *fast, *low)同时从链表起点开始遍历,其中快指针每次移动长度为2,慢指针则为1.则若无环,开始遍历之后fast不可能与low重合,且fast或fast->next最终必然到达NULL:若有环,则fast必然不迟于low先进入环,且由于fast移动步长为2,low移动步长为1,则在low进入环后继续绕环遍历一周之前fast必然能与low重合(且必然是第一次重

菜鸟系列之C/C++经典试题(五)

求圆圈中剩下的最后一个数字 题目:n个数字(0,1,-,n-1)形成一个圆圈,从数字0开始,每次从这个圆圈中删除第m个数字(第一个为当前数字本身,第二个为当前数字的下一个数字).当一个数字删除后,从被删除数字的下一个继续删除第m个数字.求出在这个圆圈中剩下的最后一个数字. 本题就是著名的约瑟夫环问题. 本题的解法我们比较容易想到用链表,当然我们可以自己写一个链表,也可以直接用stl库中的list,实现代码如下: //使用标准库 int JosephusProblem_Solution2(int