排序

常见的排序列表
image.png

测试代码及其他封装算法

   //Test
    public static void main(String[] args) {
        int[] testArry = {2, 3, 5, 1, 6, 4, 7, 9, 10, 6, 25, 48, 59, 63, 55, 88, 46, 98, 37, 12, 16, 17, 23, 34};
        int[] rightResult = {1, 2, 3, 4, 5, 6, 6, 7, 9, 10, 12, 16, 17, 23, 25, 34, 37, 46, 48, 55, 59, 63, 88, 98};//参照用

        long t1 =System.currentTimeMillis();
        boolean flag=true;
        for (int i = 0; i < 100; i++) {
            int[] testLargeArray=generateRandomArray();
            int[] rightLargeArray=new int[9999];
            System.arraycopy(testLargeArray,0,rightLargeArray,0, rightLargeArray.length);
            shell(testLargeArray);
            Arrays.sort(rightLargeArray);
            if(!(Arrays.equals(testLargeArray,rightLargeArray))){
                flag=false;
                break;
            }
        }
        long t2 =System.currentTimeMillis();

        print(testArry);
        System.out.println();
        System.out.println("排序结果为: "+flag);
        System.out.println("排序所用时间为: "+(t2-t1));
    }
    //随机生成较大的数组
    public static int[] generateRandomArray(){
        Random random = new Random();
        int[] testLargeArray=new  int[9999];
        for (int i = 0; i < 9999; i++) {
            testLargeArray[i] = random.nextInt(9999);
        }
        return testLargeArray;
    }
    //打印
   public static  void print(int[] arr){
        for (int i = 0;i<arr.length; i++ ){
            System.out.print(arr[i]+" ");
        }
    }
    //交换
    public static void swap(int[] arr, int i ,int j){
        int temp;
        temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
    }

选择排序

最简单,效率差,不稳定

//选择排序  测试用时:2723ms
public static void xuanze(int[] arr){
        for (int i = 0; i < arr.length-1 ; i++) {
            int minPos = i;
            for(int j = i+1; j < arr.length; j++ ){
                minPos = arr[minPos] > arr[j] ? j : minPos;
            }
            int temp;
            temp = arr[i];
            arr[i] = arr[minPos];
            arr[minPos] = temp;
        }
    }

冒泡排序

优化版本:加一个flag标志,若某次循环时已排好,跳出循环

    //冒泡排序  9694ms
    public void maopao(int[] arr) {
        for (int i = arr.length; i > 0; i--) {
            boolean flag = true;
            for (int j = 0; j < i - 1; j++) {
                if (arr[j] > arr[j + 1]) {
                    swap(arr, j, j + 1);
                    flag = false;
                }

            }

            if (flag) {//第一次这里犯了一个低级错误:flag=true,Orz)
                break;
            }
        }
    }

插入排序

 //插入排序 1694ms
    public static void charu(int[] arr){
        for (int i = 1; i < arr.length; i++) {
            for (int j = i; j > 0 && arr[j]<arr[j-1]; j--) {
                swap(arr,j,j-1);
            }
        }
    }

希尔排序

插入排序的优化算法

 //希尔排序 249ms
    public  static void  shell(int[] arr){
        for (int gap = arr.length/2; gap >= 1; gap /= 2) {
            for (int i = gap; i < arr.length; i++) {
                for (int j = i; j >= gap && arr[j]<arr[j-gap]; j-=gap) {
                    swap(arr,j,j-gap);
                }
            }
        }
    }

归并排序

抽象的理解为二叉树的后序遍历

    //归并排序 299
    public static void merge(int[] arr, int left, int right) {
        int[] temp = new int[right - left + 1];
        int mid = left + (right - left) / 2;
        int i = left;    //找了好久的bug,一开始写成了i=0,报错数组越界
        int j = mid + 1;
        int m = 0;
        if (left == right) return;
        merge(arr, left, mid);
        merge(arr, mid + 1, right);
        while (i <= mid && j <= right) {
            temp[m++] = arr[i] > arr[j] ? arr[j++] : arr[i++];
        }
        while (i <= mid) temp[m++] = arr[i++];
        while (j <= right) temp[m++] = arr[j++];
        for (int k = 0; k < temp.length; k++) {
            arr[left + k] = temp[k];
        }
    }

