排序算法

1. 插入排序

/**
   * 升序排列:每个数都一步一步往前找到自己的位置,前面的数是排序好的。
   *算法复杂度:O(n*n)
   */
public static void getInsertSort(int[] a) { 
    if(a == null || a.length == 0) {
        System.out.println("该数组为空!"); 
        eturn; 
    } 
    int n = a.length; 
    int temp;
    int j; 
    for(int i = 1; i < n;i++){
        temp = a[i];
        j = i-1; 
        for(; j>=0 && a[j]>temp; --j) {
            a[j+1] = a[j]; 
        } 
        a[j+1]= temp;
    }
}
插入排序

2. 快排

/**
 * 递归方式的快排(分治法):
 * 1. 分解(divide):数组A[p..r]被划分为两个(可能为空)子数组A[p..q-1]和A[q+1..r],使得A[p..q-1]中的每个元素都小于等于A[q],而A[q]也小于等于A[q+1..r]中的每个元素。其中,计算下标q也是划分过程的一部分。
 * 2. 解决(Conquer):通过递归调用快速排序,对子数组A[p..q-1]和A[q+1..r]进行排序。
 * 3. 合并(Combine):因为子数组都是原址排序,所以不需要合并操作:数组A[p..r]已经有序。
  */
public void QuickSort1(int[] a, int p, int r) {
    if (p < r) {
      int q = Partition(a, p, r);
            QuickSort1(a, p, q-1);
            QuickSort1(a,q+1, r);
    }
}

private int Partition(int[] A, int p, int r) {
    int x = A[r]; //取比较值 
    int i = p-1;
    for (int j = p; j < r; j++) {
        if (A[j] <= x) {
                     i++;
          int tmp = A[j];
                    A[j] = A[i];
                    A[i] = tmp;
        }
    }

    int tmp = A[r];
        A[r] = A[i+1];
        A[i+1] = tmp;
    
    return i+1;
}
递归方式
// 随机快排:随机选取主元,没有特定输入引起最差的运行效率
public void Sort(int[] a, int p, int r) {
    if (p < r) {
        int q = Partition(a, p, r);
        Sort(a, p, q-1);
        Sort(a,q+1, r);            
    }
}

private int Partition(int[] A, int p, int r) {
    /*随机选取主元元素*/
    Random random = new Random();
    int random_index = random.nextInt(r-p+1)+p;
    int temp = A[random_index];
    A[random_index] = A[r];
    A[r] = temp;

    int x = A[r];  //pivot = A[p]
    int i = p-1;
    for (int j = p; j < r; j++) {
        if (A[j] <= x) {  //与pivot作比较
            i++;
            int tmp = A[j];
            A[j] = A[i];
            A[i] = tmp;
        }
    }

    int tmp = A[r];
    A[r] = A[i+1];
    A[i+1] = tmp;
    
    return i+1;

}
随机快排
// 递归+双指针
public static void sort(int[] arr, int start, int end) {
   int left = start;
   int right = end;
   int temp = 0;
   if(left <= right) {
        temp = arr[left];         //以左边为基准
        while(left != right) {    //左右两边交替扫描,直到left == right
             while(left < right && arr[right] >= temp) {
                   right--;          //从右往左扫描,直到找到第一个小于基准的位置
             }
             if(left < right) {
                   arr[left] = arr[right];
             }
             while(left < right && arr[left] <= temp) {
                   left++;           //从左往右扫描,直到找到第一个大于基准的位置
             }
             if(left < right) {
                   arr[right] = arr[left];
             }
        }
        arr[right] = temp;
        sort(arr, start, left - 1);   //此时left==right
        sort(arr, right + 1, end);
   }          
}
递归+双指针

3. 归并排序

// 归并排序:分治法
// PS: 当排序规模较小时,插入排序的代价要小于递归。递归到小规模时,就不要递归了,换用插入排序。
public static int[] sort(int[] a,int low,int high){
    int mid = (low+high)/2;
    if(low<high){
        sort(a,low,mid);
        sort(a,mid+1,high);
        //左右归并
        merge(a,low,mid,high);
    }
    return a;
}
 
public static void merge(int[] a, int low, int mid, int high) {
    int[] temp = new int[high-low+1];
    int i= low;
    int j = mid+1;
    int k=0;
    // 把较小的数先移到新数组中
    while(i<=mid && j<=high){
        if(a[i]<a[j]){
            temp[k++] = a[i++];
        }else{
            temp[k++] = a[j++];
        }
    }
    // 把左边剩余的数移入数组 
    while(i<=mid){
        temp[k++] = a[i++];
    }
    // 把右边边剩余的数移入数组
    while(j<=high){
        temp[k++] = a[j++];
    }
    // 把新数组中的数覆盖nums数组
    for(int x=0;x<temp.length;x++){
        a[x+low] = temp[x];
    }
}
归并排序

4. 堆排序

