【问题标题】:Sorting algorithms with array lists使用数组列表排序算法
【发布时间】:2019-12-11 15:59:22
【问题描述】:

我目前正在研究使用数组列表的排序算法。我在 GitHub 上看到一个项目,用更大数组中的新(更大)元素覆盖小数组中的第一个(最小)元素,然后对小数组进行排序。

这是提供的解决方案:

public int findLarger() throws IndexingError {
    int[] array = getArray();
    int k = getIndex();
    if (k <= 0 || k > array.length) {
        throw new IndexingError();
    }
    int[] smallArray = new int[k];
    for (int index = k; index < array.length; index++){
         if (array[index] > smallArray[0]){
             smallArray[0] = array[index];
             Arrays.sort(smallArray);
         }
    }
    return smallArray[0];
}

但我很难理解我创建的这种方法是否更“有效”,通过使用另一个变量而不是另一个数组?

public int findLarger() throws IndexingError {
    int[] array = getArray();
    int max = array[0];
    for (int i = 0; i < k; i++) {
        if (max < array[i]) {
            max = array[i];
        }
    } 
    return max;
}


public abstract class Search {
private int[] array; 
private int k; 

Search(int[] array, int k) {
    this.array = array;
    this.k = k;
}

public int[] getArray() {
    return array;
}
int getIndex() { return k; }

abstract public int findElement() throws IndexingError;
}

编辑:

if (array.length == 0 )
            throw new RuntimeException("Array can't be empty");

        int max = array[0];
        for (int i = 0; i < array.length; i++) {
            if (max < array[i]) {
                max = array[i];
            }
        } // end of obvious solution method
        return max;
    }

【问题讨论】:

  • 第一种方法解决了什么问题?我不认为第二种方法能解决同样的问题。
  • 我不认为提供的解决方案是有效的。因为它看起来像 O(N^2 * logN)。你能提供方法getArray()getIndex()的实现吗?
  • @kaya3 这两种方法本质上都是返回小数组中最大的元素,但处理方式不同,第一种方法只使用更大的数组,而第二种方法使用变量
  • @Naples 第一个方法循环遍历大数组中的最后 n-k 个元素,而第二个循环遍历前 k 个元素。很难看出他们怎么可能做同样的事情,因为他们甚至不使用输入的相同部分。
  • @Steyrix 刚刚用 getArray() 和 getIndex() 实现更新了问题

标签: java sorting


【解决方案1】:

第一个实现真的很糟糕,为什么一次排序(O n log n)甚至最差几次array.length-k 次,找到一个集合的最小值(O(n))真是太糟糕了。

所以是的,具有单个变量的版本,存储当前最小值是正确的方法。 (请注意使用array[0] 初始化您的最大值不会抵抗空输入)

另一方面,正如其他人评论的那样,这两种算法没有使用相同的单元格,因此目前无法比较。如果在您的第​​二个实现中,您像第一个一样从 k 迭代到 array.length,那么您确实会获得比第一个更好的实现。

【讨论】:

  • 同意,我也建议作者看看 QuickSort、CountSort、MergeSort 算法,而不是在这个概念上浪费时间。
  • 第一种方法没有找到数组的最小值。它似乎试图找到出现在索引 k 之后的第 k 个最大元素。
  • @Yann 我现在已经考虑了空输入的情况,并且已经从 array.length 进行了迭代,这会是一个更有效的解决方案吗?
  • 对不起,我看错了你的代码,意图不清楚,特别是因为你自己的算法正在计算一个最大值。查看其他答案和 Kaya 的有用见解。所以第一个算法实际上是在做一些没有找到最小值的事情。但是,它排序太多次,正如其他答案所指出的那样,一次排序基本上就足够了,尽管在 n 之前 k 足够小,一次排序调用比原始代码解决方案最差。
【解决方案2】:

