Java实现常见的7种排序算法

来源:互联网 发布:windows视频剪辑 编辑:程序博客网 时间:2024/06/06 21:01

主要总结一下以下常见的排序以及其java的实现,包含有:

比较排序

冒泡排序

插入排序

希尔排序

快速排序

堆排序

归并排序


1.比较排序:从第0个开始分别与后面的比较,正序不交换,反序就交换。时间复杂度n+(n-1)+.....1=(1+n)n/2=O(n^2)。

/**比较排序 * 2015年10月29日上午9:53:38 * @param data */public static void CompareSort(int[] data){for(int i=0;i<data.length;i++){for(int j=i+1;j<data.length;j++){if(data[i]>data[j]){int change=data[i];data[i]=data[j];data[j]=change;}}}}


2.冒泡排序:最后一个开始 a[n]与a[n-1]比较,正序不交换,反序交换,接着比较a[n-1]与a[n-2] ....遍历一遍可以最小的排到最上面,如此遍历n遍就可以实现排序,从底到上,如冒泡一样。时间复杂度为O(n^2)

/** * 冒泡排序 2015年10月29日上午9:53:38 *  * @param data */public static void bubleSort(int[] data) {for (int i = 1; i <= data.length; i++) {for (int j1 = data.length - 1, j2 = j1 - 1; j2 >= 0; j2--, j1--) {if (data[j1] < data[j2]) {int change = data[j1];data[j1] = data[j2];data[j2] = change;}}}}

   改进:去掉已经排序了的部分

/** * 冒泡排序 2015年10月29日上午9:53:38 *  * @param data */public static void bubleSort(int[] data) {for (int i = 0; i <data.length; i++) {for (int j1 = data.length - 1, j2 = j1 - 1; j2 >i; j2--, j1--) {if (data[j1] < data[j2]) {int change = data[j1];data[j1] = data[j2];data[j2] = change;}}}}

     进一步改进:假如已经没有可以比较的了,退出循环

