算法导论第四章 4.1

算法导论第四章

4.1 最大子数组问题

1-1

  • 返回数组中最大的元素

1-2

FIND-MAXIMUM-SUBARRAY(A, low, high)
1   Subscript_Low = low
2   Subscript_High = right
3   Subscript_Sum = -4   for i = low to high
5       for j = i to high
6           sum = sum + A[j]
7           if Subscript_Sum < sum
8               Subscript_Sum = sum
9               Subscript_Low = i
10              Subscript_High = j
11  return (Subscript_Low, Subscript_High, Subscript_Sum)

1-3

C++代码

constexpr int CROSSOVER_POINT = 37;

class Data{

private:

    int low;
    int high;    
    int sum;
    
public:

    // 构造函数列表初始化    
    Data(const int &L, const int &H, const int &S) : low(L), high(H), sum(S) {  };
    
    // 委托构造函数    
    Data(void) : Data(0, 0, 0) {  };
    
    // 返回sum变量    
    int Ret_Sum(void) { return sum; };
    
    // 其余的让编译器默认生成就行了
};

/* 暴力算法 */
Data Brute_Force_Approach(int *A, int low, int high)
{    
    Data Temporary(low, high, A[low]);
    
    for (int i = low; i < high; i++)    
    {        
        int sum = 0;
        for (int j = i; j < high; j++)        
        {            
            sum += A[j];                        
            if (sum > Temporary.Ret_Sum())                
                Temporary = { i, j, sum };
        }    
    }      
    
    return Temporary;
}

/* 分治算法 */
Data FIND_MAX_CROSSING_SUBARRAY(int *A, int low, int mid, int high)
{    
    int left_sum = A[mid];    
    int sum = 0;    
    int max_left = 0;
    for (int i = mid; i >= low; i--)    
    {        
        sum += A[i];
        if (sum >= left_sum)        
        {            
            left_sum = sum;            
            max_left = i;        
        }
    }
    
    int right_sum = A[mid + 1];    
    sum = 0;    
    int max_right = 0;
    for (int j = mid + 1; j <= high; j++)    
    {        
        sum += A[j];        
        if (sum >= right_sum)        
        {            
            right_sum = sum;            
            max_right = j;        
        }    
    }
    
    return { max_left, max_right, left_sum + right_sum };
}

Data Divide_And_Conquer_Solution(int *A, int low, int high)
{    
    if (high == low)        
        return { low, high, A[low] };        
    else    
    {        
        int mid = (low + high) / 2;        
        Data Tamporary_Left = Divide_And_Conquer_Solution(A, low, mid);        
        Data Tamporary_RIght = Divide_And_Conquer_Solution(A, mid + 1, high);        
        Data Tamporary_Cross = FIND_MAX_CROSSING_SUBARRAY(A, low, mid, high);    
    
        if (Tamporary_Left.Ret_Sum() >= Tamporary_RIght.Ret_Sum() && Tamporary_Left.Ret_Sum() >= Tamporary_Cross.Ret_Sum())        
            return Tamporary_Left;    
        else if (Tamporary_RIght.Ret_Sum() >= Tamporary_Left.Ret_Sum() && Tamporary_RIght.Ret_Sum() >= Tamporary_Cross.Ret_Sum())        
            return Tamporary_RIght;    
        else        
            return Tamporary_Cross;    
    }
}

/* 混合算法(混合暴力算法和分治算法) */
Data find_maximum_subarray_mixed(int *A, int low, int high)
{    
    if (high <= CROSSOVER_POINT)        
        Brute_Force_Approach(A, low, high);    
    else    
    {        
        int mid = (low + high) / 2;        
        Data Tamporary_Left = find_maximum_subarray_mixed(A, low, mid);        
        Data Tamporary_RIght = find_maximum_subarray_mixed(A, mid + 1, high);        
        Data Tamporary_Cross = FIND_MAX_CROSSING_SUBARRAY(A, low, mid, high);
        
        if (Tamporary_Left.Ret_Sum() >= Tamporary_RIght.Ret_Sum() && Tamporary_Left.Ret_Sum() >= Tamporary_Cross.Ret_Sum())            
            return Tamporary_Left;        
        else if (Tamporary_RIght.Ret_Sum() >= Tamporary_Left.Ret_Sum() && Tamporary_RIght.Ret_Sum() >= Tamporary_Cross.Ret_Sum())            
            return Tamporary_RIght;        
        else            
            return Tamporary_Cross;    
    }
}