快速排序

抽象的理解为二叉树的前序遍历

//快排 206ms
public static void quickSort(int[] arr, int left, int right) {
    if (left < right) {
        int index =getIIndex(arr,left,right);
        quickSort(arr, left, index - 1);
        quickSort(arr, index + 1, right);
    }
}
public static int getIIndex(int[] arr,int left, int right){
    int low = left;
    int high = right;
    int temp = arr[low];
    while (low < high) {
        while (arr[high] >= temp && low < high) high--;
        arr[low] = arr[high];
        while (arr[low] <= temp && low < high) low++;
        arr[high] = arr[low];
    }
    arr[low] = temp;
   return low;
}

计数排序

//计数排序 78ms
//适合量大范围小,需要提前知道范围
public static int[] jishu(int[] arr){
        int[] result = new int[arr.length];
        int[] count = new int[10];
    for (int i = 0; i < arr.length; i++) {
         count[arr[i]]++;
    }
    /*
    这里提前已经分配好了result数组的大小,不需要考虑其长度,只需专注于count数组的长度即可
    这里i为count数组的下标,j为count数组的下标
     */
    for (int i=0, j =0;i<count.length;i++) {
        while (count[i]-->0){
            result[j++]=i;
        }
    }
    return result;
}

基数排序

一开始bug原因:对--count理解错误

//基数排序  165ms
//这里以待排序数有4位数举例
    public static int[] baseSort(int[] arr){
        int[] result = new int[arr.length];
        int[] count = new int[10];//必是10
        for (int i = 0; i < 4; i++) {
            int div = (int)(Math.pow(10,i));
            for (int j = 0; j < arr.length; j++) {
                count[arr[j]/div%10]++;
            }

            //处理数组得到每段数的最后位置
            for (int m = 1; m < count.length; m++) {
                count[m] = count[m] +count[m-1];
            }
            //倒序扫描数组,使排序算法稳定,同样适用于上面的计数排序
            //bug记录:一开始数组下标忘记减1了
            for (int n = arr.length -1; n >= 0 ; n--) {
                result[--count[arr[n]/div%10]] = arr[n];
            }

            //一开始把下面这两句话漏了
            System.arraycopy(result,0,arr,0,arr.length);
            Arrays.fill(count,0);
        }
        return result;
    }

堆排序

大体可分为两步,①初始化建堆和②排序重建堆
第一大步的时间复杂度为O(n)第二大步的时间复杂度约为log(n!)和nlogn是同阶函数,这也是它的时间复杂度。
两个问题:1. 调整堆后,若调整的父节点的子节点还有子节点,则子节点也应该调 虽然是从下至上,从左至右的顺序调整(也可简单理解为从后向前调整),但任然无法保证从原来父节点换下来的“小”节点比子节点小。
2. ②步中无需再像①步一样从第一个非叶子节点建堆,只需要从根节点调整即可
上代码:

  //堆排序 218ms
    public static void heapSort(int[] arr){
        for (int i = arr.length/2 - 1; i >=0 ; i--) {
            adjustHeap(arr,i,arr.length);
        }
        for (int j = arr.length-1; j > 0 ; j--) {
            swap(arr,0,j);
            adjustHeap(arr,0,j);
        }

    }
    public static void adjustHeap(int[] arr,int i,int length){
        int temp = arr[i];
        for (int k = 2*i+1; k < length; k=2*k+1) {//还有子节点的话,继续调整
            //重点:这里前后的顺序千万不能反,否则会报错数组越界!!!!!
            if(k+1<length&&arr[k]<arr[k+1])k++;//这里一开始if括号里忘记加k+1<length.
            if(arr[k]>temp){//这里应该时temp不是a[i]
                arr[i]=arr[k];
                i=k;
            } else {
                break;
            }
        }
        arr[i]=temp;//这里一开始写成了了a[k]报错,原因:k的作用域只在for循环内
    }

最终完整排序算法项目截图:
image.png

Q.E.D.