57. 数对之差的最大值:4种方法详解与总结[maximum difference of array]

【本文链接】

http://www.cnblogs.com/hellogiser/p/maximum-difference-of-array.html

题目】

在数组中,数字减去它右边的数字得到一个数对之差。求所有数对之差的最大值。例如在数组{2, 4, 1, 16, 7, 5, 11,
9}中,数对之差的最大值是11,是16减去5的结果。

分析

看到这个题目,很多人的第一反应是找到这个数组的最大值和最小值,然后觉得最大值减去最小值就是最终的结果。这种思路忽略了题目中很重要的一点:数对之差是一个数字减去它右边的数字(不包括自身)。由于我们无法保证最大值一定位于数组的左边,因此这个思路不管用。

有如下4种方法对此题进行解答。


方法1:蛮力法

很容易想到,让每一个数字逐个减去它右边的所有数字,并通过比较得到数对之差的最大值。所有的数对只差共有n*(n-1)/2,因而时间复杂度为O(n^2)。我们设定只有1个数字时,最大数对之差为INT_MIN,即0x80000000。

【代码】

C++
Code






1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79

 

// 57_MaxDiffOfArray.cpp : Defines the entry point for the console application.

//

/*

     version: 1.0

     author: hellogiser

     blog: http://www.cnblogs.com/hellogiser

     date: 2014/5/24
*/


#include "stdafx.h"
#include <iostream>
using namespace std;

// brute force

int MaxDiff_BruteForce(int *numbers, int length)

{
    // O(n^2)
    if(NULL == numbers || length < 2)

        return INT_MIN;

    //init maxdiff to min value

    int maxDiff = INT_MIN;

    for (int i = 0; i < length; ++i)

     {

        for (int j = i + 1; j < length; ++j)

         {

            int diff = numbers[i] - numbers[j];

            // update maxDiff
            if (diff > maxDiff)

                 maxDiff = diff;

         }

     }
    return maxDiff;
}

void test_base(int *numbers, int length)

{

     cout << MaxDiff_BruteForce(numbers, length) << endl;

}

void test_case1()

{
    int a[] = {2};
    int length = sizeof(a) / sizeof(int);

     test_base(a, length);
}

void test_case2()

{
    int a[] = {2, 4};
    int length = sizeof(a) / sizeof(int);

     test_base(a, length);
}

void test_case3()

{
    int a[] = {2, 4, 1, 16, 7, 5, 11, 9};
    int length = sizeof(a) / sizeof(int);

     test_base(a, length);
}

void test_main()

{
     test_case1();

     test_case2();

     test_case3();
}

int _tmain(int argc, _TCHAR *argv[])

{
     test_main();

    return 0;
}
/*

-2147483648
-2
11
*/


方法2:分治法

通常【蛮力法】不会是最好的解法,我们想办法减少减法的次数。

假设我们把数组以中间元素为分割点分成两个子数组,我们其实没有必要拿左边的子数组中较小的数字A去和右边的子数组中较大的数字B作减法。我们可以想象,数对之差的最大值只有可能是下面三种情况之一:

(1)A和B都在第一个子数组中,即第一个子数组中的数对之差的最大值;

(2)A和B都在第二个子数组中,即第二个子数组中数对之差的最大值;

(3)A在第一个子数组中,是第一个子数组的最大值;B在第二个子数组中,是第二个子数组的最小值。

那么这三个差值的最大者就是整个数组中数对之差的最大值。

在前面提到的三种情况中,得到第一个子数组的最大值和第二子数组的最小值不是一件难事,但如何得到两个子数组中的数对之差的最大值?这其实是原始问题的子问题,我们可以递归地解决。

【代码】

C++
Code






1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54

 

/*

     version: 1.0

     author: hellogiser

     blog: http://www.cnblogs.com/hellogiser

     date: 2014/5/24
*/


#define MIN(a,b) a<b?a:b

#define MAX(a,b) a>b?a:b


// get max of a b c

int max3(int a, int b, int c)

{
    int t = a > b ? a : b;

    return t > c ? t : c;

}

// get max diff from [left,right] and pass out max/min value

int MaxDiffCore(int *left, int *right, int *min, int *max)

{
    if(left == right)

     {

         *max = *min = *left;

        return INT_MIN;

     }
    int *middle = left + (right - left) / 2;
    // get left max diff

    int minLeft, maxLeft;

    int leftDiff = MaxDiffCore(left, middle, &minLeft, &maxLeft);


    // get right max diff

    int minRight, maxRight;

    int rightDiff = MaxDiffCore(middle + 1, right, &minRight, &maxRight);


    // get cross max diff

    int crossDiff = maxLeft - minRight;


    // update whole array min and max value

    *min = MIN(minLeft, minRight);

     *max = MAX(maxLeft, maxRight);


    int maxDiff = max3(leftDiff, rightDiff, crossDiff);

    return maxDiff;

}

// divide and conquer

int MaxDiff_DivideAndConquer(int *numbers, int length)

