题目描述:
实验总体思路:
利用switch结构来选择实验所要用的排序算法,每一种排序都用相同的计算运行时间的代码,不同的算法就在算法实现部分进行改动(如下代码1至5所示)。不断的改变数据规模,每一个规模在实验时,用循环进行多次实验并作为样本记录消耗的时间。最后输出在不同排序算法下,不同的数据规模的20次实验样本和平均用时
1、选择排序
代码1:
for i=0 to n-2
min=i
for j= i+1 to n-1
if ele[min]>ele[j] min=j
swap(ele[i],ele[min]) //交换
图1、选择排序在不同数据规模下排序所消耗的时间
2、冒泡排序
代码2:
for i= 0 to n-1
for j=0 to n-1-i
if a[j]>a[j+1]
swap(a[j],a[j+1]) //交换
图2、冒泡排序在不同数据规模下排序所消耗的时间
3、合并排序
代码3:
Merge(ele[1...n],left,right)
middle=(left+right)/2
if right>1eft+1
Merge(ele,left,middle)
Merge(ele,middle+1,right)
l←left r←right i←left
while l<=middle&&r<=right //两组分别一一比较,数据小的放入ele
if ele[l]<=ele[r]
t[i++]←ele[l++]
else
t[i++]←ele[r++]
while l>middle&&r<=r //只剩一组还有剩余的时,将剩下的按顺序放入
ele[i++]=s[r++]
while l<=middle && r>right
ele[i++]=s[l++];
图3、合并排序在不同数据规模下排序所消耗的时间
4、快速排序
代码4:
quick(ele[0...n-1],left,right)
if l<r
l←left r←right x←ele[l];
while l<r
while l<r && x<=ele[r] //找到一个比x小的数之后交换到前面的部分
r--
if l<r
ele[l]←ele[r] l++
while l<r && x>ele[l] //与上面相反
ll++
if l<r
ele[r]←ele[l] r--
ele[l]←x;
quick(ele,left,l-1) // 递归调用
quick(ele,l+1,right)
图4、快速排序在不同数据规模下排序所消耗的时间
5、插入排序
代码5:
for i=1→n-1
if ele[i]<ele[i-1] temp=ele[i]
for j= i-1 to 0 && ele[j]>temp
ele[j+1]←ele[j]
ele[j+1]←temp
具体实现:
package jj;
import java.util.Arrays;
import java.util.Date;
import java.util.Scanner;
/**
* 2018/12/1
* @author jjw
*
*/
public class Paixu {
public static void swap(int a,int b){
int t=a;
a=b;
b=t;
}
//快速排序
//平均效率O,适用于排序大列表
public static void quick(int[] Data,int left,int right)
{
int i,j; //下标
int middle,temp;
i = left;
j = right;
middle = Data[left];
while(true)
{
while((i++)<right-1 && Data[i]<middle);
while((j--)>left && Data[j]>middle);
if(i>=j)
break;
temp = Data[i];
Data[i] = Data[j];
Data[j] = temp; //交换
}
Data[left] = Data[j];
Data[j] = middle;
if(left<j)
quick(Data,left,j); //递归调用
if(right>i)
quick(Data,i,right);
}
//冒泡排序
//效率 O(n²),适用于排序小列表
public static void maopao(int Data[]){
for(int i=0;i<Data.length;i++){
for(int j=i+1;j<Data.length;j++){
if(Data[i]>Data[j]){
//下面是个人听别人说的一个很好用的小技巧 ^_^!
Data[i]=Data[i]+Data[j];
Data[j]=Data[i]-Data[j];
Data[i]=Data[i]-Data[j];
}
}
}
}
//选择排序
//效率O(n²),适用于排序小的列表
public static void choose(int Data[]){
for(int i=0;i<Data.length-1;i++){
int min=i;
for(int j=i+1;j<Data.length;j++){
if(Data[min]>Data[j]){
//交换
Data[min]=Data[min]+Data[j];
Data[j]=Data[min]-Data[j];
Data[min]=Data[min]-Data[j];
// swap(Data[min],Data[j]);//为什么用不了呢 烦人
}
}
}
}
//插入排序
//最佳效率O(n);最糟效率O(n²)与冒泡、选择相同,适用于排序小列表
public static void insert(int Data[]){
int left, cur;
for (int i=1;i<Data.length;i++) {
left=i-1; //前一个数组元素
cur=Data[i]; //给cur赋于Data[i]的值,将cur当做要插入的元素
while (left>=0&&Data[left]>cur) { //(就是前一个的数组大于后一个数组)
Data[left+1]=Data[left]; //如果要插入的元素小于第j个元素,就将第j个元素向后移动
left--; //跟再前一个数组元素比较
}
Data[left+1]=cur; // 直到要插入的元素不小于第i-1个元素,将cur插入到数组中
}
}
//合并排序
private static void mergeSort(int[] original) {
if (original == null) {
throw new NullPointerException("The array can not be null !!!");
}
int length = original.length;
if (length > 1) {
int middle = length / 2;
int partitionA[] = Arrays.copyOfRange(original, 0, middle);// 拆分问题规模
int partitionB[] = Arrays.copyOfRange(original, middle, length);
// 递归调用
mergeSort(partitionA);
mergeSort(partitionB);
sort(partitionA, partitionB, original);
}
}
private static void sort(int[] partitionA, int[] partitionB, int[] original) {
int i = 0;
int j = 0;
int k = 0;
while (i < partitionA.length && j < partitionB.length) {
if (partitionA[i] <= partitionB[j]) {
original[k] = partitionA[i];
i++;
} else {
original[k] = partitionB[j];
j++;
}
k++;
}
if (i == partitionA.length) {
while (k < original.length) {
original[k] = partitionB[j];
k++;
j++;
}
} else if (j == partitionB.length) {
while (k < original.length) {
original[k] = partitionA[i];
k++;
i++;
}
}
}
private static void print(int[] array) {
if (array == null) {
throw new NullPointerException("The array can not be null !!!");
}
StringBuilder sb = new StringBuilder("");
for (int element : array) {
sb.append(element + " ");
}
System.out.println(sb.toString());
}
//主函数
public static void main(String[] args){
int sum = 0;
System.out.println("1.快速排序 2.冒泡排序 3.选择排序 4.插入排序 5.合并排序");
Scanner sc =new Scanner(System.in);
int n=sc.nextInt();
for(int i=10;i<1000000;i*=10){
int[] Data = new int[i]; //定义数组
for(int i1 = 0; i1< i; i1++)
Data[i1] = (int)(Math.random()*100); //随机生成数
System.out.println("数据规模为"+i+"时:");
System.out.print("20组样本:");
// for(int i = 0; i<Data.length; i++)
// System.out.print(Data[i]+" "); //输出原始数据
for(int j=0;j<20;j++){
long startTime = System.currentTimeMillis(); //获取开始时间
switch(n){
case 1:
// System.out.println("快速排序:");
Paixu.quick(Data, 0, Data.length); //调用快排
break;
case 2:
Paixu.maopao(Data);//调用冒泡
// System.out.println("\n冒泡排序");
// for(int i:Data)//遍历
// {
// System.out.print(i+" ");
// }
break;
case 3:
Paixu.choose(Data);
// System.out.println("\n选择排序");
// for(int i:Data)//遍历
// {
// System.out.print(i+" ");
// }
break;
case 4:
Paixu.insert(Data);
// System.out.println("\n插入排序");
// for(int i:Data)//遍历
// {
// System.out.print(i+" ");
// }
break;
case 5:
// System.out.println("\n合并排序");
mergeSort(Data);
// print(Data);
break;
}
long endTime = System.currentTimeMillis(); //获取结束时间
int time=(int) (endTime - startTime);
System.out.print(time+" ");
sum +=time;
}
System.out.println("\n"+"平均时间为"+(sum/20)+"ms");
}
sc.close();
}
}
运行结果:
- 测试:
- 运行结果
选择排序:
冒泡排序:
快速排序:
插入排序:
合并排序:
进行详细结果对比,得到结论:
分析结果:
对比结果,实验分析:
方式/规模 | 10 | 100 | 1000 | 10000 | 100000 |
快速排序 | 0 | 0 | 0 | 3 | 45 |
冒泡排序 | 0 | 0 | 0 | 14 | 1449 |
选择排序 | 0 | 0 | 1 | 17 | 1442 |
插入排序 | 0 | 0 | 0 | 3 | 71 |
合并排序 | 0 | 0 | 0 | 1 | 7 |
3.对比得出结论:
选择排序:数据基本符合n2(二次增长)时间复杂度O(n2)
冒泡排序:数据基本符合n2(二次增长)时间复杂度O(n2)
快速排序:数据基本符合n(线性增长)时间复杂度O(n)
插入排序:数据基本符合n2(二次增长)时间复杂度O(n2)
合并排序:数据基本符合n(线性增长)时间复杂度O(n)
4.经验归纳:
以前很少用到方法,都是在主函数里乱写。觉得方法不好理解。这次用一个一个的方法分开实现一个一个功能,写完了之后觉得这种方式很方便。很有层次感。然而不足之处还是有很多,首先是自己编程确实不够熟悉,对于合并算法的理解还是不够透彻,实验中参考了网络,还有待好好改进。