在排序算法中,分为稳定排序和不稳定排序。一个算法是否稳定,根据排序前后排序前后相同数的相对位置是否发生变化来判断。相对位置变化的称为不稳定排序,不变化的称为稳定排序。稳定排序分为以下四类:
- 冒泡排序(Bubble):以升序为例。从前向后每次比较相邻的两个数,大的放后面,小的放前面。第一趟跑完后,最大值在最后一位,最大值的位置已经放好。下一次再进行调整时跑的长度减一。直到长度减少到一或者某次跑的过程中任意两个相邻的数都没有交换位置,则说明排成升序列。
public class Bubble {
public static void bubbleSort(int[] a)
{
//标记,某一次跑完都未进行值交换。market为false,跳出循环
boolean market=true;
for(int i=0;i<a.length-1&&market;i++)
{
market=false;
for(int j=0;j<a.length-1-i;j++)//冒泡
{
if(a[j+1]<a[j])
{
int t=a[j+1]+a[j];
a[j]=a[j+1];
a[j+1]=t-a[j];
market=true;//说明跑的过程中有发生了值交换
}
}
}
}
public static void main(String[] args) {
int[] a={4,26,-45,30,19,5,-56,20,68,100,9999};
bubbleSort(a);
for(int i=0;i<a.length;i++)
{
System.out.println(a[i]);
}
}
}
时间复杂度为O(N2),空间复杂度为O(1)
- 插入排序(Insert):将一个数组中的数分为无序空间和有序空间。每一次取出无序空间中的第一个,在有序空间中找一个合适的位置进行插入即可。取出即为对该数进行备份。以升序为例,将备份的数和前面有序空间中的数进行逐位比较,若比有序空间的某个数小,将该数向后复制一位,继续向前比较,直到找到小于等于它的某个数。然后将备份的数放到该数后面。类似于站队,路人甲在已经按个子高低排好的队伍里找自己的位置。他先和最高的人比较,发现自己没人家高,然后和次高的比,发现还是没人家高。直到找到比他低的或和他一样的才停下,要站到人家后面,因此比他高的所有人要向后退一位给他让出空间。
public static void InsertSort(int[] a)
{
for(int i=1;i<a.length;i++)
{
int t=a[i],j;
for(j=i-1;j>-1&&t<a[j];a[j+1]=a[j],j--);//a[j+1]=a[j]表示把比他大的向后复制
//for循环跑完后找到和他相等或比他小的数,然后将他放在这个数的后面
a[j+1]=t;
}
}
public static void main(String[] args) {
int[] a={3,2,5,8,4,7,6,9};
InsertSort(a);
for(int i=0;i<a.length;i++)
{
System.out.println(a[i]);
}
}
时间复杂度为O(N2),空间复杂度为O(1)。
- 归并排序(Merge):归并排序运用到了分治算法的思想,对一个数组先进行分,后进行和。对一个无序的数组进行划分,划分成两个,然后对左边的再进行划分。划分到不能继续进行划分时(即两个无序子数组中都只有一个数),将两个子数组合并成一个升序数组。再用这个升序数组和其他的升序数组合并成一个新的升序数组。
public static int[] a={5,4,6,9,10,0,20,65,2};//存储原数组
public static void mergeSort(int start,int end)//里面的1、2、3表示写的时候的步骤
{
//3.判断返回条件
if(start==end)//划分的区间里只有一个数
{
return;
}
int mid=(start+end)/2;
//1.划分子区间
mergeSort(start,mid);//左区间,返回后左区间有序
mergeSort(mid+1,end);//右区间,返回后右区间有序
//2.将start到mid区间和(mid+1)到end区间合并成一个升序列,放到b的start到end
int i=start,j=mid+1,k=0;
int[] b=new int[end-start+1];//辅助空间
while(i<=mid||j<=end)
{
if(i<=mid&&j<=end)
{
b[k++]=a[i]<a[j]?a[i++]:a[j++];//将a[i]和a[j]中较小的放到b中
}else if(i<=mid){
b[k++]=a[i++];
}else{
b[k++]=a[j++];
}
}
//将b复制到a的start到end段
for(k=0;k<=end-start;k++)
{
a[start+k]=b[k];
}
}
public static void main(String[] args) {
mergeSort(0,a.length-1);
for(int i=0;i<a.length;i++)
{
System.out.println(a[i]);
}
}
使用了二分法,时间复杂度为O(Nlog2N),在两个子序列合并成升序列时额外申请了空间,因此空间复杂度为O(N)。
- 基数排序(Radix):先用每个数的个位进行。以升序为例,有0-9十个桶,按照个位数将数组中的每个数放到相应的桶中。在从0开始,将桶中的每个数按照顺序取出放回原数组中。若最大值有十位,在按十位进行以上操作。没有十位的数十位当做0,将其放到0号桶中。若最大值有百位,则再进行以上操作。直到最大值中的最高位进行过此操作。排序结束。
//纯顺序表式存储,也可以用顺序表加链表来存储
public static void radixSort(int[] a)
{
int[][] b=new int[10][a.length];
int time=Radix.maxNumdigit(a);//最大值有多少位数
int k=1;
for(int loop=0;loop<time;loop++,k*=10)
{
//将a按照基数放到二维数组中
for (int i = 0; i < a.length; i++) {
int p=a[i]/k%10;//表示a[i]要放在b[p]行
b[p][++b[p][0]]=a[i];
}
//将二维数组中的数放到a中
int q=0;
for(int i=0;i<10&&q<a.length;i++)
{
for(int j=1;j<=b[i][0];j++)
{
a[q++]=b[i][j];
}
b[i][0]=0;
}
}
}
public static int maxNumdigit(int[] a)
{
int num=0;
int max=a[0];
for (int i = 0; i < a.length; i++) {
if(a[i]>max)
{
max=a[i];
}
}
for(;max!=0;max/=10,num++);
return num;
}
public static void main(String[] args) {
int[] a={45,20,99999,22,326,4444,333,100,55555,4,12};
radixSort(a);
for (int i = 0; i < a.length; i++) {
System.out.println(a[i]);
}
}
... | ---The End--- |