递归除数组

时间:2015-10-19 06:44:29

标签: java algorithm recursion time-complexity

我需要获得许多可能的方法将数组划分为小的子数组。我们可以将数组分为verticaly和horizo​​ntaly。我的算法非常好,但时间复杂度太差了。你能看看如何改进吗?

参数

nStart - 第一行子数组

nEnd - 子阵列的最后一行

mStartmEnd - 用于第二维(列)。

check() - 检查结束条件的函数

return - 分割数组的不同方法的数量。我们除了功能检查返回true

public static long divide(int nStart, int nEnd, int mStart, int mEnd) {
    long result = 0;

    for(int i = 1; i < nEnd - nStart; i++) {
        if(check(nStart, nStart + i, mStart, mEnd) && check(nStart + i, nEnd, mStart, mEnd))
            result += divide(nStart, nStart + i, mStart, mEnd) * divide(nStart + i, nEnd, mStart, mEnd);
    }

    for(int i = 1; i < mEnd - mStart; i++) {
        if(check(nStart, nEnd, mStart, mStart + i) && check(nStart, nEnd, mStart + i, mEnd)) 
            result += divide(nStart, nEnd, mStart, mStart + i) * divide(nStart, nEnd, mStart + i, mEnd);
    }

    return (result == 0 ? 1 : result) % 1000000000; 
}

示例

输入

2 2 10 01

输出 2

输入

3 2 101 010

输出 5

我认为你需要知道check()函数是如何工作的。当下一个子阵列只有一个或只有零时,我们停止分裂。这是代码:

public static boolean check(int nStart, int nEnd, int mStart, int mEnd) {
    if((nEnd - nStart) + (mEnd - mStart) == 2) 
        return false;
    for(int i = mStart; i < mEnd; i++) {
        for(int j = nStart; j < nEnd; j++) {
            if(bar[i][j] != bar[mStart][nStart])
                return true;
        }
    }
    return false;
}

1 个答案:

答案 0 :(得分:1)

通过查看代码,我可以看到在递归的每个步骤中,您将二维数组划分为两个数组,其中包含一个水平或垂直切割。然后验证这两个部分是否满足check-method定义的某些条件,如果是,则将这两部分放入递归中。当递归不能再继续时,你返回1.下面我假设你的算法总是产生你想要的结果。

我担心这种算法的有效优化很大程度上取决于检查条件的作用。在琐碎的情况下,当问题崩溃成一个直截了当的数学问题时,它总是会返回真实的,这个问题可能具有一般的非递归解决方案。稍微复杂但仍然有效地可解决的情况是条件仅检查阵列的形状,这意味着例如check(1,5,1,4)将返回与check(3,7,5,8)相同的结果。

最复杂的当然是一般解决方案,其中检查条件可以是任何东西。在这种情况下,没有太多可以做的来优化您的暴力解决方案,但我想到的一件事是为您添加一个内存算法。您可以使用java.awt.Rectangle类(或创建自己的类)来保存子数组的维度,然后使用java.util.HashMap存储divide-method执行结果for furure参考,如果使用相同的参数再次调用该方法。这样可以防止可能发生的重复工作。

因此,您将haspmap定义为您类中的静态变量:

static HashMap<Rectangle,Long> map = new HashMap<Rectangle,Long>();

然后在除法方法的开头添加以下代码:

Rectangle r = new Rectangle(nStart,mStart,nEnd,mEnd);
Long storedRes = map.get(r);
if (storedRes != null) {
    return storedRes;
}

然后将方法的结尾更改为表单:

result = (result == 0 ? 1 : result) % 1000000000;
map.put(r, result);
return result;

这可以为您的算法带来性能提升。

要回到我之前的判断,如果检查条件足够简单,则可以更有效地完成相同的优化。例如,如果你的检查条件只检查数组的形状,你只需要将它的宽度和高度作为地图的一个关键,这将减少地图的大小,并减少其中的正数命中数。