算法导论的C实现——分治法求最大子数组

本文详细介绍了使用分治法求解最大子数组问题的C语言实现过程,包括核心算法的伪代码转换、关键函数的定义及其实现细节。通过对不同数组实例的测试,验证了算法的有效性和正确性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

算法导论的C实现——分治法求最大子数组


最近打算开启新篇,仔细啃一下算法导论这本书,为以后读博打下一点基础。希望能够尽可能实现书中伪代码与历程,并且希望自己能够坚持到最后。

//main.cpp

#include <stdio.h>
#include <stdint-gcc.h>
#include <math.h>
#include "partition.h"

int main() {
    //-------------------- maximum subarray ------------------//
    //simple example
    const int Array1[11] = {-1, 10, 5, 6, -15, 2, 98, -45, 65, -65, 10};
    //4.1-1 数组中全是负数: 返回绝对值最小数
    const int Array2[11] = {-989, -10, -5, -6, -15, -2, -98, -45, -65, -89, -10};
    testFIND_MAXIMUM_SUBARRAY(Array1, 0, 10);
    //4.1-5
    testEXTEND_FIND_MAX_SUBARRAY(Array1, 11);

    return 0;
}

//partition.cpp
//
// Created by king98 on 19-4-28.
//

#include <stdio.h>
#include <stdint-gcc.h>
#include <math.h>
#include "partition.h"

//-------------------- maximum subarray ------------------//
//TEST FUNCTION
void testFIND_MAXIMUM_SUBARRAY(const int *A, uint32_t low, uint32_t high)
{
    retFIND_MAXIMUM_SUBARRAY testFIND_MAXIMUM_SUBARRAY = FIND_MAXIMUM_SUBARRAY(A, low, high);
    printf("left:%d\n", testFIND_MAXIMUM_SUBARRAY.low);
    printf("right:%d\n", testFIND_MAXIMUM_SUBARRAY.high);
    printf("sum:%d\n", testFIND_MAXIMUM_SUBARRAY.sum);
}
//FIND_MAXIMUM_SUBARRAY(A,low,high)
struct retFIND_MAXIMUM_SUBARRAY FIND_MAXIMUM_SUBARRAY(const int *A, uint32_t low, uint32_t high)
{
    struct retFIND_MAXIMUM_SUBARRAY ret{}, left{}, right{}, cross{};

    if(low==high)
    {
        ret={low, high, A[low]};
        return ret;
    }
    else
    {
        uint32_t mid = (uint32_t) floor((low+high)/2);
        left = FIND_MAXIMUM_SUBARRAY(A, low, mid);
        right = FIND_MAXIMUM_SUBARRAY(A, mid+1, high);
        cross = FIND_MAX_CROSSING_SUBARRAY(A, low, mid, high);
        //compare left, right and crossing subarray
        if(left.sum>right.sum && left.sum>cross.sum)
        {
            return left;
        }
        else if(right.sum>left.sum && right.sum>cross.sum)
        {
            return right;
        }
        else
        {
            return cross;
        }
    }
}
//FIND_MAX_CROSSING_SUBARRAY(A,low,mid,high)
//DESC: 找到跨越中点的最大子数组
//ARGS: A--目标数组
//      low--子数组头
//      mid--子数组中点下标
//      high--子数组尾
//RETS:
struct retFIND_MAXIMUM_SUBARRAY FIND_MAX_CROSSING_SUBARRAY(const int *A, uint32_t low, uint32_t mid, uint32_t high)
{
    struct retFIND_MAXIMUM_SUBARRAY ret{};
    //calculate left sum subarray
    int left_sum = negINFINITY;
    int sum = 0;
    uint32_t max_left = 0;
    for(uint32_t i=mid; i>=low && i<=mid;i--)
    {
        sum += A[i];
        if(sum>left_sum)
        {
            left_sum = sum;
            max_left = i;
        }
    }
    //calculate right sum subarray
    int right_sum = negINFINITY;
    sum = 0;
    uint32_t max_right = 0;
    for(uint32_t i=mid+1;i<=high;i++)
    {
        sum += A[i];
        if(sum>right_sum)
        {
            right_sum = sum;
            max_right = i;
        }
    }

    ret={max_left, max_right, left_sum+right_sum};
    return ret;
}

//4.1-5
//TEST FUNCTION
void testEXTEND_FIND_MAX_SUBARRAY(const int *A, uint32_t length)
{
    retEXTEND_FIND_MAX_SUBARRAY testEXTEND_FIND_MAX_SUBARRAY{};
    testEXTEND_FIND_MAX_SUBARRAY = EXTEND_FIND_MAX_SUBARRAY(A, length);
    printf("left:%d\n", testEXTEND_FIND_MAX_SUBARRAY.low);
    printf("right:%d\n", testEXTEND_FIND_MAX_SUBARRAY.high);
    printf("sum:%d\n", testEXTEND_FIND_MAX_SUBARRAY.sum);
}
//EXTEND_FIND_MAX_SUBARRAY
struct retEXTEND_FIND_MAX_SUBARRAY EXTEND_FIND_MAX_SUBARRAY(const int *A, uint32_t length)
{
    //A[0,...0]
    retEXTEND_FIND_MAX_SUBARRAY ret={0, 0, A[0]};

