java中的排序总结
1、冒泡排序(有m个数,就要进行m-1趟比较;在第j趟要进行m-j次两两比较)
static void bubbleSort(int[] array)
{
int temp;
int flag=0;//设置标志,如果第一次循环比较时没有发生交换,则说明数组是升序排序,不用排序,提前结束循环
for(int i=0;i<array.length-1;i++)//外层循环控制循环次数
{
for(int j=0;j<array.length-1-i;j++)//内层循环控制每次循环里的比较次数
{
if(array[j]>array[j+1])
{
temp=array[j];
array[j]=array[j+1];
array[j+1]=temp;
flag=1;
}
}
if(flag==0)
{
System.out.println("No Sort\n");
break;
}
}
}
2、选择排序
void selectionsort(int[] a)
2 {
3 int i,j;
4 int k;
5 int tmp;
6
7 for(i = 0; i < a.length-1; i++)//控制循环次数,m个数需要m-1次循环
8 {
9 k = i;
10 for(j = i+1; j < a.length; j++)
11 {
12 if(a[j] < a[k])
13 k = j;
14 }
15 //i不等于k是就证明a[i]不是最小的,
16 //i等于k时证明a[i]就是本轮比较过程中最小的值
17 if(i != k)
18 {
19 tmp = a[i];
20 a[i] = a[k];
21 a[k] = tmp;
22 }
23 }
24 }
3、插入排序(比较适合少量元素的数组,至少比冒泡排序要快)
插入排序由n-1趟排序组成,对于p=1到n-1趟,插入排序保证从位置0到位置p上的元素为已排序状态。
public static void insertSort(int[] a)
{
int j;
for(int p=1;p<a.length;p++)
{
int tmp=a[p];
for(j=p;j>0&&tmp.compareTo(a[j-1])<0;j--)
{
a[j]=a[j-1];
}
a[j]=tmp;
}
}
4、希尔排序(也称为缩减增量排序)
它通过比较相距一定间隔的元素来工作,各趟比较所用的距离随着算法的进行而减小,直到只比较相邻元素的最后一趟排序为止。
//使用希尔增量的希尔排序
public static void shellSort(int[] a)
{
int j;
for(int gap=a.length/2;gap>0;gap/=2)
{
for(int i=gap;i<a.length;i++)
{
int tmp=a[i];
for(j=i;j>=gap&&tmp.compareTo(a[j-gap])<0;j-=gap)
a[j]=a[j-gap];
a[j]=tmp;
}
}
}
5、堆排序
先建立n个元素的二叉堆,将花费O(n)的时间,然后执行n次deleteMin操作。按照顺序,最小的元素将离开堆。通过将这些元素记录到第二个数组然后再将数组拷贝回来,得到n个元素的顺序。每次deleteMin操作将花费时间O(logn),因此总的运行时间是O(nlogn)。为了避免使用一个附加的数组,在每次使用deleteMin之后,堆缩小1。因此,位于堆中最后的单元可以用来存放刚刚删去的元素。
//将堆中指定序号的两个元素交换
private void swapReferences(int[] a,int b,int c)
{
int temp;
temp=a[b];
a[b]=a[c];
a[c]=temp;
}
//获取节点的左孩子在数组中的位置(数组下标从0开始)
private static int leftChild(int i)
{
return 2*i+1;
}
//下滤:建大顶堆及删除堆顶的最大值
private static void percDown(int[] a,int i,int n)
{
int child;
int tmp;
for(tmp=a[i];leftChild(i)<n;i=child)
{
child=leftChild(i);
if(child!=n-1&&a[child].compareTo(a[child+1]))
child++;
if(tmp.compareTo(a[child])<0)
a[i]=a[child];
else
break;
}
a[i]=tmp;
}
/**
*
*Standard heapSort
*/
public static void heapSort(int[] a)
{
for(int i=a.length/2;i>=0;i--)//build MaxHeap
percDown(a,i,a.length);
for(int i=a.length-1;i>0;i--)
{
swapReferences(a,0,i);//delete max
percDown(a,0,i);
}
}
6、归并排序
这个算法中基本的操纵是合并两个已排序的表,是经典的分治策略。它将问题分为一些小的问题然后递归求解,而治的阶段则将分的阶段解得的各答案修补在一起。分而治之是递归非常有效的用法。
private static void mergeSort(int[] a,int[] tmpArray,int left,int right)
{
if(left<right)
{
int center=(left+right)/2;
mergeSort(a,tmpArray,left,center);
mergeSort(a,tmpArray,center+1,right);
merge(a,tmpArray,left,center+1,right);
}
}
//
private static void merge(int[] a,int[] tmpArray,int leftPos,int rightPos,int rightEnd)
{
int leftEnd=right-1;
int tmpPos=leftPos;//
int numElements=rightEnd-leftPos+1;//元素的个数
//main loop
while(leftPos<=leftEnd&&rightPos<=rightEnd)
{
if(a[leftPos].compareTo(a[rightPos])<=0)
tmpArray[tmpPos++]=a[leftPos++];
else
tmpArray[tmpPos++]=a[rightPos++];
}
//copy rest of left half
while(leftPos<=leftEnd)
tmpArray[tmpPos++]=a[leftPos++];
//copy rest of right half
while(rightPos<=rightEnd)
tmpArray[tmpPos++]=a[rightPos++];
//copy tmpArray back
for(int i=0;i<numElements;i++,rightEnd--)
a[rightEnd]=tmpArray[rightEnd];
}
//mergeSort algorithm
public static void mergeSort(int[] a)
{
int[] tmpArray=new int[a.length];//开辟的一个临时数组,作为桥梁,长度为数组a的长度
mergeSort(a,tmpArray,0,a.length-1);
}
7、快速排序
- 选取枢纽元
枢纽元的最好选择是数组的中值,一般的做法是使用左端、右端和中心位置上 的三个元素的中值作为枢纽元。这样使用三数中值分割法消除了预排序输入的坏情形。 分割策略
第一步:是通过将枢纽元与最后的元素交换使得枢纽元离开要被分割的数据段。
第二步: i从第一个元素开始而j从倒数第二个元素开始。
第三步:当i在j的左边,我们将i右移,移过那些小于枢纽元的元素,并将j左移,移过那些大于枢纽元的元素。当i和j停止时,i指向一个大元素而j指向一个小元素。如果i在j的左边,那么将这两个元素互换,其效果是把一个大元素推向右边而把一个小元素推向左边。重复以上过程直到i和j彼此交错为止。
最后一步:是将枢纽元与i所指向的元素交换。具体算法的实施过程:
我们可以把枢纽元放到a[right-1],并在分割阶段将i和j初始化为left+1和right-2。因为a[left]比枢纽元小,所以将它用作j的警戒标记,不必担心j跑过端点。将枢纽元存储在a[right-1]则也提供一个警戒标记。
//交换指定位置处的元素
private void swapReferences(int[] a,int b,int c)
{
int temp;
temp=a[b];
a[b]=a[c];
a[c]=temp;
}
//三数中值分割法选取枢纽元
private static int median3(int[] a,int left,int right)
{
int center=(left+right)/2;
if(a[center].compareTo(a[left])<0)//顺序不能错
swapReferences(a,left,center);
if(a[right].compareTo(a[left]<0)
swapReferences(a,left,right);
if(a[right].compareTo(a[center])<0)
swapReferences(a,center,right);
swapReferences(a,center,right-1);
return a[right-1];
}
//快速排序的主程序
private static void quickSort(int[] a,int left,int right)
{
if(left+CUTOFF<=right)//对于小数组不使用递归的快速排序,CUTOFF小于20
{
int pivot=median3(a,left,right);
int i=left,j=right-1;
for(;;)
{
while(a[++i].compareTo(pivot)<0){}
while(a[--j].compareTo(pivot)>0){}
if(i<j)
swapReferences(a,i,j);
else
break;
}
swapReferences(a,i,right-1);
quicksort(a,left,i-1);
quicksort(a,i+1,right);
}
else
insertSort(a,left,right);//对于小数组使用插入排序
}
8、快速选择(基于快速排序的思想,只做一次递归调用,平均运行时间是O(N))
private static void quickSelect(int[] a,int left,int right,int k)
{
if(left+CUTOFF<=right)
{
int pivot=median3(a,left,right);
int i=left,j=rigth-1;
for(;;)
{
while(a[++i].compareTo(pivot)<0){}
while(a[--j].compareTo(pivot)>0){}
if(i<j)
swapReferences(a,i,j);
else
break;
}
swapReferences(a,i,right-1);
if(k<=i)
quickSelect(a,left,i-1,k);
else if(k>i+1)
quickSelect(a,i+1,right,k);
else
insertSort(a,left,right);
}
}
9、桶式排序(计数排序)
桶式排序不再是基于比较的排序,待排序列满足以下两个特征:
1)待排序列所有的值处于一个可枚举的范围之内(只能是整形数组)
2)待排序列所在的这个可枚举的范围不应该太大,否则排序开销比较大(当K较大,而数组长度n较小,即K>>n时,辅助数组C[K+1]的空间消耗较大)
当数组为整型,且k和n接近时,可以用此方法排序
桶式排序需要两个数组,第一个buckets数组用于记录“落入”各桶中元素的个数,进而保存各元素在有序序列中的位置,第2个数组用于缓存待排数据;
如果待排序数据的范围在0~K之间,那么它的时间复杂度是O(k+n)
public class BucketSortTest
{
public static int count=0;
public static void main(String[] args)
{
int[] data=new int[]{5,3,6,2,1,9,4,8,7};
bucketSort(data,0,10);
}
public static void bucketSort(int[] data,int min,int max)
{
//缓存数组
int[] tmp=new int[data.length];
//buckets用于记录待排序元素的信息,定义了max-min个桶
int[] buckets=new int[max-min];
//计算每个元素在序列中出现的次数
for(int i=0;i<data.length;i++)
buckets[data[i]-min]++;
//计算“落入”各桶内的元素在有序序列中的位置
for(int i=1;i<max-min;i++)
{
buckets[i]=buckets[i]+buckets[i-1];
}
//将data中的元素完全复制到tmp数组中
System.arraycopy(data,0,tmp,0,data.length);
//根据buckets数组中的信息将待排序的各元素放入相应的位置
for(int k=data.length-1;k>=0;k--=)
data[--buckets[tmp[k]-min]]=tmp[k];
}
}