排序算法总结

冒泡排序:首轮冒泡后最大的元素在最右边,次轮最大的元素在倒二,最终得到有序的数据

复杂度:(N-1)+(N-2)+...+1 = N*(N-1)/2,数据量较大的时候是 O(N2) 时间级别。

 

选择排序:每一次从待排序的数据元素中选出最小的一个元素,存放在序列的起始位置,直到全部待排序的数据元素排完。

复杂度:N*(N-1)/2,数据量较大的时候是 O(N2) 时间级别。

 

插入排序:每一步将一个待排序的记录,插入到前面已经排好序的有序序列中去,直到插完所有元素为止。

复杂度:对大致有序的数据性能高,否知性能低

 

递归:在运行的过程中调用自己。

/**
 * 0!=1  1!=1
 * 负数没有阶乘,如果输入负数返回-1
 * @param n
 * @return
 */
public static int getFactorialFor(int n){
    int temp = 1;
    if(n >=0){
        for(int i = 1 ; i <= n ; i++){
            temp = temp*i;
        }
    }else{
        return -1;
    }
    return temp;
}

 

二分查找:查找的数组一定是有序的才行

 

 

分治法:当我们求解某些问题时,由于这些问题要处理的数据相当多,或求解过程相当复杂,使得直接求解法在时间上相当长,或者根本无法直接求出。对于这类问题,我们往往先把它分解成几个子问题,找到求出这几个子问题的解法后,再找到合适的方法,把它们组合成求整个问题的解法。如果这些子问题还较大,难以解决,可以再把它们分成几个更小的子问题,以此类推,直至可以直接求出解为止。这就是分治策略的基本思想。

 

归并排序:归并两个已经有序的数组,归并两个有序数组A和B,就生成了第三个有序数组C。数组C包含数组A和B的所有数据项。

 

public static int[] mergeSort(int[] c,int start,int last){
    if(last > start){
        //也可以是(start+last)/2,这样写是为了防止数组长度很大造成两者相加超过int范围,导致溢出
        int mid = start + (last - start)/2;
        mergeSort(c,start,mid);//左边数组排序
        mergeSort(c,mid+1,last);//右边数组排序
        merge(c,start,mid,last);//合并左右数组
    }
    return c;
}
 
public static void merge(int[] c,int start,int mid,int last){
    int[] temp = new int[last-start+1];//定义临时数组
    int i = start;//定义左边数组的下标
    int j = mid + 1;//定义右边数组的下标
    int k = 0;
    while(i <= mid && j <= last){
        if(c[i] < c[j]){
            temp[k++] = c[i++];
        }else{
            temp[k++] = c[j++];
        }
    }
    //把左边剩余数组元素移入新数组中
    while(i <= mid){
        temp[k++] = c[i++];
    }
    //把右边剩余数组元素移入到新数组中
    while(j <= last){
        temp[k++] = c[j++];
    }
     
    //把新数组中的数覆盖到c数组中
    for(int k2 = 0 ; k2 < temp.length ; k2++){
        c[k2+start] = temp[k2];
    }
}

消除递归:大多数都是用栈来实现递归的。

 

希尔排序

 

  直接插入排序,首先我们将需要插入的数放在一个临时变量中,这也是一个标记符,标记符左边的数是已经排好序的,标记符右边的数是需要排序的。接着将标记的数和左边排好序的数进行比较,假如比目标数大则将左边排好序的数向右边移动一位,直到找到比其小的位置进行插入。这里就存在一个效率问题了,如果一个很小的数在很靠近右边的位置,比如上图右边待排序的数据 1 ,那么想让这个很小的数 1 插入到左边排好序的位置,那么左边排好序的数据项都必须向右移动一位,这个步骤就是将近执行了N次复制,虽然不是每个数据项都必须移动N个位置,但是每个数据项平均移动了N/2次,总共就是N2/2,因此插入排序的效率是O(N2)。

 

快速排序

  对冒泡排序的一种改进,采用的是分治策略(一般与递归结合使用),以减少排序过程中的比较次数:

 

  一、先通过第一趟排序,将数组原地划分为两部分其中一部分的所有数据都小于另一部分的所有数据原数组被划分为2份

 

  二、通过递归的处理, 再对原数组分割的两部分分别划分为两部分,同样是使得其中一部分的所有数据都小于另一部分的所有数据。 这个时候原数组被划分为了4份

 

  三、就1,2被划分后的最小单元子数组来看,它们仍然是无序的,但是! 它们所组成的原数组却逐渐向有序的方向前进。

 

  四、这样不断划分到最后,数组就被划分为多个由一个元素或多个相同元素组成的单元,这样数组就有序了。

  这里涉及到基准元素左游标右游标三个概念。

下面用生动的图片来描述:

public class QuickSort {
     
    //数组array中下标为i和j位置的元素进行交换
    private static void swap(int[] array , int i , int j){
        int temp = array[i];
        array[i] = array[j];
        array[j] = temp;
    }
     
    private static void recQuickSort(int[] array,int left,int right){
        if(right <= left){
            return;//终止递归
        }else{
             
            int partition = partitionIt(array,left,right);
            recQuickSort(array,left,partition-1);// 对上一轮排序(切分)时,基准元素左边的子数组进行递归
            recQuickSort(array,partition+1,right);// 对上一轮排序(切分)时,基准元素右边的子数组进行递归
        }
    }
     
    private static int partitionIt(int[] array,int left,int right){
        //为什么 j加一个1,而i没有加1,是因为下面的循环判断是从--j和++i开始的.
        //而基准元素选的array[left],即第一个元素,所以左游标从第二个元素开始比较
        int i = left;
        int j = right+1;
        int pivot = array[left];// pivot 为选取的基准元素(头元素)
        while(true){
            while(i<right && array[++i] < pivot){}
             
            while(j > 0 && array[--j] > pivot){}
             
            if(i >= j){// 左右游标相遇时候停止, 所以跳出外部while循环
                break;
            }else{
                swap(array, i, j);// 左右游标未相遇时停止, 交换各自所指元素,循环继续
            }
        }
        swap(array, left, j);//基准元素和游标相遇时所指元素交换,为最后一次交换
        return j;// 一趟排序完成, 返回基准元素位置(注意这里基准元素已经交换位置了)
    }
     
    public static void sort(int[] array){
        recQuickSort(array, 0, array.length-1);
    }
     
    //测试
    public static void main(String[] args) {
        //int[] array = {7,3,5,2,9,8,6,1,4,7};
        int[] array = {9,9,8,7,6,5,4,3,2,1};
        sort(array);
        for(int i : array){
            System.out.print(i+" ");
        }
        //打印结果为:1 2 3 4 5 6 7 7 8 9
    }
}

优化:对快速排序来说,拥有两个大小相等的子数组是最优的情况,为了找到一个数组中的中值数据,一般是取数组中第一个、中间的、最后一个,选择这三个数中位于中间的数。

 

posted @ 2018-11-14 09:03  Eric Lan  阅读(203)  评论(0编辑  收藏  举报