Java中排序算法的总结

时间:2022-06-19 20:06:58

鉴于对每一种排序记忆地混乱,由此从各处搜集资料整理如下:

1、选择排序

     对于一组记录,经过第一轮比较后得到最小的记录,然后将该记录与第一个记录的位置进行交换;接着对不包括第一个记录以外的其他记录进行第二轮比较,得到的最小记录并与第二个记录进行交换,重复下去。

//选择排序 时间0(n*n) 不稳定
public class Selectsort {

public static void selectsort(int [] a)
{
int i,j;
int temp;
int n= a.length;
int flag=0;
for(i=0;i<n;i++)
{
temp=a[i];
flag=i;
for(j=i+1;j<n;j++)
{
if(a[j]<temp){
temp=a[j];
flag=j;
}
}
if(flag!=i)
{
a[flag]=a[i];
a[i]=temp;
}
}
}
public static void main(String[] args) {
int i=0;
int []arr={5,6,2,3,8,9};
selectsort(arr);
for(i=0;i<arr.length;i++)
{
System.out.print(arr[i]+" ");
}
}
}

2、插入排序

 初始时假设第一个记录自成一个有序序列,其余的记录为无序,接着从第二个开始,按照记录的大小依次将当前处理的记录插入到之前的有序序列中,直至最后一个记录插入到有序序列中为止。

//直接插入最好时间0(n), 最差0(n*n) 稳定
public class Insert {

public static void insertSort(int[] a)
{
if(a!=null)
{
for(int i=1;i<a.length;i++)
{
int temp=a[i];
int j=i;
if(a[j-1]>temp)
{
while(j>=1 && a[j-1]>temp)
{
a[j]=a[j-1];
j--;
}
}
a[j]=temp;
}
}
}

public static void main(String[] args) {

int [] arr ={3,4,1,2,8,9};
insertSort(arr);
for(int i=0;i<arr.length;i++)
{
System.out.print(arr[i]+" ");
}
}
}


3、冒泡排序

从第一个记录开始依次对相邻的两个记录进行比较,当前面的记录大于后面的记录时,交换位置,进行一轮比较和换位后,n个记录中的最大值记录将位于第n位;然后对前n-1个

记录进行第二轮比较。

//最好时间0(n)  最坏时间0(n*n)   稳定 n小时较好
public class Bubblesort {

public static void Bubblesort(int [] arr)
{
int i=0;
int j=0;
int temp;
int len =arr.length;
for(i=0;i<len-1;i++)
{
for(j=len-1;j>i;j--)
{
if(arr[j]<arr[j-1])
{
temp=arr[j];
arr[j]=arr[j-1];
arr[j-1]=temp;
}
}
}

}

public static void main(String[] args) {
int [] a={6,3,1,8,4};
Bubblesort(a);
for(int i=0;i<a.length;i++)
{
System.out.print(a[i]+" ");
}
}
}

4、归并排序

运用递归的思想。对于给定的一组记录,先将每两个相邻的长度为1的子序列进行归并,得到n/2(向上取整)个长度为2 的或1的有序子序列,再将其亮亮归并,反复执行此过程。

关键: 1:划分半子表 ; 2:合并半子表。 归并排序的计算量和难点在于合,而快排在于分。

public class Mergesort {

public static void MergeSort(int []a ,int p,int len)
{
if(p<len)
{
int q=(p+len)/2;
MergeSort(a,p,q);
MergeSort(a,q+1,len);
Merge(a,p,q,len);
}
}
public static void Merge(int[] a,int p,int q,int len)
{
int i,j,k,n1,n2;
n1=q-p+1;//注意是长度
n2=len-q;
int[] L=new int[n1];
int[] R= new int[n2];
//分别装入数组
for(i=0,k=p;i<n1;i++,k++)
L[i]=a[k];
for(i=0,k=q+1;i<n2;i++,k++)
R[i]=a[k];
for(k=p,i=0,j=0;i<n1&&j<n2;k++)//从p开始装入
{
if(L[i]<R[j])
a[k]=L[i++];
else
a[k]=R[j++];
}
if(i<n1)//将剩下的填满
{
for(j=i;j<n1;j++,k++)
a[k]=L[j];
}
if(j<n2)
{
for(i=j;i<n2;i++,k++)
a[k]=R[j];
}
}
public static void main(String[] args) {
int i=0;
int[] a={6,7,3,22,1,9,2};
int len= a.length;
MergeSort(a,0,len-1);
for(i=0;i<len;i++)
{
System.out.print(a[i]+" ");
}
}
}


5、快速排序

关键:找轴点  就是将所有元素逐个转换成轴点的过程。

轴点划分的均衡性: 最好情况: 平均:0( nlogn) ;     最坏情况 :划分极不均衡,如轴点是最大最小元素  : 0( n*n)与起泡排序相当;    平均性能:0( nlogn);

空间复杂度:需要一个栈空间来实现递归  。

而递归是真正消耗空间: 最优情况 :0([logn]+1) 即每一次都平分数组的情况 ;      最差情况: 0( n) ,即退化为冒泡排序 ;     平均:0( logn);    

