常见排序算法分析与实现(Java版)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/boker_han/article/details/82469143

交换排序类

1. 冒泡排序
核心提炼:相邻元素,两两比较,按序交换,逐趟进行,给人以水泡逐渐上浮的感觉
时间复杂度:每一趟比较的次数依次为:n-1次、n-2次…1次,所以累加求和得到复杂度为O(n^2)
空间复杂度:只需要一个中间变量temp用于元素交换,故复杂度为O(1)
实现代码:

  /**
     * 冒泡排序(两两比较--交换)
     * @param arr
     */
    public static void bubbleSort(int[] arr){
        if(arr == null || arr.length <= 0){
            return;
        }
        int length = arr.length;
        //如果有一趟比较的过程中没有进行任何元素的交换,说明已经有序,不再进行后续的比较交换工作
        boolean stop = true;
        for(int i = 0 ; i < length - 1 ; i++){
            for(int j = 0 ; j < length - i - 1 ; j++){
                if(arr[j] > arr[j + 1]){
                    swap(arr,j,j+1);
                    stop = false;
                }
            }
            if(stop == true){
                break;
            }
        }
    }
    /**
     * 公用交换方法
     * @param arr
     * @param left
     * @param right
     */
    private static void swap(int[] arr , int left , int right){
        if(arr == null || arr.length <= 0 || left < 0 || right >= arr.length){
            return;
        }
        int temp = arr[left] + arr[right];
        arr[left] = temp - arr[left];
        arr[right] = temp - arr[left];
    }

2. 快速排序
核心提炼:选择分界点元素,将待排序元素按照大小分为两部分,小于分界点元素的以及大于分界点元素的,对这两部分递归进行上述操作,直至所有的元素有序
背后的思想:分治法,分而治之
时间复杂度:O(nlog2(n))
空间复杂度:O(1)
注意:快速排序对于基本有序或有序的待排序数组来说,会退化为冒泡排序,可以对中轴位置的获取进行优化,可以避免这种极端情况
实现代码:

    /**
     * 递归调用快排核心方法进行局部数组的快排
     * @param arr
     * @param begin
     * @param end 
     */
    public static void quickSort(int[] arr,int begin,int end){
        if(arr == null || arr.length <= 0 || begin < 0 || end > arr.length - 1){
            return;
        }
        if(begin < end){
            int pivot = quickSortCore(arr , begin , end);
            quickSort(arr,begin,pivot-1);
            quickSort(arr,pivot+1,end);
        }
    }
    /**
     * 快排核心方法(获取中轴元素位置)
     * @param arr
     */
    public static int quickSortCore(int[] arr,int begin,int end){
        if(arr == null || arr.length <= 0 || begin < 0 || end > arr.length - 1){
            return -1;
        }
        int low = begin;
        int high = end;
        int pivot = arr[begin];
        while(low < high){
            //切记这里需要判断前后位置,并且比较大小时需要考虑两者相等的情况,否则会出现死循环
            while(high > low && pivot <= arr[high]){
                high--;
            }
            arr[low] = arr[high];
            while(high > low && pivot >= arr[low]){
                low++;
            }
            arr[high] = arr[low];
        }
        //一趟快排之后得到的分界点位置
        arr[low] = pivot;
        return low;
    }

选择排序类

3. 简单选择排序
核心提炼:逐趟选择剩余待排序元素中最小的元素,放到有序部分的尾部
时间复杂度:O(n^2)
空间复杂度:O(1)
实现代码:

    /**
     * 简单选择排序(找下标--交换)
     * @param arr
     */
    public static void selectSort(int[] arr){
        if(arr == null || arr.length <= 0){
            return;
        }
        int length = arr.length;
        for(int i = 0 ; i < length - 1 ; i++){
            int min = i;
            for(int j = i + 1 ; j < length ; j++){
                if(arr[j] < arr[min]){
                    min = j;
                }
            }
            if(min != i){
                swap(arr,i,min);
            }
        }
    }

