【问题标题】:Find the minimum of a sorted and shifted array with better than O(n) time complexity找到具有优于 O(n) 时间复杂度的排序和移位数组的最小值
【发布时间】:2019-10-12 06:56:46
【问题描述】:

我们有一个任务是搜索一个有序数组的最小元素,然后向右移动。例如:[1, 5, 6, 19, 56, 101] 变为 [19, 56, 101, 1, 5, 6]。该方法应该使用分治算法来实现,并且它应该具有比 O(n) 更好的渐近时间复杂度。 编辑:我忘了补充说数组中的元素是唯一的。

我已经实现了一个方法,想问问这是否比 O(n) 更好,是否有办法改进我的方法。

public class FindMinimum {
    public void findMinimum(int[] arr) {

        // the recursive method ends when the length of the array is smaller than 2
        if (arr.length < 2) {
            return;
        }

        int mid = arr.length / 2;

        /*
         * if the array length is greater or the same as two, check if the middle
         * element is smaller as the element before that. And print the middle element
         * if it's true.
         */
        if (arr.length >= 2) {
            if (arr[mid - 1] > arr[mid]) {
                System.out.println("Minimum: " + arr[mid]);
                return;
            }
        }

        /*
         * separate the array in two sub-arrays through the middle and start the method
         * with those two arrays again.
         */
        int[] leftArr = new int[mid];
        int[] rightArr = new int[arr.length - mid];
        for (int i = 0; i < mid; i++) {
            leftArr[i] = arr[i];
        }
        for (int i = mid; i < arr.length; i++) {
            rightArr[i - mid] = arr[i];
        }
        findMinimum(leftArr);
        findMinimum(rightArr);
    }
}

【问题讨论】:

  • 这至少具有 O(n) 时间复杂度,因为您正在复制数组。另外:您不想同时下降到左右子数组中。
  • 如果移位为零(即输入只是一个排序数组),这会起作用吗?
  • @Andy Turner 你是对的,我会尝试在不复制完整数组且只创建一个子数组的情况下更改方法,但我还想不出解决方案。不,如果班次为 0,这将不起作用,我也会尝试为此实施解决方案。谢谢。

标签: java recursion divide-and-conquer


【解决方案1】:

在 Java 中,您可以使用列表,因为您可以创建子列表。

private Integer findMinimum(List<Integer> list) {
    if (list.size() < 2)
        return list.get(0);

    int mid = list.size() / 2;

    // create left and right list
    List<Integer> leftList = list.subList(0, mid);
    List<Integer> rightList = list.subList(mid, list.size());

    if (leftList.get(leftList.size() - 1) <= rightList.get(rightList.size() - 1))
        return findMin(leftList);
    else
        return findMin(rightList);
}

当您使用 Java 创建子列表时,没有副本。所以创建一个新的子列表需要 O(1) 的复杂度。 因此该函数的复杂度为 O(logn)。

【讨论】:

    【解决方案2】:

    这是我的新解决方案,无需在任何地方复制数组。

    public class FindMinimum {
    
        public void findMinimum(int[] arr) {
            findMinimumSub(arr, 0, arr.length - 1, 2);
        }
    
        private void findMinimumSub(int[] arr, int start, int end, int size) {
            // the recursive method ends when the length of the array is smaller than 2
            if ((end - start) < 2) {
                if (arr[end] > arr[start])
                    System.out.println("Minimum: " + arr[start]);
                else
                    System.out.println("Minimum: " + arr[end]);
                return;
            }
    
            int mid = arr.length / size;
    
            if (arr[start] > arr[end]) {
                // right side
                start += mid;
                findMinimumSub(arr, start, end, size * 2);
            }
            else {
                // left side
                findMinimumSub(arr, start, mid, size * 2);
            }
        }
    }
    

    【讨论】:

    • 我想你会发现这个解还是O(n)。考虑到数组有 256 个元素,O(log n) = 8。但是,实现的每一步都会复制剩余数组的一半,因此第一个递归调用有 128 个副本,第二轮有 64 个副本,以此类推。您的复杂性/成本函数遵循递归方程f(n)=c.n/2+f(n/2)。上述方程的解是 O(n)。您需要做的是使每次迭代的时间恒定,同时将数组除以 2。然后 f(n)=c+f(n/2) 暗示 O(log n) 解决方案。基本上,解决方案不能有任何数组的复制。
    • @TSG 非常感谢你,你是对的。我会再试一次。
    猜你喜欢
    • 1970-01-01
    • 2023-01-16
    • 1970-01-01
    • 1970-01-01
    • 2013-12-08
    • 1970-01-01
    • 2016-06-21
    • 2021-08-30
    • 1970-01-01
    相关资源
    最近更新 更多