Java - 以“正确”的方式实现排序算法

时间:2010-07-20 09:05:00

标签: java generics sorting insertion-sort

我目前正在玩Java中实现各种排序算法,主要是为了好玩,但我正在努力解决“正确”的问题。也就是说,我希望用户能够在可比较的任何内容上调用所选的排序算法 - int s,long s,String s,boolean s (实际上,这些在Java中是否具有可比性?),他们自己的类;随你。问题是如何做到这一点。

我在考虑使用类来表示排序算法,因此使用通用列表或其他任何内容(List<E>)存储要排序的内容。这也允许我使用多个构造函数,从而允许用户以各种形式传递数据 - 列表,数组等等。这是正确的方法吗?我目前的问题是,我不希望用户在想要对某些内容进行排序时必须创建一个类,我宁愿它可以像System.out.println之类的那样被调用。

// Example:

int[] myInts = {5,4,3,2,1};

// This is what I do *not* want.
InsertionSort mySort = new InsertionSort();
int[] sortedInts = mySort.sort(myInts);

// This is more like what I want.
int[] sortedInts = Sorting.insertionSort(myInts);

我为看似基本的问题道歉,但我只是在学习编程语言。对于在一家软件公司工作的2年 nd 年计算机学生而言,这对于他的暑期工作来说有点荒谬,但是你会惊讶于他们需要什么样的编程知识。我的大部分工作......通常都是更多的设计知识。

编辑:

为清楚起见,我的三个主要问题是:

  • 让用户创建一个类来进行排序,或者在用户导入的类中使用静态方法更好吗?
  • 是否可以轻松处理原始数据类型和通用对象?因为我希望能够处理任何实现可比较(或类似)的通用对象,所以这会导致基元问题(因为它们没有实现任何东西;)。)。
  • 处理通用输入的最佳方法是什么 - 在我尝试对它们进行排序之前应该检查什么(例如,实现Comparable)?

4 个答案:

答案 0 :(得分:1)

您可以采用Collections提供binarySearch操作的方式为例......实际上,

int[] sortedInts = Sorting.insertionSort(myInts);

更像是java-way,即使我个人更喜欢

public class Sorting {
     public static <DataType extends Comparable> Iterable<DataType> insertionSort(Iterable<DataType> data);
}
  • <DataType>确保输出数据与输入
  • 的类型相同
  • Iterable<DataType>数据输入数据是可迭代的,以确保最大的兼容性。显然,使用List会很简单,因为它允许内部项目重新排序。但是,使用可迭代的方法确保此方法的实现者必须重新创建列表才能修改它,保证输入列表保持不变,输出列表是另一个。

由于我刚刚看到你编辑了你的问题,让我逐点回复(并考虑在那之后选择答案,因为添加新问题比编辑现有问题更容易 - 除非你提出问题社区维基,就像我的回复一样)

  

让用户创建一个类来进行排序,或者在用户导入的类中使用静态方法更好吗?

在我看来,在这种情况下使用静态方法是可取的,因为你必须以一种非常“基本”的方式操纵你没有创建的对象。

  

是否可以轻松处理原始数据类型和通用对象?因为我希望能够处理任何实现Comparable(或类似)的通用对象,所以这会导致基元问题(因为它们没有实现任何东西;)。)。

你听说过autoboxing吗?它是Java 5的一个特性,它使主要类型成为对象的“等价物”。这就是说int会自动转换为Integer,正如你所知,它实现了Comparable。

  

处理通用输入的最佳方法是什么 - 在我尝试对它们进行排序之前应该检查什么(例如,实现Comparable)?

请注意,由于我的方法声明(),检查输入数据实现Comparable不是由你完成的,而是由Jav编译器完成,允许你的IDE显示错误。

答案 1 :(得分:1)

我想你回答了自己的问题?如果要公开静态方法而不是让用户创建和对象并调用实例方法,那么就这样做。 Sorting.insertionSort()看起来很好。

内部可以发送任何你喜欢的东西。在里面,如果你想用类和多态和诸如此类的方法来实现它,那就继续吧。虽然看起来有点矫枉过正。我不确定继承和多态在这里有多大帮助。

答案 2 :(得分:1)