4. 堆排序
核心提炼:完全二叉树为背景,引出大顶堆和小顶堆,关键部分在于初次建堆和后续堆的调整
实现代码:

   /**
     * 堆排序主方法
     * @param arr
     */
    public static void heapSort(int[] arr){
        if(arr == null || arr.length <= 0){
            return;
        }
        int len = arr.length;
        //第一次将给定数组调整为大顶堆,将数组转换为完全二叉树即可得出编码的逻辑,顺序是从右向左
        for(int i = len/2 - 1 ; i >= 0  ; i--){
            adjustMaxHeap(arr,i,len);
        }
        //交换堆顶元素至数组尾部,并对交换后的数组进行大顶堆的调整
        for(int i = len - 1 ; i > 0 ; i--){
            swap(arr,0,i);
            adjustMaxHeap(arr,0,i-1);
        }
    }
    /**
     * 堆排序-调整堆的方法
     * @param arr
     * @param root
     * @param len
     */
    public static void adjustMaxHeap(int[] arr, int root, int len){
        //子树的根节点
        int temp = arr[root];
        //循环处理从当前节点的子树,使其变为大顶堆,顺序是从上往下
        for(int i = 2*root; i < len; i++){
            //判断左右子节点中的较大元素,并将下标更新
            if(i < len-1 && arr[i] < arr[i+1]){
                i++;
            }
            //如果根节点已大于左右子节点,则堆调整完毕
            if(temp >= arr[i]){
                break;
            }
            //将较大的子节点替换为父节点
            arr[root] = arr[i];
            //更新最后待插入的节点下标
            root = i;
        }
        //更新最后的子节点
        arr[root] = temp;
    }

插入排序类

5. 简单插入排序
核心提炼:将待排序元素分为两部分,左边为有序部分(初始时只有第一个元素),右边为待排序部分,依次取出待排序元素与有序部分进行比较,寻找合适的插入位置进行插入
实现代码:

    /**
     * 简单插入排序(无序变有序--找位置插入)
     * @param arr
     */
    public static void insertSort(int[] arr){
        if(arr == null || arr.length <= 0){
            return;
        }
        int length = arr.length;
        for(int i = 1 ; i < length ; i++){
            int temp = arr[i];
            int j = i - 1;
            for( ; j >= 0 ; j--){
                if(arr[j] > temp){
                    arr[j + 1] = arr[j];
                }else{
                    break;
                }
            }
            arr[j+1] = temp;
        }
    }

6. 希尔排序
核心提炼:简单插入排序的改进版,采用缩小增量的插入排序,将待排序元素分段进行排序,直至最终的完全有序
代码实现:


7. 归并排序
核心提炼:完全二叉树为背景,分治思想
实现代码:

       public static void mergeSort(int[] arr){
        if(arr == null || arr.length <= 0){
            return;
        }
        int[] temp = new int[arr.length];
        mergeSortCore(arr,temp,0,arr.length-1);
    }
    private static void mergeSortCore(int[] arr, int[] temp, int low, int high) {
        int mid = (low + high) / 2;
        if(low == high){
            temp[low] = arr[low];
            for(int i = low; i <= high ; i++){
                arr[i] = temp[i];
            }
        }else{
            mergeSortCore(arr,temp,low,mid);
            mergeSortCore(arr,temp,mid+1,high);
            mergeCore(arr,temp,low,mid,high);
            //注意,一定要将已排序部分更新到原数组中
            for(int i = low; i <= high ; i++){
                arr[i] = temp[i];
            }
        }
    }

    private static void mergeCore(int[] arr, int[] temp, int low, int mid, int high) {
        int begin = mid + 1;
        int index = low;
        while(low <= mid && begin <= high){
            if(arr[low] < arr[begin]){
                temp[index] = arr[low];
                low++;
            }else{
                temp[index] = arr[begin];
                begin++;
            }
            index++;
        }
        while(low <= mid){
            temp[index] = arr[low];
            index++;
            low++;
        }
        while(begin <= high){
            temp[index] = arr[begin];
            index++;
            begin++;
        }
    }

8. 基数排序
9. 桶排序

猜你喜欢

转载自blog.csdn.net/boker_han/article/details/82469143