public static void heapSort(int[] arr) {
   //构建大顶堆(一棵完全树的非叶节点的index是len/2-1)
   for(int i = arr.length/2 - 1; i >= 0; i--) {
        sink(arr, i, arr.length);
   }
   //排序,将最大值(根节点)放在最后一位,其它数再推选一个根节点
   for(int i = arr.length - 1; i >= 0; i--) {
        int temp = arr[0];
        arr[0] = arr[i];
        arr[i] = temp;
        sink(arr, 0, i);
   }
}
private static void sink(int[] arr, int index, int len) {
   int temp = arr[index];//主角,上升或下降
   for(int i = 2*index + 1; i < len; i = 2*index + 1) {
        if(i < len - 1 && arr[i] < arr[i+1]) {
             i++;  //儿子中最大的和老爸比
        }
        if(temp >= arr[i]) break;
        arr[index] = arr[i];
        index = i;
   }
   arr[index] = temp;
}
堆排序-升序-大顶堆

5. 计数排序

原理:因为数组的下标是升序的,我们用数据对应辅助数组的下标,便可以得到有序的数据
缺点:空间换时间,当 k 很大时,效果不如基于比较的排序
复杂度:O(n+k),辅助数组为大小为k
public int[] countingSort(int[] A, int n) {
    //找数组中的最大值和最小值,确定桶的个数
    int max=A[0];
    int min=A[0];
    for(int i=0;i<n;i++){
        if(A[i]>max)
            max=A[i];
        if(A[i]<min)
            min=A[i];
    }
    //定义桶数组B
    int[] B= new int[max-min+1];

    //把数组A的元素装到对应桶里
    for(int i=0; i<n; i++){
        B[A[i]-min]++;
    }
    //把所有桶倒出来
    for(int i=0, j=0; j<max-min+1; j++){
        //倒桶j
        for(int k=B[j]; k>0; k--){
            A[i++]=j+min;
        }
    }
    return A;
}
计数排序

6. 桶排序

思想:类似于ConcurrentHashMap的分段处理,将元素放进N个桶中,对每个桶排序(桶与桶在分割的时候是有序的)
优点:降低了内存占用,不用像计数排序那样声明那么大的数组
缺点:如果数据分布不均匀,可能导致元素大量集中在某几个桶中,简化排序的意义不在
public static void bucketSort(int[] arr){
    
    int max = Integer.MIN_VALUE;
    int min = Integer.MAX_VALUE;
    for(int i = 0; i < arr.length; i++){
        max = Math.max(max, arr[i]);
        min = Math.min(min, arr[i]);
    }
    
    //桶数
    int bucketNum = (max - min) / arr.length + 1;
    ArrayList<ArrayList<Integer>> bucketArr = new ArrayList<>(bucketNum);
    for(int i = 0; i < bucketNum; i++){
        bucketArr.add(new ArrayList<Integer>());
    }
    
    //将每个元素放入桶
    for(int i = 0; i < arr.length; i++){
        int num = (arr[i] - min) / (arr.length);
        bucketArr.get(num).add(arr[i]);
    }
    
    //对每个桶进行排序
    for(int i = 0; i < bucketArr.size(); i++){
        Collections.sort(bucketArr.get(i));
    }
    
    System.out.println(bucketArr.toString());
    
}
桶排序

7. 基数排序

基本思想:将整数按位数切割成不同的数字,然后按每个位数分别比较。
具体做法:将所有待比较数值统一为同样的数位长度,数位较短的数前面补零。然后,从最低位开始,依次进行一次排序。这样从最低位排序一直到最高位排序完成以后, 数列就变成一个有序序列。
步骤:
1. 取余10,比个位,排序
2. 除以10 ,取余10,比10位,排序(升位或降位)
......
public class RadixSort {
   // 各位装通法, n为最大元素的length, length为数组大小
    public int[] radixSort(int[] A, int n, int length) {
        int divisor = 1;// 定义每一轮的除数,1,10,100...
        int[][] bucket = new int[10][length];// 定义了10个桶,以防每一位都一样全部放入一个桶中,length为了防止所有数都放在一个桶里
        int[] count = new int[10];// 统计每个桶中实际存放的元素个数
        int digit;// 获取元素中对应位上的数字,即装入那个桶
        for (int i = 1; i <= n; i++) {
            // 经过n次装通操作,排序完成
            for (int temp : A) {// 计算入桶
                digit = (temp / divisor) % 10;
                bucket[digit][count[digit]++] = temp;
            }
            int k = 0;// 被排序数组的下标
            for (int b = 0; b < 10; b++) {// 从0到9号桶按照顺序取出
                if (count[b] == 0)// 如果这个桶中没有元素放入,那么跳过
                    continue;
                for (int w = 0; w < count[b]; w++) {
                    A[k++] = bucket[b][w];
                }
                count[b] = 0;// 桶中的元素已经全部取出,计数器归零
            }
            divisor *= 10;
        }
        return A;
    }
}
基数排序

 

posted @ 2019-07-19 18:00  莹狼  阅读(186)  评论(0编辑  收藏  举报