实现排序算法的常规方法是实现静态方法,例如,看一下Arrays.sort()的源代码。您可以使用针对不同参数类型的不同实现来重载此方法(例如,实现可比较的对象,提供您自己的比较器与原始数组等。)

这是我之前写的:

public static <T> void swap(T[] a, int x, int y) {
    T t=a[x];
    a[x]=a[y];
    a[y]=t;
}

public static <T extends Comparable<? super T>> void mergeInOrder(T[] src, T[] dst, int p1, int p2, int p3, int p4) {
    if (src[p2].compareTo(src[p3])<=0) return; // already sorted!

    // cut away ends
    while (src[p1].compareTo(src[p3])<=0) p1++;
    while (src[p2].compareTo(src[p4])<=0) p4--;

    int i1=p1;
    int i3=p3;
    int di=p1;
    while(di<p4) {
        if (src[i1].compareTo(src[i3])<=0) {
            dst[di++]=src[i1++];
        } else {
            dst[di++]=src[i3++];
            if (i3>p4) {
                System.arraycopy(src,i1,dst,di,p2-i1+1);
                break;
            }
        }
    }

    System.arraycopy(dst, p1, src, p1, (p4-p1)+1);
}

public static <T extends Comparable<? super T>> void mergeSort(T[] src, T[] dst, int start, int end) {
    if (start+1>=end) {
        if (start>=end) return;
        if (src[start].compareTo(src[end])>0) {
            swap(src,start,end);
        }
        return;
    }

    int middle=(start+end)/2;
    mergeSort(src,dst,start, middle);
    mergeSort(src,dst,middle+1, end);
    mergeInOrder(src,dst,start,middle,middle+1,end);
}

private static ThreadLocal<Comparable<?>[]> mergeSortTemp=new ThreadLocal<Comparable<?>[]>();

@SuppressWarnings("unchecked")
public static <T extends Comparable<? super T>> void mergeSort(T[] src) {
    int length=src.length;
    Comparable<?>[] temp=mergeSortTemp.get();
    if ((temp==null)||(temp.length<length)) {
        temp=new Comparable[length*3/2];
        mergeSortTemp.set(temp);
    }
    mergeSort(src,(T[])temp,0,length-1);
}

但是,我可以想到将排序算法实现为生成自己实例的类的两个很好的理由:

  • 它允许您以多态方式传递排序算法的实例 - 这可能是有用的,例如,你正在创建一个排序算法的集合,并希望在它们上面运行许多基准测试。
  • 您可以在分拣机实例中拥有私有状态 - 这对某些排序算法很有用,例如:有一些预分配的数组用于临时存储,如果你想能够同时使用来自多个线程的不同排序实例,将它放在类实例中是有意义的 - 静态方法实现需要某种形式的同步(例如,见在上面的代码中使用ThreadLocal。)

答案 3 :(得分:0)

我不确定这是否是您正在努力的...但是几乎不可能实现既适用于引用类型又适用于(真实)原始类型的算法。原因是Java类型系统没有具有基本类型和Object作为子类型的名义通用类型。

正常的解决方法是使用相应的包装类包装基本类型;例如IntegerintBooleanbool,依此类推。这允许您实现(例如)适用于任何Collection<T>或任何<T>[]的排序算法。

这种方法在应用于大型(例如)整数数组时会出现性能/内存使用问题。要么是性能损失,要么是为每种基本类型单独实现算法及其支持类。

(我说旁边是不可能的,因为有可能抽象出一对数组元素的比较并以一种不暴露实际元素的方式交换一对数组元素键入界面;例如

public interface ArraySortAdapter {
  public abstract int compareElements(Object array, int pos1, int pos2);
  public abstract void swapElements(Object array, int pos1, int pos2);
}

并为不同的数组类型提供不同的实现; e.g。

public class IntArraySortAdapter implements ArraySortAdapter {
  public int compareElements(Object array, int pos1, int pos2) {
      int[] intArray = (int[]) array;
      if (intArray[pos1] < intArray[pos2]) {
          return -1;
      } else if (intArray[pos1] > intArray[pos2]) {
          return +1;
      } else {
          return 0;
      }
  }

  ...
}

然而,至少可以说这是麻烦且效率低下的......)