冒泡排序
算法描述
步骤1: 比较相邻的元素。如果第一个比第二个大,就交换它们两个;
步骤2: 对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对,这样在最后的元素应该会是最大的数;
步骤3: 针对所有的元素重复以上的步骤,除了最后一个;
步骤4: 重复步骤1~3,直到排序完成。
图解
代码
static void bubbleSort(int[] arr)//冒泡排序
{
int temp;
for (int i=1;i<arr.length;i++)//冒泡的次数
for(int j=0;j<arr.length-i;j++)//冒出第i大的数,放在length-i位置
{
if(arr[j]>arr[j+1])
{
temp=arr[j];
arr[j]=arr[j+1];
arr[j+1]=temp;
}
}
return ;
}
选择排序
算法描述
步骤1:初始状态:无序区为R[1…n],有序区为空;
步骤2:第i趟排序(i=1,2,3…n-1)开始时,当前有序区和无序区分别为R[1…i-1]和R(i…n)。该趟排序从当前无序区中-选出关键字最小的记录 R[k],将它与无序区的第1个记录R交换,使R[1…i]和R[i+1…n)分别变为记录个数增加1个的新有序区和记录个数减少1个的新无序区;
步骤3:n-1趟结束,数组有序化了。
图解
代码
static void selectionSort(int[] arr)//选择排序
{
int temp,min;
for(int i=0;i<arr.length-1;i++)//i为起始、想要位置
{
//寻找最小值
min=i;
for(int j=i+1;j<arr.length;j++)
{
if (arr[min] > arr[j])
min = j;
}
//替换最小值
if(min!=i)
{
temp=arr[min];
arr[min]=arr[i];
arr[i]=temp;
}
}
return ;
}
插入排序
算法描述
步骤1: 从第一个元素开始,该元素可以认为已经被排序;
步骤2: 取出下一个元素,在已经排序的元素序列中从后向前扫描;
步骤3: 如果该元素(已排序)大于新元素,将该元素移到下一位置;
步骤4: 重复步骤3,直到找到已排序的元素小于或者等于新元素的位置;
步骤5: 将新元素插入到该位置后;
步骤6: 重复步骤2~5。
图解
代码
static void insertSort(int[] arr)//插入排序
{
int insert;//要插入的值
for(int i=0;i<arr.length;i++)//i为要插入的值
{
insert=arr[i];
for(int j=i-1;j>=0;j--)
{
if(arr[j]<insert)//找到插入的位置
{
arr[j+1]=insert;
break;
}
else if(j!=0)//未找到位置,但是还可以再找,后移
{
arr[j+1]=arr[j];
}
else//未找到位置,不可以再找,后移,插入
{
arr[j+1]=arr[j];
arr[j]=insert;
}
}
}
return ;
}
希尔排序
算法描述
步骤1:选择一个增量序列t1,t2,…,tk,其中ti>tj,tk=1;
步骤2:按增量序列个数k,对序列进行k 趟排序;
步骤3:每趟排序,根据对应的增量ti,将待排序列分割成若干长度为m 的子序列,分别对各子表进行直接插入排序。仅增量因子为1 时,整个序列作为一个表来处理,表长度即为整个序列的长度。
图解
代码
public static void shellSort(int arr[])//希尔排序
{
int insert;//要插入的值
for(int num = arr.length/2;num>0;num/=2)//分组的数量
{
for(int k = 0;k<num;k++)//分组起点k
{
//插入排序
for(int i=k;i<arr.length;i+=num)//i 为要插入的点
{
insert=arr[i];
for(int j=i-num;j>=0;j-=num)//j 为向前遍历的位置
{
if(insert>arr[j])//找到插入点
{
arr[j+num]=insert;
break;
}
else if(j<num)//没找到插入点,仍可向前找
{
arr[j+num]=arr[j];
arr[j]=insert;
break;
}
else //没找到插入点,且不能向前找了
{
arr[j+num]=arr[j];
}
}
}
}
}
}
快速排序
描述
步骤1:从数列中挑出一个元素,称为 “基准”(pivot );
步骤2:重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。在这个分区退出之后,该基准就处于数列的中间位置。这个称为分区(partition)操作;
步骤3:递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序。
图解
代码
public static void quickSort(int arr[],int l,int r){
if(l>=r)
return;
int LIFT=l;
int RIGHT=r;
int temp=arr[l];
while (l<r)
{
while(l<r&&arr[r]>=temp)
r--;
arr[l]=arr[r];
while (l<r&&arr[l]<=temp)
l++;
arr[r]=arr[l];
}
arr[l]=temp;
quickSort(arr,LIFT,l-1);
quickSort(arr,l+1,RIGHT);
}
测试代码
使用的是最坏情况
static void printArray(int[] arr)//打印数组
{
for (int e:arr)
{
System.out.print(e+" ");
}
System.out.println();
}
public static void main(String[] args)
{
int []arr1=new int[200000];
int []arr2=new int[200000];
int []arr3=new int[200000];
int []arr4=new int[200000];
int []arr5=new int[200000];
for(int i=200000;i>=1;i--)
{
arr1[200000-i]=i;
arr2[200000-i]=i;
arr3[200000-i]=i;
arr4[200000-i]=i;
arr5[200000-i]=i;
}
long startTime,endTime;
//冒泡排序
startTime=System.currentTimeMillis(); //获取开始时间
bubbleSort(arr1);
endTime=System.currentTimeMillis(); //获取结束时间
System.out.println("冒泡排序程序运行时间:" + (endTime - startTime) + "ms"); //输出程序运行时间
//选择排序
startTime=System.currentTimeMillis(); //获取开始时间
selectionSort(arr2);
endTime=System.currentTimeMillis(); //获取结束时间
System.out.println("选择排序程序运行时间:" + (endTime - startTime) + "ms"); //输出程序运行时间
//插入排序
startTime=System.currentTimeMillis(); //获取开始时间
insertSort(arr3);
endTime=System.currentTimeMillis(); //获取结束时间
System.out.println("插入排序程序运行时间:" + (endTime - startTime) + "ms"); //输出程序运行时间
//希尔排序
startTime=System.currentTimeMillis(); //获取开始时间
shellSort(arr4);
endTime=System.currentTimeMillis(); //获取结束时间
System.out.println("希尔排序程序运行时间:" + (endTime - startTime) + "ms"); //输出程序运行时间
//快速排序
startTime=System.currentTimeMillis(); //获取开始时间
quickSort(arr5,0,arr2.length-1);
endTime=System.currentTimeMillis(); //获取结束时间
System.out.println("快速排序程序运行时间:" + (endTime - startTime) + "ms"); //输出程序运行时间
}
性能
总结
使用快排时,由于是递归调用,要注意栈的深度,很容易栈溢出。
解决方法:设置JVM参数:-Xss20m
同样是最坏情况,希尔排序的性能好得让我吃惊,快排速度也不错。