【问题标题】:Recursion - if there is continuity of numbers in java递归 - 如果java中的数字是连续的
【发布时间】:2016-06-11 20:42:13
【问题描述】:

我有一个练习,我遇到了错误,很想帮助解决错误以及如何解决练习。

编写一个递归函数,接收一组数字和一个数字 9-1 之间。如果存在连续性,则该函数返回 true numbers 从 1 到数字,否则返回 false。

它总是带给我真实的,没有错误。

例子:

对于数组3,1,2,3,4,6,3和数字4返回到true

对于数组3,1,2,1,2,3,5 和数字4 返回false

代码:

public static boolean continuityOfnumbers(int[] arr, int n) {
    int counter = 0;
    int index = arr.length - 1;

    if (n == 0)
            return true;

    if (arr[index] - 1 == arr[index - 1])
        counter += 1;

    index--;

    return continuityOfnumbers(arr, n - 1);
}

【问题讨论】:

  • 查看您的代码:它所做的只是return true; 或将递归调用的结果返回给它自己。现在问:既然它只返回true 或调用自身的结果,它怎么可能返回false
  • 快速建议:您传递一个变量n 并将其用于递归的“返回真”停止条件。但是,您永远不会将它用于递归步骤。尝试删除counterindex,然后使用更多n
  • 你更大的问题是你只看数组中的最后一个元素。您每次都需要做以下两件事之一:将“开始”点(1 应该在的位置)向前移动,或者查看之后的下一个元素,增加比较值。
  • 我不确定“数字从1到数字的连续性”的解释。这些示例并未完全涵盖它。这是否意味着数组中某处必须有一个连续的1,2,3,4 序列,还是仅仅意味着数字1234 必须出现在某处在数组中?您的代码似乎表明了第一个选项。
  • 你问了,收到了三个回复,却从未选择答案或给 cmets,这被认为是粗鲁的

标签: java recursion


【解决方案1】:

这是一个O(n) 运行时和存储复杂性解决方案。它创建一个布尔连续性数组并检查它和递归的结束。

不要只是复制粘贴,一定要阅读代码中的cmets,看看其中的逻辑是什么,并确保你理解它。

它还进行了一些预验证检查,以确保 n 在进行递归之前实际上包含在数组中并且可以访问。

private static boolean continuityOfnumbers(int[] arr, int n) {
    // Max sure we can reach N
    if (n >= arr.length) return false;

    // Make sure N is in the array
    boolean findN = false;
    // Also find the max value in the array
    int max = Integer.MIN_VALUE;
    for (int x : arr) {
        if (x == n) findN = true;
        if (max < x) max = x;
    }
    // If N is not found or we still cant reach N, return false
    if (!findN || n > max) return false;

    // Build a continuity array of all false
    boolean[] contArr = new boolean[n];

    // Start recursion
    return continuityOfnumbers(arr, n, contArr, 0);
}

private static boolean continuityOfnumbers(int[] arr, int n, boolean[] contArr, int index) {
    // Base case - we reached the end of the array
    if (index >= arr.length) {
        // Check that all values up to N are true
        for (int i = 0; i < n; i++) {
            if (!contArr[i]) return false;
        }
        // Here, we know the values are continuous
        return true;
    }

    // Set the index of the continuity array to a non-zero value
    int val = arr[index];
    if (val <= n && !contArr[val-1]){
        contArr[val-1] = true;
    }

    // Recurse with the next index
    return continuityOfnumbers(arr, n, contArr, index+1);
}

对于函数3, 1, 2, 3, 4, 6, 3n = 4,这是一个示例调试运行,其中0falsetrue1

start:  [0, 0, 0, 0]
Set 3:  [0, 0, 1, 0]
Set 1:  [1, 0, 1, 0]
Set 2:  [1, 1, 1, 0]
Set 4:  [1, 1, 1, 1]
true

n = 6 相同的数字(应该返回false,因为5 不存在)。

start:  [0, 0, 0, 0, 0, 0]
Set 3:  [0, 0, 1, 0, 0, 0]
Set 1:  [1, 0, 1, 0, 0, 0]
Set 2:  [1, 1, 1, 0, 0, 0]
Set 4:  [1, 1, 1, 1, 0, 0]
Set 6:  [1, 1, 1, 1, 0, 1]
false