/** * 冒泡排序 2015年10月29日上午9:53:38 *  * @param data */public static void bubleSort(int[] data) {        Boolean noFinishCompare=true;for (int i = 0; i <data.length&&noFinishCompare; i++) {                noFinishCompare=false;for (int j1 = data.length - 1, j2 = j1 - 1; j2 >i; j2--, j1--) {if (data[j1] < data[j2]) {int change = data[j1];data[j1] = data[j2];data[j2] = change;noFinishCompare=true;//只要有交换就没有完成比较}}}}


3.插入排序:将要排序的数组分成两个部分,一部分是已经排序好了,一部分是原来的,一开始默认已经排序好的只有第一个数组下标。接着从第二个数组开始遍历,判断要排序的元素在已经排序的部分的位置,然后插入。比如判断第二个数组比第一个数组小,插入在左边,比它大插入在右边,确定插入位置的依据是比上一个位置小,不大于下一个位置。时间复杂度为O(n^2);

/**插入排序 * 2015年10月29日上午11:09:33 * @param data */public static void inSertSort(int[] data ){for(int i=1;i<data.length;i++){if(data[i]<data[0]){int change=data[i];//拿出data[i]for(int s=i;s>=1;s--)//从0位起将i前面的后移一位{data[s]=data[s-1];}data[0]=change;//将i放到第一位}if(data[i]>data[0]){int change=data[i];//拿出data[i]for(int s=0;s<i;s++ )//遍历已经排序的部分{  if(change>data[s]&&change<=data[s+1]){//通过大于【i】小于或等于【i+1】来确定要插入的位置                                    for(int e=i;e>=s+1;e--){data[e]=data[e-1];  //右移动留出插入位置}data[s+1]=change;  //插入}}}}}

改进:由于前部分已经排序了的,那么判断a[i]的插入位置可以根据往前推算判断,若a[i-1]>a[i],那么a[i-1]后移以一位,知道a[i-k]<a[i],那么i-k之前的数肯定比i的小,可以将a[i]插入到a[i-k+1]的位置上:

/**插入排序 * 2015年10月29日上午11:09:33 * @param data */public static void inSertSort(int[] data ){for(int i=1;i<data.length;i++){int ai=data[i];int j;for(j=i-1;j>=0&&data[j]>ai;j--){data[j+1]=data[j];}data[j+1]=ai;}}


4.希尔排序:

原理是什么:对于无序数组 a【1】a【2】a【3】a【4】a【5】a【6】

将其划分成子序列: gap=6/2=3;

分别是 :  a【1】a【4】       a【2】a【5】       a【5】a【6】 正序不交换,反序交换

那么通过排序让子序列有序得到:b【1】b【2】b【3】b【4】b【5】b【6】
 可知:b【1】<b【4】  b【2】<b【5】   b【3】<b【6】

接下来 gap=3/2=1;

分别是:b【1】b【2】     b【2】b【3 】  b【3】b【4】     b【4】b【5】    b【5】b【6】

排序后得到序列:c【1】c【2】c【3】c【4】c【5】c【6】

可知c【1】<c【2】  c【2】<c【3】   】 c【3】<c【4】   c【4】<c【5】   c【5】<c【6】

结合b,可得到有序序列。

算法核心:不断的分成几个有序子序列,对子序列进行排序,最后得到基本有序的序列,然后进行一次插入排序。

public static void ShellSort(int[] datas){int gap=datas.length/2;if(datas.length>0)gap=datas.length%2==0?gap-1:gap;while(gap>=1){for(int i=0;i<=datas.length/2;i++){if(datas[i]>datas[i+gap]){int exchange=datas[i+gap];datas[i+gap]=datas[i];datas[i]=exchange;}}gap=gap/2;}inSertSort(datas);}}

希尔排序最终也是用到插入排序,目的就是尽可以有序化,减小插入排序的运算,定义一个变量用于确定它允许的次数:

/**插入排序 * 2015年10月29日上午11:09:33 * @param data */public static void inSertSort(int[] data ){for(int i=1;i<data.length;i++){int ai=data[i];int j;for(j=i-1;j>=0&&data[j]>ai;j--){System.out.println("-----num"+num++);data[j+1]=data[j];}data[j+1]=ai;}}public static void ShellSort(int[] datas){int gap=datas.length/3;if(datas.length>0)gap=datas.length%2==0?gap-1:gap;while(gap>=1){for(int i=0;i<=datas.length/2;i++){if(datas[i]>datas[i+gap]){System.out.println("-----num"+num++);int exchange=datas[i+gap];datas[i+gap]=datas[i];datas[i]=exchange;}}gap=gap/3;}inSertSort(datas);}

测试数据如下:
int[] datas = new int[] {8,15,4,55,98,14,77,35,88,21,546,875,1,65,756,43,4,87,54,11,25,66,78,95,555,423,657,442};

但是调用插入排序,打印出来的num:

-----num125

使用希尔排序的话:

-----num65
而且希尔排序的步长不一样的话运算树也不一样:

public static void ShellSort(int[] datas){int gap=datas.length/3;if(datas.length>0)gap=datas.length%2==0?gap-1:gap;while(gap>=1){for(int i=0;i<=datas.length/2;i++){if(datas[i]>datas[i+gap]){System.out.println("-----num"+num++);int exchange=datas[i+gap];datas[i+gap]=datas[i];datas[i]=exchange;}}gap=gap/2;}inSertSort(datas);}

打印出来的是:

-----num61


5.快速排序:

快速排序也叫分治法,以一个基准数为标准将其分为两个部分,一个是比这个数小的部分,一个是比这个数大的部分,然后让这两个部分按照这样的法则递归下去。

分成两个有序的部分思想是:

【1】【2】【3】【4】....【k】...【n-1】【n】

先以key=【1】为基准数,从n下标往左遍历,获取到第一个比key小的【k】,交换位置:【k】【2】【3】..【m】.....【1】.。。【n-1】【n】

然后从2开始往左遍历,找到第一个比key大的【m】,交换k下标以2下标:【k】【2】【3】..【1】..【m】...【n-1】【n】

可以知道:【1】前面的数都比【1】小,【m】后面的数都比【m】大

然后继续按照这个法则,从【1】下标到【m】的下标进行排序,一直到左边的下标等于右边的下标,最终分成两个部分....【1】....

前面的比【1】小,后面的比【1】大。。

然后分成两个部分递归,从0下标到【1】前一个下标,从【1】的后一个下标到【n】下标。以此下去最终通过实现排序。

/**快速排序 * 2015年10月30日下午1:50:53 *  * @param data */public static void FastSortToHalf(int[] data, int leftFirstPosition,int rightLastPosition) {int size = rightLastPosition;if (leftFirstPosition < rightLastPosition) {while (leftFirstPosition < rightLastPosition) {//通过循环分成两个部分int key = data[leftFirstPosition];while (rightLastPosition > leftFirstPosition&& data[rightLastPosition] >= key) {rightLastPosition--;}if (leftFirstPosition < rightLastPosition) {data[leftFirstPosition] = data[rightLastPosition];data[rightLastPosition] = key;}while (leftFirstPosition < rightLastPosition&& data[leftFirstPosition] <= key) {leftFirstPosition++;}if (leftFirstPosition < rightLastPosition) {int changge = data[leftFirstPosition];data[leftFirstPosition] = data[rightLastPosition];data[rightLastPosition] = changge;}}FastSortToHalf(data, 0, leftFirstPosition - 1);//对这两个部分进行递归FastSortToHalf(data, leftFirstPosition + 1, size);}}


6.堆排序:

堆排序最好最坏以及平均的情况下时间复杂度都是O(nlongn),性能上比比较排序、冒泡排序、插入排序好。

思想:需要构建与保持一个最大堆,然后利用最大堆的性质实现排序。

原理:将要排序的数组看成一个完全二叉树,对于第i个位置,如果他有parent,那么parent位置为i/2,如果他有左子树,那么左子树位置为2*i,如果他有右子树,那么右子树位置为2*i+1,构建最大堆的原理是:如果父节点比它的子节点小,那么将父节点与它子节点的最大节点交换,子节点以此类推,达到父节点永远大于子节点,如此从下往上递归,最终根节点是最大的数。创建了大项堆后,将更节点的值与最后的值交换,将0-n-1下标的项重新构造大项堆,如此递归下去,最终排序。

所以步骤有:1.构造大项堆 2.将根节点与最后项交换 3.递归。


public static void HeapSort(int[] data) {        BuildBiggestHeap(data, data.length);//创建大项堆        for(int i=data.length;i>2;i--)//最后一个长度应该是3个节点        {          swap(data, 0,i-1);//交换根节点与最后项        BuildBiggestHeap(data, i);//重新创建大项堆        }}/**创建大项堆 * 2015年11月2日下午3:21:02 * @param data * @param length */public static void BuildBiggestHeap(int[] data,int length) {if(length>data.length)return;for(int i=length/2-1;i>=0;i--){int parent_position=i;int left_position=i*2+1;int right_position=i*2+2;if(right_position<length){if(right_position==length-1){//这时只有一个左子节点if(data[parent_position]<data[left_position]);swap(data, left_position, parent_position);}else{//有两个子节点int lagerposition=0;lagerposition=data[left_position]>data[right_position]?left_position:right_position;lagerposition=data[lagerposition]>data[parent_position]?lagerposition:parent_position;if(lagerposition!=parent_position)swap(data, parent_position, lagerposition);}}}}     //交换    private static void swap(int[] data, int i, int j) {           int tmp=data[i];           data[i]=data[j];           data[j]=tmp;       } 

7.归并排序:

归并排序是建立在归并操作上的一种有效的排序算法,该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。

排序实现思路是:将数组分成左右两个部分,将两个部分分别进行排序,然后将排序了的两个部分合并。

拆分的思路是通过递归进行不断的拆分,最终拆分成长度为2或者3的序列。

归并的思路是:构造一个缓冲数组,数组长度为要归并的两部分的长度,左部分下标为i,右部分下标为j,数组下标为k,如果a【i】<a【j】,那么将a【i】赋予s【k】,并i++,k++,反之j++,k++。当i或者j长度分配完后,应该讲i或者j剩下的部分按顺序富裕k。

所以代码实现的步骤为:1.拆分.  2.合并   并且对拆分进行递归。

<strong> </strong>private static void MergeSort(int[] data,int firstposition,int mergeiength)    {    if(mergeiength==2)    {    sortlength2(data,firstposition);//长度为2时排序    return;    }    if(mergeiength==3){    sortlength3(data,firstposition);//长度为3时排序    return;    }                    int mergeleftlength=mergeiength/2;    int mergerightlength=mergeiength-mergeleftlength;        MergeSort(data, firstposition, mergeleftlength);//拆分左边进行递归    MergeSort(data, firstposition+mergeleftlength, mergerightlength);//拆分右边进行递归        //拆分排序后将左右两部分合并    int[] temp=new int[mergeiength];//缓存数组,将左右部分合并到缓存数组中    int tempposition=0;        int i=firstposition,j=i+mergeleftlength;    int leftmaxposition=firstposition+mergeleftlength;    int rightmaxposition=firstposition+mergeiength;    while(i<leftmaxposition&&j<rightmaxposition){        if(data[i]>data[j])     {    temp[tempposition++]=data[j++];         }else {     temp[tempposition++]=data[i++];     }        }     while(tempposition<mergeiength)//合并剩余的部分     {     if(i<leftmaxposition)     {     temp[tempposition++]=data[i++];     }     if(j<rightmaxposition){     temp[tempposition++]=data[j++];     }     }            System.arraycopy(temp, 0, data, firstposition, mergeiength);//拷贝到缓存数组的对应位置中    }            private static void sortlength3(int[] data, int firstposition) {sortlength2(data, firstposition);//先排好前两个if(data[firstposition+2]<data[firstposition]){int s=data[firstposition+2]; data[firstposition+2]=data[firstposition+1]; data[firstposition+1]=data[firstposition]; data[firstposition]=s;}if(data[firstposition+2]>data[firstposition]&&data[firstposition+2]<data[firstposition+1]){int s=data[firstposition+2]; data[firstposition+2]=data[firstposition+1]; data[firstposition+1]=s;}}private static void sortlength2(int[] data, int firstposition) {if(data[firstposition]>data[firstposition+1])swap(data, firstposition, firstposition+1);}



0 0