Java分治算法堆栈溢出错误

Posted

技术标签:

【中文标题】Java分治算法堆栈溢出错误【英文标题】:Java divide and conquer algorithm stack overflow error 【发布时间】:2019-02-02 16:47:33 【问题描述】:

我正在尝试使用分治法找出 int 数组中最小数字的索引,但出现此堆栈溢出错误:

Exception in thread "main" java.lang.***Error
    at java.lang.StrictMath.floor(Unknown Source)
    at java.lang.Math.floor(Unknown Source)

这是我的分而治之方法:

private static int dC(int[] a, int f, int l) 
        if(f == 1)
            return f;
        if(a[dC(a, f, (int)(Math.floor((double)(f+l)/2)))] > a[dC(a, (int)(Math.floor((double)(f+l)/2)+1), l)])
            return dC(a, (int)(Math.floor((double)(f+l)/2)+1), l);
        else
            return dC(a, f, (int)(Math.floor((double)(f+l)/2)));
    

这是我在 main 方法中放入的内容:

int[] a = 35,30,40,50;

System.out.println(dC(a, 0, 3));

【问题讨论】:

只需检查二进制搜索的源代码,并以此为基础编写您自己的代码。 不相关的:使用有意义的名称。 a、f、l,它们都表示nothing。你写代码不是为了让编译器开心,你写代码是为了人类可以阅读和理解它。 整数除法 (7 / 2 == 3) 没有 floor(double) 演员更适合。 (int)(Math.floor((double)(f+l)/2))(f+l)/2 相同(假设f+l 为非负数)。 【参考方案1】:

你的停止“规则”有问题

private static int dC(int[] a, int f, int l) 
        if(l == f) // <-- This mean you have one item, so you want to return it.
            return f;
        if(a[dC(a, f, (int)(Math.floor((double)(f+l)/2)))] > a[dC(a, (int)(Math.floor((double)(f+l)/2)+1), l)])
            return dC(a, (int)(Math.floor((double)(f+l)/2)+1), l);
        else
            return dC(a, f, (int)(Math.floor((double)(f+l)/2)));
    

另外,我会尝试只计算一次,所以像这样(也是 Joop Eggen 所说的整数算术):

private static int dC(int[] a, int f, int l) 
        if(l == f)
            return f;
        int m = (f+l) / 2;
        int left = dC(a, f, m);
        int right = dC(a, m+1, l);
        if(a[left] > a[right])
            return left;
        else
            return right;
    

【讨论】:

哦,是的,我发现我放的是 (f == 1) 而不是 (f == l)【参考方案2】:

这只是经典的二分搜索问题。从我可以通过查看您的代码收集到的信息来看,您似乎陷入了用于对当前数组的左右子数组进行每次递归调用的逻辑中。我在下面使用的逻辑是将左递归从 start 到 (start+end)/2 的所有内容,以及从 ((start+end)/2) + 1 到 end 的所有内容进行右递归。这保证了永远不会有任何重叠。

当算法发现自己​​位于数组中的单个条目上时,就会发生基本情况。在这种情况下,我们只是返回该值,我们不会进一步递归。

private static int dC(int[] a, int start, int end) 
    if (start == end) return a[start];

    int left = dC(a, start, (start+end)/2);
    int right = dC(a, ((start+end)/2) + 1, end);

    return left < right ? left : right;


public static void main(String args[])

    int[] a = 10, 3, 74, 0, 99, 9, 13;
    System.out.println(dC(a, 0, 6));   // prints 0

Demo

注意:我不知道Math.floor 将在这里扮演什么角色,因为您使用的是 整数 数字数组,而不是双精度数或浮点数。我删除了它,因为我认为不需要它。

【讨论】:

【参考方案3】:

定位到min/max的索引是一个典型的问题,你可以试试:

public static void main(String... args) 
    int[] arr = generateArrays(100, 1000, 0, 10000, true);
    int minIndex = findMinIndex(arr, 1, arr.length - 1);
    int theMin = arr[minIndex];
    Arrays.sort(arr);
    System.out.println(String.format("The min located correctly: %s", arr[0] == theMin));


private static int findMinIndex(int[] a, int l, int r) 
    if (r - l < 1) return l;
    int mid = l + (r - l) / 2;
    int lIndex = findMinIndex(a, l + 1, mid);
    int rIndex = findMinIndex(a, mid + 1, r);
    int theMinIndex = l;
    if (a[lIndex] < a[theMinIndex]) theMinIndex = lIndex;
    if (a[rIndex] < a[theMinIndex]) theMinIndex = rIndex;
    return theMinIndex;

以及生成随机数组的助手。

public static int[] generateArrays(int minSize, int maxSize, int low, int high, boolean isUnique) 
    Random random = new Random(System.currentTimeMillis());
    int N = random.nextInt(maxSize - minSize + 1) + minSize;
    if (isUnique) 
        Set<Integer> intSet = new HashSet<>();
        while (intSet.size() < N) 
            intSet.add(random.nextInt(high - low) + low);
        
        return intSet.stream().mapToInt(Integer::intValue).toArray();
     else 
        int[] arr = new int[N];
        for (int i = 0; i < N; ++i) 
            arr[i] = random.nextInt(high - low) + low;
        
        return arr;
    

【讨论】:

以上是关于Java分治算法堆栈溢出错误的主要内容,如果未能解决你的问题,请参考以下文章

[算法]:分治法-求大整数相乘

Day590.分治算法 -数据结构和算法Java

分治算法在子列表中找到两个匹配的元素

递归分治算法之二维数组二分查找(Java版本)

算法笔记_065:分治法求逆序对(Java)

「五大常用算法」一文搞懂分治算法