{
    // T(n)=2*T(n/2)+O(1)===>Tn=O(n)

    if(NULL == numbers || length < 2)

        return INT_MIN;

    int min, max;

    return MaxDiffCore(numbers, numbers + length - 1, &min, &max);

}

在函数MaxDiffCore中,我们先得到第一个子数组中的最大的数对之差leftDiff,再得到第二个子数组中的最大数对之差rightDiff。接下来用第一个子数组的最大值减去第二个子数组的最小值得到crossDiff。这三者的最大值就是整个数组的最大数对之差。在求解数对之差的同时,还要求解子数组的最小值和最大值。


方法3:转化法

转换为求子数组的最大和问题。

接下来再介绍一种比较巧妙的解法。如果输入一个长度为n的数组numbers,我们先构建一个长度为n-1的辅助数组diff,并且diff[i]等于numbers[i]-numbers[i+1](0<=i<n-1)。如果我们从数组diff中的第i个数字一直累加到第j个数字(j
> i),也就是diff[i] + diff[i+1] + … + diff[j] = (numbers[i]-numbers[i+1]) +
(numbers[i + 1]-numbers[i+2]) + ... + (numbers[j] – numbers[j + 1]) = numbers[i]
– numbers[j + 1]。

分析到这里,我们发现原始数组中最大的数对之差(即numbers[i] – numbers[j +
1])其实是辅助数组diff中最大的连续子数组之和。

【代码】

C++
Code






1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45

 

/*

     version: 1.0

     author: hellogiser

     blog: http://www.cnblogs.com/hellogiser

     date: 2014/5/24
*/

// max sub sequence sum

int MaxSubSequenceSum(int *array, int length)

{
    // f = max(f+a[i],a[i])

    if(NULL == array || length <= 0)

        return INT_MIN;

    int f = array[0];
    int greatest = array[0];
    for (int i = 1; i < length; i++)

     {

        if (f <= 0)

             f = array[i];

        else

             f += array[i];

        // update greatest

        if (greatest < f)

             greatest = f;

     }
    return greatest;
}

// maximum continuous sub-sequence sum

int MaxDiff_Transformation(int *numbers, int length)

{
    // Tn=O(n)
    if(NULL == numbers || length < 2)

        return INT_MIN;

    // generate diff array

    int diffLength = length - 1;
    int *diff = new int[diffLength];

    for (int i = 0; i < diffLength; ++i)

         diff[i] = numbers[i] - numbers[i + 1];

    // get maximum continuous sub-sequence sum

    int maxDiff = MaxSubSequenceSum(diff, diffLength);

    delete []diff;

    return maxDiff;

}

方法4:动态规划法

既然我们可以把求最大的数对之差转换成求子数组的最大和,而子数组的最大和可以通过动态规划求解,那我们是不是可以通过动态规划直接求解呢?下面我们试着用动态规划法直接求数对之差的最大值。

我们定义diff[i]是以数组中第i个数字为减数的所有数对之差的最大值(0<=i<n)。

则有diff[i+1] = max(diff[i], maxi-array[i+1]),maxi表示数组array[0,…i]的最大值。

【代码】

C++
Code






1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

 

/*

     version: 1.0

     author: hellogiser

     blog: http://www.cnblogs.com/hellogiser

     date: 2014/5/24
*/


// max diff using dynamic programming

// diff[i]: maxDiff ending at array[i] (0<=i<n)

// maxi: max value of array[0,...i]

// diff[i+1] = max (diff[i],maxi-array[i+1])

int MaxDiff_DP(int *numbers, int length)

{
    // Tn=O(n)
    if(NULL == numbers || length < 2)

        return INT_MIN;

    int maxi = numbers[0];
    int maxDiff = INT_MIN;

    for (int i = 1; i < length; i++)

     {

        if(maxi < numbers[i - 1])

             maxi = numbers[i - 1];

        if(maxDiff < maxi - numbers[i])

             maxDiff = maxi - numbers[i];

     }
    return maxDiff;

}


【总结】

方法1:蛮力法】时间复杂度为O(n2),空间复杂度为O(1)。

方法2:分治法】时间复杂度为O(n),空间复杂度为O(1)。
由于该方法基于递归实现,因此会有额外的时间、空间消耗。

方法3:转化法】时间复杂度为O(n),空间复杂度为O(n)。

方法4:动态规划法】时间复杂度为O(n),空间复杂度为O(1)。该方法则没有额外的时间、空间消耗,并且它的代码是最简洁的,因此这是最值得推荐的一种解法。

【参考】

http://zhedahht.blog.163.com/blog/static/2541117420116135376632/

http://www.cnblogs.com/python27/archive/2011/12/01/2270724.html

【本文链接】

http://www.cnblogs.com/hellogiser/p/maximum-difference-of-array.html

57. 数对之差的最大值:4种方法详解与总结[maximum difference of array],布布扣,bubuko.com

时间: 2024-08-05 04:25:32

57. 数对之差的最大值:4种方法详解与总结[maximum difference of array]的相关文章

