排序分类:
1、插入排序:直接插入排序(InsertSort)、二分插入排序、希尔排序(ShellSort)
2、选择排序:简单选择排序、堆排序
3、交换排序:冒泡排序、快速排序
4、归并排序
5、基数排序
排序算法优劣指标:
(1)稳定性:当序列中存在两个或两个以上的关键字相等的时候,如果排序前序列中r1领先于r2,那么排序后r1如果仍旧领先r2的话,则是稳定的。(相等的元素排序后相对位置不变)
(2)不稳定性:当序列中存在两个或两个以上的关键字相等的时候,如果排序前序列中r1领先于r2,那么排序后r1如果落后r2的话,则是不稳定的。(相等的元素排序后相对位置发生改变)
(3)时间复杂度:算法的时间开销是衡量其好坏的最重要的标志。高效率的算法应该具有更少的比较次数和记录移动次数。
(4)空间复杂度:即执行算法所需要的辅助存储的空间。
直接插入排序:
基本思想:遍历序列的关键字,将待排序的关键字依次与其前一个关键字起逐个向前扫描比较,若是待排关键字小于扫描到的关键字,则将扫描到的关键字的向后移动一个位置,直到找到没有比待排关键字大的地方插入(待排序关键字前面的都是有序数列)
//直接插入排序
public static void directInsertSort(int[] nums)
{
int i,j;
int temp;
for(i=1;i<nums.length;i++)//排序几趟 n-1
{
temp=nums[i];//待排数保存
for(j=i-1;j>=0&&nums[j]>temp;j--)//每趟最多比较次数 i
{
nums[j+1]=nums[j];//如果扫描到的数大于待排数,则向后移一位
}
nums[j+1]=temp;
}
}
最坏情况:整个序列是逆序的时候,则内层循环的条件temp<nums[j]始终成立,此时对于每一次外层循环,内层循环次数每次达到最大值(即内层循环位i次),外层循环i取值为1~i-1,所以总的执行次数为n(n-1)/2 ,时间复杂度为O(n^2)。
最好情况:整个序列为正序的时候。内层循环条件始终不成立,所以内层循环始终不执行,始终执行语句nums[j+1]=temp。所以时间复杂度为O(n)。
空间复杂度:算法所需的辅助存储空间不随待排序列的规模变化而变化,是个常量,所以为O(1)。
二分插入排序:
二分插入排序算法实质上是对直接插入排序的一种优化,在排序数量大的情况下,速度远高于直接插入排序。
基本思想:以待排关键字所在位置将序列分为有序数列和无序数列两部分,然后对有序数列进行折半查找,找出一个点,左边的序列都是小于待排序关键字,该点与其右边至待排关键字的序列都是大于待排关键字的,将右边序列右移然后插入空处。
//二分插入排序
public static void binaryInsertSort(int[] nums)
{
for(int i=1;i<nums.length;i++)//排序n-1趟
{
//优化,减少不必要的查找,待排序关键字>待排序前一关键字,说明已经有序,反之则要排序
if(nums[i]<nums[i-1])
{
int temp=nums[i];
int left=0;
int right=i-1;
while(left<=right)
{
int mid=(left+right)/2;
//left左边的都是比待排序关键字小的
if(nums[mid]<temp)
{
left=mid+1;
}else
{
right=mid-1;
}
}
//将left起到待排关键字的右边的序列全部向右移动一位
for(int j=i;j>left;j--)
{
nums[j]=nums[j-1];
}
nums[left]=temp;//插入有序数列
}
}
}
最坏情况:整个序列是逆序的时候,则内层循环的条件left<=right.始终成立,此时对于每一次外层循环,第一个内循环达到最大值(log21~log2(n-1)),第二个内层循环次数每次达到最大值(即内层循环位i次),外层循环i取值为1~i-1,所以总的执行次数为n(n-1)/2 +nlog2n,时间复杂度为O(n^2)。
最好情况:整个序列为正序的时候。内层循环条件始终不成立,所以内层循环始终不执行,始终执行语句if(nums[i]<nums[i-1]),所以时间复杂度为O(n)。
空间复杂度:算法所需的辅助存储空间不随待排序列的规模变化而变化,是个常量,所以为O(1)。
冒泡排序:
基本思想:每次相邻两个关键字进行比较(0与1,1与2依次比较大小),小数上浮,大数下沉,每趟排序找出最大的数换到最右边。
//冒泡排序
public static void bubbleSort(int[] nums){
for(int i=0;i<nums.length-1;i++)
{
int flag=0;//flag用来标记本趟排序是否发生了交换
for(int j=0;j<nums.length-1-i;j++)
{
int temp=nums[j];
if(nums[j]>nums[j+1])
{
nums[j]=nums[j+1];
nums[j+1]=temp;
flag=1;//flag=1表示本次排序发生了交换
}
}
System.out.print("冒泡第" + i + "趟排序:");
printArray(nums);
if(flag==0)//如果没有发生交换,说明序列有序,排序结束
{
return;
}
}
}
flag用来标记本趟排序是否发生了交换
for(int j=0;j<nums.length-1-i;j++)
{
int temp=nums[j];
if(nums[j]>nums[j+1])
{
nums[j]=nums[j+1];
nums[j+1]=temp;
flag=1;//flag=1表示本次排序发生了交换
}
}
System.out.print("冒泡第" + i + "趟排序:");
printArray(nums);
if(flag==0)//如果没有发生交换,说明序列有序,排序结束
{
return;
}
}
}
最坏情况:序列逆序,此时内层循环if语句的条件始终成立,基本操作执行的次数为n-i。i取值为1~n-1,所以总的执行次数为(n-1+1)(n-1)/2=n(n-1)/2,所以时间复杂度为O(n^2)。
最好情况:序列正序。此时内层循环的条件比较语句始终不成立,不发生交换,内层循环执行n-1次,所以时间复杂度为O(n)。
空间复杂度为O(1)。
简单选择排序:
基本思想:第i次遍历序列,找到最小的数,与第i个数交换。
//简单选择排序
public static void selectSort(int[] nums)
{
for(int i=0;i<nums.length-1;i++)
{
int j=0;
int temp;
int k=i;//保存每趟找到的最小数
for(j=i+1;j<nums.length;j++)
{
if(nums[k]>nums[j])
{
k=j;
}
}
if(k!=i)//如果找到了最小数,并且不是当前待排数关键字,交换
{
temp=nums[i];
nums[i]=nums[k];
nums[k]=temp;
}
System.out.print("选择第" + i + "趟排序:");
printArray(nums);
}
}
时间复杂度:O(n^2)
空间复杂度:O(1)
快速排序:
基本思想:
第一步:i=0,选出一个基准数key=nums[i](可以是第一个,也可以是中间的,都无所谓)保存,第二步:(j=nums.length-1)从右至左遍历序列直到遍历到的这个数小于该基准数,将该数放到基准数的位置(nums[i]=nums[j];(i++)),第三步:从左至右遍历序列直到遍历到的数大于该基准,将该数移动到之前第二步替换基准数的那个数的位置nums[j]=nums[i] ;j++。然后重复2,3步,直到i=j,将基准数放到i这个位置
public void quickSort(int[] a,int low,int high){
if(low>high)
return;
int i,j,key;
i=low;
j=high;
key=a[low];
while(i<j){
while(i<j&&a[j]>key){
j--;
}
if(i<j){
a[i++]=a[j];
}
while(i<j&&a[i]<key){
i++;
}
if(i<j){
a[j--]=a[i];
}
}
a[i]=key;
quickSort(a,low,i-1);
quickSort(a,i+1,high);
}
还没写完 持续更新中……