    int sum;
    for(uint32_t j=1; j<length; j++)
    {
        for(uint32_t i=0; i<=j; i++)
        {
            sum = 0;
            for(uint32_t k=i; k<=j; k++)
            {
                sum += A[k];
            }
            if(sum>ret.sum)
            {
                ret.sum = sum;
                ret.low = i;
                ret.high = j;
            }
        }
    }
    return ret;
}
//partition.h
//
// Created by king98 on 19-4-28.
//

#ifndef TEST_DIVIDE_H
#define TEST_DIVIDE_H

//int == INT32
#define negINFINITY INT32_MIN
#define posINFINITY INT32_MAX

#include <stdio.h>
#include <stdint-gcc.h>
#include <math.h>

//-------------------- maximum subarray ------------------//
    //RETURN STRUCTURE
    struct retFIND_MAXIMUM_SUBARRAY{
        uint32_t low;
        uint32_t high;
        int sum;
    };
    //FUNCTION
    void testFIND_MAXIMUM_SUBARRAY(const int *A, uint32_t low, uint32_t high);
    struct retFIND_MAXIMUM_SUBARRAY FIND_MAXIMUM_SUBARRAY(const int *A, uint32_t low, uint32_t high);
    struct retFIND_MAXIMUM_SUBARRAY FIND_MAX_CROSSING_SUBARRAY(const int *A, uint32_t low, uint32_t mid, uint32_t high);

    //4.1-5
    //RETURN STRUCTURE
    struct retEXTEND_FIND_MAX_SUBARRAY{
        uint32_t low;
        uint32_t high;
        int sum;
    };
    //FUNCTION
    void testEXTEND_FIND_MAX_SUBARRAY(const int *A, uint32_t length);
    struct retEXTEND_FIND_MAX_SUBARRAY EXTEND_FIND_MAX_SUBARRAY(const int *A, uint32_t length);

#endif //TEST_DIVIDE_H

### 使用分治法解决最大子数组问题 #### 最大子数组问题概述 最大子数组问题是寻找给定一维整数数组中的连续子数组,使得该子数组的和达到最大值。这个问题可以通过多种方法解决,其中一种高效的方式就是使用分治法。 #### 分治法的核心思想 分治法通过将原问题划分为若干个较小的子问题来逐步解决问题。对于最大子数组问题,可以将其划分为三个可能的情况: 1. **左半部分的最大子数组**:完全位于数组中间位置左边的部分。 2. **右半部分的最大子数组**:完全位于数组中间位置右边的部分。 3. **跨越中间位置的最大子数组**:包含中间位置及其两侧元素的子数组。 最终的结果将是这三个情况中的最大值[^1]。 #### 跨越中间位置的最大子数组计算 为了找出跨越中间位置的最大子数组,可以从中间位置向左扩展找到最大的前缀和,再从中间位置向右扩展找到最大的后缀和,两者相加即为跨越中间位置的最大子数组之和。 以下是详细的伪代码描述: ```python def find_max_crossing_subarray(arr, low, mid, high): # 初始化左侧最大和 left_sum = float('-inf') sum_ = 0 max_left = mid # 计算从中间向左的最大和 for i in range(mid, low - 1, -1): sum_ += arr[i] if sum_ > left_sum: left_sum = sum_ max_left = i # 初始化右侧最大和 right_sum = float('-inf') sum_ = 0 max_right = mid + 1 # 计算从中间向右的最大和 for j in range(mid + 1, high + 1): sum_ += arr[j] if sum_ > right_sum: right_sum = sum_ max_right = j return (max_left, max_right, left_sum + right_sum) ``` #### 完整的分治算法实现 完整的分治算法基于递归方式实现,具体如下所示: ```python def find_maximum_subarray(arr, low, high): if high == low: # 基本情况:只有一个元素 return (low, high, arr[low]) else: mid = (low + high) // 2 # 左侧最大子数组 (left_low, left_high, left_sum) = find_maximum_subarray(arr, low, mid)[^4] # 右侧最大子数组 (right_low, right_high, right_sum) = find_maximum_subarray(arr, mid + 1, high) # 跨越中间的最大子数组 (cross_low, cross_high, cross_sum) = find_max_crossing_subarray(arr, low, mid, high) if left_sum >= right_sum and left_sum >= cross_sum: return (left_low, left_high, left_sum) elif right_sum >= left_sum and right_sum >= cross_sum: return (right_low, right_high, right_sum) else: return (cross_low, cross_high, cross_sum) # 主函数调用 arr = [-2, 1, -3, 4, -1, 2, 1, -5, 4] result = find_maximum_subarray(arr, 0, len(arr) - 1) print(f"Maximum subarray is from index {result[0]} to {result[1]}, with a sum of {result[2]}") ``` 这段代码实现分治法最大子数组的过程,并返回了起始索引、结束索引以及对应的子数组和。 --- ### 时间复杂度分析 分治法的时间复杂度主要由以下几个因素决定: - 划分子问题的操作需要 \(O(1)\) 的时间。 - 解决两个子问题需要 \(T(n/2)\),因为每次都将问题规模减半。 - 找到跨越中间的最大子数组需要线性扫描整个数组的一半,因此时间为 \(O(n)\)。 综合来看,总的时间复杂度为 \(T(n) = 2T(n/2) + O(n)\),根据主定理可得其时间复杂度为 \(O(n \log n)\)[^3]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值