数对之差的最大值 &amp;&amp; 子数组的最大和

问题1: 在数组中,数字减去他的右边的数字得到一个数对之差,求所有数对之差的最大值. 例如数组{2.4.1.16.7.5.11.9}中,数对之差的最大值是11,是16减去5的结果. 问题2:给定一个含有n 个元素的数列,元素有正有负,找出和最小的一组相邻的书,既给定a[n],是的a[i]+a[i+1]+...+a[j]的和最小. 先看第一道题目: 如果从头遍历,遍历到某一个位置,从这个位置开始遍历到数组结束,看此元素的数对只差的最带值,然后更行当前的数对之差的最大值,O(n^2)的事件复杂度.

JS判断最大值4种方法

//**************常规用法************************** var ary=[100,23,78,90,80]; var sum=0; for(var i=0;i<ary.length;i++) { if(ary[i]>sum){ sum=ary[i]; } } alert(sum); //***************apply方法*************************// 最大数var a=[123,1,2,345];var ma4 = Mat

数对之差最大值

在数组中,数字减去它右边的数字得到一个数对之差,求所有数对之差的最大值.例如在数组{2, 4, 1, 16, 7, 5, 11, 9}中,数对之差的最大值是11,是16减去5的结果.分析上面的例子,数对之差最大值是16-5,而对于5来说,16是它左边子数组中的最大值,那么可以这样做,顺序遍历数组a,找到当前下标为i的元素左子数组的最大值,用此最大值减去当前a[i]元素,记做当前数对差值,再与之前记录的数对差值作比较判断是否进行更新,代码实现如下: 1 int maxDiff(int *arr,

笔试算法题(26):顺时针打印矩阵 &amp; 求数组中数对差的最大值

出题: 输入一个数字矩阵,要求从外向里顺时针打印每一个数字: 分析: 从外向里打印矩阵有多重方法实现,但最重要的是构建合适的状态机,这样才能控制多重不同的操作: 注意有四种打印模式(左右,上下,右左,下上),所以需要一个index变量控制每次循环时执行的打印模式: 注意水平打印和垂直打印分别需要两个变量控制打印元素,并且两组变量中的两个端点都是相互靠近的(hs和he,vs和he),每执行一种打印模式之前,需要更新当前打印模式中打印方向的其实坐标,因为它已经在上一种打印模式中打印过: 每一种打印模

求数组的最小数、最大值,求一组数的平均数,sort函数详解,类数组转数组

求数组的最小值和最大值 1 //求数组当中最大值和最小值 2 var arr=[3,2,6,1,45,23,456,23,2,6,3,45,37,89,30]; 3 //第一种方法 根据排序方法来求最大值和最小值 从小到大排序 第0位就是最小值 最后一位就是最大值 4 arr.sort(function(a,b){ 5 return a-b; //按从小大的情况排序 6 //return b-a; 按从大到小的情况排序 7 }) 8 console.log(arr); 9 var min=arr

求数组中两个元素差的最大值

找到最大值找到最小值,然后相减即可. 1.如果是说,前面的减去后面的,差的最大值? 2.如果是说,后面的减去前面的,差的最大值? 这两个问题就不一样了. 基础的想法可以用暴力.但是有更好的方法. 针对1,从后往前遍历数组,记录遍历过的数组元素的最小值,用当前元素相减. 针对2,从前往后遍历数组,记录遍历过的数组元素的最小值,用当前元素相减.

Codeforces Round #283 (Div. 2) A. Minimum Difficulty【一个数组定义困难值是两个相邻元素之间差的最大值。 给一个数组,可以去掉任意一个元素,问剩余数列的困难值的最小值是多少】

A. Minimum Difficulty time limit per test 2 seconds memory limit per test 256 megabytes input standard input output standard output Mike is trying rock climbing but he is awful at it. There are n holds on the wall, i-th hold is at height ai off the g

sar-iostat-vmstat-参数详解

sar sar 是分析系统性能的重要工具之一,通过sar指令可以全面地获取系统的CPU运行队列.磁盘I/O.分页(交换分区).内存.CPU 中断网络等性能数据. sar [options] [-o filename] [interval count] options:  -A:显示系统所有资源设备,CPU.内存.磁盘 的运行状态.  -u:显示系统所有CPU 采样的负载.  -P:显示当前系统中指定CPU的使用情况  -d:显示系统所有硬盘设备在采样时间内的使用状况.  -r:显示系统内存在采样

【找数组元素最大值的十四种方法】for【练习知识点和总结】

以下是心血成果,版权所有,未经允许,不得转载.作者:李金涛:FROM:光环国际 AT:201712172024.(方便以后,再次修改) <script> // 比较数组中数值的大小是比较常见的操作,比较大小的方法有多种,下面来介绍如下十四种方法,原理代码如下: // 1,排序取值:sort比较器排序(常用),以及冒泡排序(不常用,但方法思想很重要);// 2,假设比较取大值:假设max=arr[0];index=0; 在循环往后比较,如果有比max大的数就让max记录下大的数,索引赋给inde