【问题标题】:Why is my quick sort so slow?为什么我的快速排序这么慢?
【发布时间】:2011-06-02 04:26:29
【问题描述】:

我正在练习编写排序算法作为一些面试准备的一部分,我想知道是否有人可以帮助我找出为什么这种快速排序不是很快?它似乎具有正确的运行时复杂性,但它比我的合并排序慢约 2 的常数因子。我也将感谢任何可以改进我的代码但不一定回答问题的 cmet。

非常感谢您的帮助!如果我犯了任何礼仪错误,请不要犹豫,让我知道。这是我在这里的第一个问题。

private class QuickSort implements Sort {

        @Override
        public int[] sortItems(int[] ts) {
            List<Integer> toSort = new ArrayList<Integer>();
            for (int i : ts) {
                toSort.add(i);
            }
            toSort = partition(toSort);
            int[] ret = new int[ts.length];
            for (int i = 0; i < toSort.size(); i++) {
                ret[i] = toSort.get(i);
            }
            return ret;
        }

        private List<Integer> partition(List<Integer> toSort) {
            if (toSort.size() <= 1)
                return toSort;
            int pivotIndex = myRandom.nextInt(toSort.size());
            Integer pivot = toSort.get(pivotIndex);
            toSort.remove(pivotIndex);
            List<Integer> left = new ArrayList<Integer>();
            List<Integer> right = new ArrayList<Integer>();
            for (int i : toSort) {
                if (i > pivot)
                    right.add(i);
                else
                    left.add(i);
            }
            left = partition(left);
            right = partition(right);
            left.add(pivot);
            left.addAll(right);
            return left;
        }

}

非常感谢所有帮助过的人!

这是我为后代大大改进的课程:

private class QuickSort implements Sort {

        @Override
        public int[] sortItems(int[] ts) {
            int[] ret = ts.clone();
            partition(ret,0,ret.length);
            return ret;
        }

        private void partition(int[] toSort,int start,int end) {
            if(end-start<1) return;
            int pivotIndex = start+myRandom.nextInt(end-start);
            int pivot = toSort[pivotIndex];
            int curSorted = start;
            swap(toSort,pivotIndex,start);
            for(int j = start+1; j < end; j++) {
                if(toSort[j]<pivot) {
                    if(j!=curSorted+1) 
                        swap(toSort,curSorted,curSorted+1);
                    swap(toSort,j,curSorted++);
                }
            }
            // Now pivot is at curSorted
            partition(toSort,start,curSorted);
            partition(toSort,curSorted+1,end);
        }
    }

【问题讨论】:

  • 只是把它扔在那里,但是当数组的数字完全随机时,快速排序实际上是最快的,而不是在这种情况下顺序无关紧要的合并排序。
  • 我建议你看看集合中的快速排序代码。它相当快速和高效。或者你可以直接使用它。
  • 标题讽刺+1 :)

标签: java sorting complexity-theory quicksort


【解决方案1】:

快速排序的最大优势之一是它可以作为就地算法实现。不要创建新列表,而是对元素进行就地排序。

【讨论】:

  • 同样,看看你的输入是否几乎没有排序!
  • @Saurabh:真的没有理由这样做。如果您始终选择最左边的元素作为枢轴,则快速排序只会对已排序的输入表现出反常的二次性能。使用随机选择或三个中值的枢轴,这不是问题(在 OP 的代码中,枢轴是随机选择的)。
  • @James:尽管你说的是准确的,但随机选择枢轴输入(取决于随机选择),性能会降低。所以尽管 Saurabh 的 cmets 并不完全准确,但评论的动机仍然值得考虑:病理数据/角落病例等。
  • @Moron:有点。对于某些输入,某些快速排序实现具有二次时间复杂度这一事实只是该算法的一个已知事实。这并不是真正的病态。然而,当对已经排序的输入进行排序需要二次时间时,这是病态的,因为这是不合理的。对于大多数用例,数据很可能已经排序或大部分排序。需要二次时间来对序列进行排序的随机枢轴选择的可能性要低得多(几乎微不足道)。
  • 也就是说,如果我没记错的话,这是 Bob Sedgewick 用来证明为什么在大多数情况下选择三中位数的枢轴选择比随机枢轴选择更可取的几个原因之一。跨度>
【解决方案2】:

除了不重用列表之外,你在每一步都在 Integer 和 int 之间进行转换:

        for (int i : toSort) {  // converts from Integer to int
            if (i > pivot)
                right.add(i);  // converts from int to Integer
            else
                left.add(i);   // converts from int to Integer
        }

请注意,从 int 到 Integer 的转换通常需要创建一个新对象。

最后 random.nextInt() 可能是一个不平凡的操作。如果 toSort 超过一定的大小,最好只选择一个随机枢轴,否则使用更简单的枢轴选择策略(测量它!)。

【讨论】:

    猜你喜欢
    • 2021-06-19
    • 2015-06-25
    • 2016-02-17
    • 2017-03-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-11-14
    • 1970-01-01
    相关资源
    最近更新 更多