public class Quicksort {

public static void quicksort(int [] a, int low,int high)
{
int i,j;
int index;
if(low>=high)
return ;
i=low;
j=high;
index=a[low];
while(i<j)
{
while(i<j && a[j]>=index)
j--;
if(i<j)
a[i++]=a[j];
while(i<j && a[i]<index)
i++;
if(i<j)
a[j--]=a[i];

}
a[i]=index;
quicksort(a,low,i-1);
quicksort(a,i+1,high);

}
public static void main(String[] args) {
int i=0;
int []a ={4,6,8,2,1,0,9,7};
int len=a.length;
quicksort(a,0,len-1);
for(i=0;i<len;i++)
{
System.out.print(a[i]+" ");
}
}

}

关键字的选取:

方式一: 三者取中:首、尾、中

方式二: 取随机数:取左边和右边之间的一个随机数

快速排序和归并排序的区别:

都是“分而治之”的思想 ;  进行的分组策略不同,后面的合并策略也不同。

区别 一:归并排序分组策略是假设待排序的元素存放在数组中,那么其把数组前面一般元素作为一组,后面一半作为另外一组; 而快排则是根据元素的值来分组,大于某一个值

的元素放在一组,而小于的放在另一组;

区别二:快排的合并时,只需把两个分组合并在一起就可,而归并排序则需要对两个有序的数组根据大小合并。


6、希尔排序

又被称为“缩小增量排序”,先将待排序的数组元素分成多个子序列,使得每个子序列的元素个数相对较小,然后对各个子序列分别进行直接插入排序,带整个待排序的序列“基本有序后”,最后再对所有元素进行一次直接插入排序。
//时间复杂度:0( n3/2)
public class Shellsort {

public static void shellsort(int [] a)
{
int len= a.length;
int i;
int j;
int h,temp;
for(h=len/2;h>0;h=h/2)
{
for(i=h;i<len;i++)
{
temp=a[i];
for(j=i-h;j>=0;j-=h)
{
if(temp<a[j])
a[j+h]=a[j];
else
break;
}
a[j+h]=temp;
}
}
}
public static void main(String[] args) {
int i=0;
int [] a={5,3,2,7,0,1,6};
int len =a.length;
shellsort(a);
for(i=0;i<len;i++)
{
System.out.print(a[i]+" ");
}
}
}

7、堆排序

堆是一种特殊的树形数据结构,其每个结点都有一个值,通常提到的堆都是一棵完全二叉树,根节点的值小于或大于两个子节点的值,同时,根节点的两个子树也分别是一个堆。

堆排序是一种树形选择排序,在排序过程中,将R看做一棵完全二叉树的顺序存储结构,利用完全二叉树中父节点和子节点之间的内在联系来选定最小的元素。

分类:大顶堆和小顶堆。每个结点的值都大于或等于其左右孩子结点的值,为大顶堆;每个结点的值都小于或等于其左右孩子结点的值,为小顶堆。

主要包括两个过程:一是构建堆; 二是交换堆顶元素和最后一个元素的位置。

public class HeapSort {

public static void Heapsort(int [] a)
{
int i;
int len=a.length;
for(i=len/2-1;i>=0;i--)
{
Heapadjust(a,i,len-1);
}
for(i=len-1;i>=0;i--)
{
int temp=a[0];
a[0]=a[i];
a[i]=temp;
Heapadjust(a,0,i-1);
}
}
public static void Heapadjust(int[] a,int s,int len)//左孩子先和右孩子比较,再和根结点比较
{
int temp,j;
temp=a[s];
for(j=2*s+1;j<=len;j=j*2+1)
{
if(j<len&&a[j]<a[j+1])
j++;
if(temp>=a[j])
break;
else
{
a[s]=a[j];
s=j;
}
}
a[s]=temp;
}
public static void main(String[] args) {
int i=0;
int []a ={22,34,11,23,78,56};
Heapsort(a);
int len=a.length;
for(i=0;i<len;i++)
{
System.out.print(a[i]+" ");
}
}
}
    在正式排序时,第i次取堆顶记录重建堆需要用0(logi)的时间(完全二叉树某个结点到根结点的距离为[log2 i ]+1)并且需要取n-1次堆顶记录,因此重建堆的时间复杂度为0(nlogn)最好最坏平均均为0(nlogn)。 空间复杂度只有一个用来交换的暂存单元,非常不错,但是不稳定,并且不适合待排序序列个数较小的情况。

8、总结

1)在排序之后仍能够保持之前的相对次序,就是稳定的。其中包含:直接插入、冒泡和归并;

2)时间复杂度为0(n*n)的有直接插入、冒泡、简单选择;为0(nlogn)的有希尔排序、快排、堆排序和归并排序;

3)空间复杂度为0( n) 的归并排序,为0(logn)是快排,其余都为1;

4)虽然直接插入和冒泡的速度比较慢,但是当初始序列整体或者局部有序时,他们会有较高的效率,此时的快排效率会下降;当排序序列较小且不要求稳定性时,直接选择效

率较高;当要求稳定性时,冒泡效率较好;