【讨论】:

  • 由于您只需要检查数字 1-4(索引 0-3)是否存在,因此无需创建大小为 6 的数组。大小为 4 就足够了,因为您只关心存在,boolean 数组比 int 0 和 1 值数组更合适,例如boolean[] found。 --- 既然您对其他答案的评论中的性能非常感兴趣,为什么总是迭代整个列表,一旦检测到所有 4 个值都存在就可以停止扫描?
  • boolean[] 的好点,我想我被困在 C 模式下。想到n的值一看到就早点回来,只是没想过要不要走得更远。而且我想我最多只能扫描 3 次,并非总是如此?
【解决方案2】:

正如其他人所说,您的解决方案的问题是,您永远不会返回 false,因此您总是收到 true。

这是因为当您找到起始编号但序列不完整时,您从未正确处理此案例。您需要重新设置搜索值,然后从失败的位置重新开始。

在下面的代码中,set 是您正在检查的数字集,initial 是序列的起始数字。 search 是下一个要搜索的数字(序列中的下一个数字)。 lastIdx 是数组中的索引,我们正在查看。

停止条件是到达序列的结尾(成功,搜索 == 1 在集合中的 lastIdx 处)或者如果 lastIdx 到达集合的结尾(失败,lastIdx

一个特殊的角色是step 变量。当您重置部分序列时,step 是必需的:您当前正在查看的位置需要再次检查,如果它是下一个序列的开始。如果您还没有开始序列,则索引已经检查过,您需要前进到下一个。

集合是从头到尾遍历的,因为您在方法中做了同样的事情。

boolean continuityOfNumber(int[] set, int initial, int search, int lastIdx) {
    if(lastIdx < 0)
        return false;

    if( set[lastIdx] == search ) {
        if( search == 1 )
            return true;

        return continuityOfNumber(set, initial, search - 1, lastIdx - 1);
    }

    int step = (initial == search) ? 1 : 0;

    return continuityOfNumber(set, initial, initial, lastIdx - step);
}


final int[] testSet = {0, 1, 2, 3, 4, 2, 3, 4, 1, 2, 3, 2, 0};
final int[] testSet2 = {0, 2, 3, 4, 2, 3, 4, 1, 2, 3, 2, 0};
assertTrue(con.continuityOfNumber(testSet, 4, 4, testSet.length-1));
assertFalse(con.continuityOfNumber(testSet2, 4, 4, testSet2.length-1));

【讨论】:

    【解决方案3】:

    您的示例代码始终返回 true,因为这是它返回的唯一值。基本上,您的方法使用n-1 递归调用自身,直到n = 0return true

    您需要将问题划分为以下子问题:

    号码n的连续性表示

    1. 号码n-1的连续性
    2. n 包含在数组中
    public static boolean continuityOfnumbers(Integer[] arr, int n) {
        if (n == 0)
            return true;
        return continuityOfnumbers(arr, n - 1) && contains(arr, n);
    }
    
    private static boolean contains(Integer[] arr, Integer n) {
        return Arrays.asList(arr).contains(n);
    }
    

    我使用 Integer[] 而不是 int[] 只是为了将 contains 部分写在一行中。这是关于代码的简单性,而不是效率。它是关于如何以简单的方式使用递归。

    但是,如果您的问题需要数字 1-n 有序,这不是正确的解决方案。

    【讨论】:

    • 我不认为这是解决方案。递归应该在找到数字时中断并返回 true,否则返回 false。不断调用 contains 使其成为 O(n^2) 解决方案
    • 我会颠倒返回语句的顺序,所以如果缺少一个数字,你会尽快停止回避。
    • 除了 n^2 问题之外,它实际上并不能确保数字是按顺序排列的。
    • @Clockwork-Muse 问题中的示例清楚地表明,数字不需要按顺序排列: 对于数组3,1,2,3,4,6,3 和数字4 返回到@987654331 @。该示例起作用的唯一方法是,测试只是验证数字 1-4 的存在。第二个示例失败,因为号码 4 不存在。
    • 问题使用int[],但您将其更改为Integer[],没有任何理由或解释。我的猜测是你可以这样做 Arrays.asList(arr).contains(n),但这只是懒惰,需要盒装数组的开销,所以你可以在一行中编写一个简单的 for 循环。
    猜你喜欢
    • 1970-01-01
    • 2015-09-08
    • 2018-02-09
    • 1970-01-01
    • 1970-01-01
    • 2021-04-23
    • 2016-07-14
    • 2019-02-13
    • 1970-01-01
    相关资源
    最近更新 更多