这是一个很难回答的问题,原因有两个:

  • 首先,方法 #1 和方法 #2 做的事情不同,因此比较它们的效率并没有真正的意义。
  • 其次,方法#1 实际执行的操作有点难以准确确定,并且不清楚它实际执行的操作与应执行的操作相同。也就是说,方法#1 不仅仅是解决不同问题的方法;我怀疑这是针对不同问题的不正确解决方案

让我解释一下。方法#2 非常简单:它从子数组array[0..k] 中找到最大元素。方法#1 显然没有这样做:它只从子数组array[k..n] 中读取数据。

它显然也没有从那个子数组中找到最大值,因为它将数据放入smallArray,对其进行排序,然后从索引0返回值;最大值将在索引k - 1 处。但索引 0 处的值也不是最小值,因为只有当数据大于大于已有数据时,数据才会被放入smallArray

方法#1 的实际行为可以通过示例进行研究。为方便起见,我将签名更改为以arrayk 作为参数:

  • findLarger(new int[] { 1, 2, 3, 4, 5, 6, 7 }, 3) 是 5:4、5、6、7 中的第三大值。

  • findLarger(new int[] { 1, 2, 3, 7, 6, 5, 4 }, 3) 也是 5:7、6、5、4 中的第三大值。

  • findLarger(new int[] { 7, 6, 5, 4, 3, 2, 1 }, 3) 是 2:4、3、2、1 中的第三大值。

  • findLarger(new int[] { 1, 2, 3, 7, 6, 5, 4 }, 1) 是 7:第一个最大的 2、3、7、6、5、4。

对于这些示例,它始终返回子数组 array[k..n] 中最大的元素 k。但是,在其他情况下,它不会:

  • findLarger(new int[] { -1, -2, -3, -4, -5, -6 }, 2) 是 0,而不是 -3、-4、-5、-6 之一。
  • findLarger(new int[] { 1, 2, 3, 4, 5, 6, 7 }, 5) 是 0,而不是 6、7 之一。

因此,方法#1 所做的完整说明是:它返回子数组array[k..n] 中最大的kth 正元素,如果该子数组包含的正数少于k,则返回0 。返回 0 的特殊情况,以及将 k 用于两个不相关的目的,表明此方法应该解决返回 kth 最大元素的更直接问题,但是写错了。

进一步的证据是,对算法的一个非常简单的更改使其无条件返回kth 最大元素:而不是用零初始化smallArray,而是从array复制第一个k元素并排序他们。

    // changed: copy first k elements from array, and sort
    int[] smallArray = Arrays.copyOfRange(array, 0, k);
    Arrays.sort(smallArray);

    for (int index = k; index < array.length; index++){
         if (array[index] > smallArray[0]){
             smallArray[0] = array[index];
             Arrays.sort(smallArray);
         }
    }
    return smallArray[0];

更多的证据是与the code in this other Stack Overflow question 的相似性,这意味着找到kth 最大的元素,它会找到copyOfRangesort 而不仅仅是new int[k]


所以现在我们可以谈谈替代方法#1的固定版本的效率。

  • 方法#1的时间复杂度为O(nk log k)。

  • 方法 #1 可以改进为 O(nk),方法是在内部循环中更改 Arrays.sort 以将第一个元素移动到其在 O(k) 时间;这是可行的,因为只有第一个元素会乱序,所以不需要完整的排序。

  • 找到kth 最大元素的明显方法是对数组进行排序并返回索引n - k 处的值。这需要 O(n log n) 时间;方法 #1 仅在 k log k n 时更好,即当 kk 小时em>n.

  • 你可以做得更好 - quickselect 算法平均只需要 O(n) 时间,这显然是这个问题的最佳选择。但是,它具有罕见的 O(n²) 的最坏情况复杂度。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-10-02
    • 1970-01-01
    • 2022-11-28
    • 1970-01-01
    • 1970-01-01
    • 2018-05-03
    • 2013-09-09
    • 1970-01-01
    相关资源
    最近更新 更多