1-4

1-5

参考理解算法导论此题答案后写的

个人理解: 该算法主旨再于,用一个 D a t a Data Data类数组来存储序列 0.. n 0..n 0..n的所有元素的和,然后进行比较,选出最大的。

例如:

   D a t a [ 0 ] . s u m Data[0].sum Data[0].sum存储的就是 A [ 0 ] A[0] A[0]的所有元素的和,当然, D a t a [ 0 ] Data[0] Data[0]还会存储下标,这里为 D a t a [ 0 ] . l o w = 0 Data[0].low = 0 Data[0].low=0 D a t a [ 0 ] . h i g h = 0 Data[0].high = 0 Data[0].high=0

   D a t a [ 10 ] . s u m Data[10].sum Data[10].sum存储的就是 A [ 0...10 ] A[0...10] A[0...10]的所有元素的和, D a t a [ 10 ] . l o w = 0 Data[10].low = 0 Data[10].low=0 D a t a [ 10 ] . h i g h = 10 Data[10].high = 10 Data[10].high=10

  如果单单只是这样,那肯定是选不出最大的子数组,所有真正的关键再于

     i f ( T a m p o r a r y [ i − 1 ] . R e t _ S u m ( ) < 0 ) if (Tamporary[i - 1].Ret\_Sum() < 0) if(Tamporary[i1].Ret_Sum()<0)

  因为有这条判断语句,所以在我们循环的往 D a t a [ i ] Data[i] Data[i]里面存储数据的时候,会进行对 D a t a [ i − 1 ] Data[i - 1] Data[i1] s u m sum sum数据进行判断是否小于 0 0 0,也就是对前面一个 D a t a Data Data数据元素的 s u m sum sum数据进行判断。

  它的意义再于:如果前面的数据加在一起,结果却小于 0 0 0,那么我们不再将前面的和与当前的数据相加,因为前面的数据加起来的结果小于 0 0 0,就算相加,也只是使得当前的数据变小,所以我们直接存储当前的数据与下标。

/* 线性算法 */
Data find_maximum_subarray(int *A, int low, int high)       // 传递参数为数组范围下标
{    
    Data *Tamporary = new Data[high - low + 1];    
    Tamporary[0] = { low, low, A[low]};        
    for (int i = 1; i <= high - low; i++)    
    {        
        if (Tamporary[i - 1].Ret_Sum() < 0)            
            Tamporary[i] = { i + low, i + low,  A[i + low]};        
        else            
            Tamporary[i] = { Tamporary[i - 1].Ret_Low(), i + low, A[i] + Tamporary[i - 1].Ret_Sum() };    
    }
    int sum = Tamporary[0].Ret_Sum();    
    int number = 0;    
    for (int i = 1; i <= high - low; i++)    
    {        
        if (Tamporary[i].Ret_Sum() >= sum)        
        {            
            sum = Tamporary[i].Ret_Sum();            
            number = i;        
        }       
    }
    return Tamporary[number];
}

算法导论4.1.5 标准答案

总结:我们构建一个数组 S S S,该数组包含以A的每个索引结尾的最大子数组。也就是说, S [ j ] S[j] S[j]包含以j结尾的最大子数组的信息。我们首先遍历输入以构建 S S S。然后,执行 他们在文字中提出的建议。 这是 n + n = 2 n = Θ ( n ) n + n = 2n =Θ(n) n+n=2n=Θ(n)

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值