我有以下算法分别用于插入排序,选择排序和快速排序。我已经运行了一个测试,为每个算法排序一个10,000个整数的数组,介于0到1000之间。奇怪的是,选择排序的平均值明显低于插入排序和快速排序。插入,选择和快速排序的平均值分别为182044.4,217.9和545.4毫秒。考虑到我可以对插入和快速排序进行小的改进,我不会期望缺少这些增强会导致如此大的差异。我在这里的实施错在哪里?同样,不是小的增强,如移动索引或检查,但实际错误。
我已经提供了测试代码和下面的结果
由于
public final static <E extends Comparable<E>> ArrayList<E> insertionSort(ArrayList<E> arr)
{
for(int i = 1; i < arr.size(); i++)
{
for(int j = i; j > 0; j--)
{
if(arr.get(j).compareTo(arr.get(j - 1)) < 0)
{
swap(arr, j, j-1);
}else{
break;
}
}
}
return arr;
}
`
public final static <E extends Comparable<E>> ArrayList<E> selectionSort(ArrayList<E> arr)
{
for(int i = 0; i < arr.size() - 1; i++){
int min = i;
for(int j = i + 1; j < arr.size(); j++){
if(arr.get(j).compareTo(arr.get(min)) < 0){
min = j;
}
}
swap(arr, i, min);
}
return arr;
}`
`
public final static <E extends Comparable<E>> ArrayList<E> quickSort(ArrayList<E> arr)
{
quickSort(arr, 0, arr.size() -1);
return arr;
}
private static <E extends Comparable<E>> void quickSort(ArrayList<E> arr, int lo, int hi)
{
if(hi - lo < 1)
{
return;
}
if((hi - lo) == 1)
{
if(arr.get(lo).compareTo(arr.get(hi)) > 0)
{
swap(arr, lo, hi);
}
return;
}
int pivot = (hi - lo) / 2 + lo;
int j;
swap(arr, pivot, hi);
pivot = hi;
for(int i = lo; i < pivot; i++)
{
if(arr.get(i).compareTo(arr.get(pivot)) > 0 )
{
for(j = i + 1; j < pivot; j++)
{
if(arr.get(j).compareTo(arr.get(pivot)) < 0)
{
swap(arr, i, j);
break;
}
}
if(j == pivot)
{
swap(arr, i, pivot);
pivot = i;
}
}
}
//do sort op here
quickSort(arr, lo, pivot - 1);
quickSort(arr, pivot + 1, hi);
}
`
要测试的代码 - genRans(n)只生成0到n之间随机数的数组列表 ` int n = 10000; ArrayList list = new ArrayList();
int tries = 10;
double[] times = new double[tries];
double sum = 0;
double time = 0;
long startTime = 0;
long endTime = 0;
for(int i = 0; i < tries; i++)
{
list = genRans(n);
startTime = System.nanoTime();
//System.out.println("before " + list);
Sort.selectionSort(list);
//System.out.println("after" + list);
endTime = System.nanoTime();
time = (endTime - startTime) / 1000000;
times[i] = time;
sum += time;
}
System.out.println("Times for selection sort = " + Arrays.toString(times));
System.out.println("Avg time for selection = " + sum / tries);
sum = 0;
for(int i = 0; i < tries; i++)
{
list = genRans(n);
startTime = System.nanoTime();
Sort.insertionSort(list);
endTime = System.nanoTime();
time = (endTime - startTime) / 1000000;
times[i] = time;
sum += time;
}
System.out.println("Times for insertion sort = " + Arrays.toString(times));
System.out.println("Avg time for insertion = " + sum / tries);
sum = 0;
for(int i = 0; i < tries; i++)
{
list = genRans(n);
startTime = System.nanoTime();
Sort.quickSort(list);
endTime = System.nanoTime();
time = (endTime - startTime) / 1000000;
times[i] = time;
sum += time;
}
System.out.println("Times for quick sort = " + Arrays.toString(times));
System.out.println("Avg time for quick = " + sum / tries);
}
` 结果: 选择时间排序= [235.0,214.0,216.0,216.0,216.0,216.0,217.0,216.0,217.0,216.0] 平均选择时间= 217.9 插入时间排序= [182936.0,181976.0,182571.0,182448.0,180757.0,180567.0,181593.0,185073.0,181241.0,181282.0] 平均插入时间= 182044.4 快速排序的时间= [629.0,487.0,579.0,557.0,547.0,482.0,543.0,571.0,525.0,534.0] 平均快速时间= 545.4
答案 0 :(得分:1)
衡量时间的方式肯定是错的。我用JMH计算了你的排序方法(加上java的内置排序)。以下是10000个元素的结果:
Benchmark Mode Cnt Score Error Units
SortVSort.insertion avgt 30 52.802 ± 0.103 ms/op
SortVSort.java avgt 30 1.196 ± 0.004 ms/op
SortVSort.quick avgt 30 23.923 ± 0.026 ms/op
SortVSort.selection avgt 30 99.173 ± 0.105 ms/op
答案 1 :(得分:0)
您的快速分配步骤实际上是二次时间,几乎肯定是错误的。
当您找到string
i
时,您正在向arr[i] > arr[pivot]
向前扫描,找到一个位置i+1
来交换它:这将导致多次跳过相同的数组元素。假设前n / 2个项最初都是j
,其余的都是> arr[pivot]
:外部循环的前n / 2次迭代中的每一个(递增< arr[pivot]
)都将需要内部循环的n / 2次迭代,增加i
以找到要交换的伙伴,总体上进行O(n ^ 2)次迭代。
此外,如果您找不到要与之交换j
的合作伙伴j
,您当前会将数据透视转换为i
位置。这意味着如果枢轴上方的值多于它下方的值,则枢轴将在错误的位置结束 - 在所有小于耗尽的值(作为交换具有大于它的值的伙伴)之后,它将保持不变滑向右边。