八大排序算法(冒泡排序、快速排序、堆排序.....)

每坚持一天,offer就会离我更近一步

文章目录

    • 冒泡排序
    • 选择排序
    • 插入排序
    • 希尔排序
    • 快速排序
    • 计数排序
    • 堆排序
    • 归并排序

冒泡排序

算法描述:从第一个元素开始,两两比较,如果前者比后者大,那么就将两者进行交换,这样每经过一次排序,都能找到一个最大的元素并且把它放在数组的最后。
稳定性:稳定
时间复杂度:O(N^2)
代码:

public static int[] bubbleSort(int[] arr){

        for (int i = 0; i < arr.length - 1; i++) {
//            比较次数要减去i,每比较一次都会把最大的排到最后
            for (int j = 0; j < arr.length - 1 - i; j++) {
//                如果前边元素比后边的元素大就交换
                if(arr[j+1] < arr[j]){
                    int temp = arr[j+1];
                    arr[j+1] = arr[j];
                    arr[j] = temp;
                }
            }
        }
        return arr;
    }

选择排序

算法描述:在每一次排序时,先假设第一个元素是最小的,然后从第二个元素一直遍历到最后一个元素,如果找到比第一个小的元素就进行交换,否则不交换。第一次排序时,有序区间中只有一个元素;第二次排序,有序区间中有两个元素,依次递增,直到所有元素都有序
时间复杂度:O(N^2)
稳定性:不稳定
代码:

public static void selectSort(int[] arr){
        for(int i = 0;i < arr.length - 1;i++){
//            假设第一个元素是最小的
            int minIndex = i;
            for (int j = i+1; j < arr.length; j++) {
//                再从无序的元素中找到比arr[minIndex]小的元素,进行交换
                if (arr[j]<arr[minIndex]){
                    int temp = arr[j];
                    arr[j] = arr[minIndex];
                    arr[minIndex] = temp;
                }
            }
        }

    }

插入排序

算法描述:,初始时,先把第一个元素看成是有序的,然后把未排序的元素一个一个插入到有序序列中。
时间复杂度:O(N^2)
稳定性:稳定

public static void insertSort(int[] arr){
//        将arr[0]看作是有序的,所以从arr[1]开始遍历
        for (int i = 1; i < arr.length; i++){
//        暂存待插入元素
            int inserted = arr[i];

            int j = i - 1;
//            从后往前开始比较,如果待插入元素更小就将有序元素后移
            for (; j >= 0 && inserted < arr[j]; j--) {
                arr[j+1] = arr[j];
            }
//        将待插入元素插入
            arr[j+1] = inserted;
        }
    }

希尔排序

改进了插入排序

public static void shellSort(int[] arr){
        int n = arr.length;
        for(int gap = n/2;gap > 0; gap /= 2){
            for (int i = gap; i < n; i++) {
                int inserted = arr[i];
                int j=i-gap;
                for (; j >= 0&&arr[j]>inserted; j-=gap) {
                    arr[j+gap] = arr[j];
                }
                arr[j+gap] = inserted;
            }
        }

    }

快速排序

算法描述:选择第一个元素当哨兵,一个左指针一个右指针,左指针从左开始扫描,遇到比哨兵大的元素就停;右指针从右开始扫描,遇到比哨兵小的元素就停,然后交换两个元素,直到左指针和右指针相遇,并交换相遇处的元素和哨兵。然后分别进行左半部分和右半部分的排序,同样的过程。
时间复杂度:O(NlogN)
稳定性:不稳定
代码:

public static void quickSort(int[] arr,int begin,int end){
        if (begin > end) return;
//        第一个元素当作哨兵
        int pivot = arr[begin];

        int i = begin;
        int j = end;
        while (true){
//            从后往前找比哨兵小的
            while (arr[j] >= pivot && i < j)
                j--;
//            从前往后找比哨兵大的
            while (arr[i] <= pivot && i < j)
                i++;
//            两个指针相遇就暂停
            if (i >= j){
                break;
            }
//            指针相遇后交换元素
            int temp = arr[i];
            arr[i] = arr[j];
            arr[j] = temp;
        }
//          交换哨兵和指针相遇处的元素
        arr[begin] = arr[i];
        arr[i] = pivot;
//        对左半部分元素进行排序
        quickSort(arr,begin,i-1);
//        对右半部分元素进行排序
        quickSort(arr,i+1,end);
    }

计数排序

算法描述:从初始数组中选取一个最大的元素,用它来做辅助数组的长度,然后遍历初始数组,统计每一个元素出现的次数并保存到辅助数组中,然后遍历辅助数组,取出元素并覆盖初始数组即可。
时间复杂度:O(N)
空间复杂度:O(N)
适用于元素范围较小的数组
代码:

public static void countSort(int[] arr){
        int max = Integer.MIN_VALUE;
//        找出数组中最大的数
        for(int num:arr){
            if(num > max) max = num;
        }
//      初始化计数数组,统计每个元素所出现的次数
        int[] temp = new int[max+1];
        for(int i=0;i<arr.length;i++){
            temp[arr[i]]++;
        }
//      取出计数数组中的元素,覆盖原来的数组
        int k = 0;
        for(int i=0;i<temp.length;i++){
            for(int j = temp[i];j > 0;j--){
                arr[k++] = i;
            }
        }
    }

堆排序

算法描述:先根据给定数组建一个大根堆(小根堆也可以),然后每次取堆顶元素和堆尾元素进行交换,此时取下来的堆顶元素就是当前数组中最大的,然后再次维护堆的性质,执行n次之后数组即为有序。
时间复杂度:O(NlogN)(建堆的时间复杂度是O(N),维护堆性质的时间复杂度是O(logN)
稳定性:不稳定
代码:

//    堆排序入口
    public static void heapSort(int[]arr){
        int n = arr.length;

        //建立堆,大根堆
        //        注意,建完堆之后是局部有序的,建堆的复杂度是O(n)
//        从n/2-1开始的原因:下标为i的节点的父节点下标是(i-1)/2,当i=n-1时,代入得i=n/2-1
        for( int i = n/2-1;i>=0;i--){
            heapify(arr,n,i);
        }

        //        排序
        //每次取堆中最大元素并维护堆的性质
        for( int i=n-1;i>0;i--){
            int temp = arr[i];
            arr[i] = arr[0];
            arr[0] = temp;
            heapify(arr,i,0);
        }
    }
//        比较的具体过程
    public static void heapify(int[] arr,int n,int i){
//        父节点下标
        int largest = i;
//        左孩子下标
        int lson = 2 * i + 1;
//        右孩子下标
        int rson = 2 * i + 2;
//        比较父节点和左孩子,左孩子下标必须小于n,如果左孩子大,则替换父节点
        if(lson<n&&arr[largest]<arr[lson]){
            largest = lson;
        }
//        比较父节点和右孩子,右孩子下标必须小于n,如果右孩子大,则替换父节点
        if(rson<n&&arr[largest]<arr[rson]){
            largest = rson;
        }
//        如果不满足大根堆的性质,则进行交换
//        将父节点和左右孩子中较大的元素交换
        if(largest!=i){
            int temp = arr[largest];
            arr[largest] = arr[i];
            arr[i] = temp;
//            向下继续比较
            heapify(arr,n,largest);
        }
    }

归并排序

算法描述:采用递归与分治的思想,先把数组按照两两划分的原则对数组分割,直到每个分组都是一个元素,然后再按照之前分割的顺序比较大小之后进行两两合并。
时间复杂度:O(NlogN)
稳定性:稳定
代码:

public static void merge(int[] arr,int[] tempArr,int left,int mid,int right){
//        标记左半区第一个未排序的元素
        int l_pos = left;
//        标记右半区第一个未排序的元素
        int r_pos = mid + 1;
//        临时数组元素的下标
        int pos = left;
//        开始合并
        while (l_pos <= mid&&r_pos <= right){
            if(arr[l_pos] < arr[r_pos])
                tempArr[pos++] = arr[l_pos++];
            else
                tempArr[pos++] = arr[r_pos++];
        }
//        可能左半区还有剩余元素
        while (l_pos <= mid)
            tempArr[pos++] = arr[l_pos++];
//        可能右半区还有剩余元素
        while (r_pos <= right)
            tempArr[pos++] = arr[r_pos++];
//        把临时数组的元素复制回主数组
        while (left <= right){
            arr[left] = tempArr[left];
            left++;
        }

    }


    public static void msort(int[] arr,int[] tempArr,int left,int right){
//        如果只有一个元素则不进行划分
        if(left < right){
            int mid = (left+right)/2;
            //递归划分左半部分
            msort(arr,tempArr,left,mid);
            //递归划分右半部分
            msort(arr,tempArr,mid+1,right);
            //合并
            merge(arr,tempArr,left,mid,right);
        }
    }

    public static void mergeSort(int[] arr){
        int n = arr.length;
//        分配一个辅助数组
        int[] tempArr = new int[n];
//        开始归并排序,传入左边界和右边界
        msort(arr,tempArr,0,n-1);
    }

八大排序算法(冒泡排序、快速排序、堆排序.....)_第1张图片
整理也挺费时间的,如果觉得对你有帮助,点个赞再走吧!感谢收看!

你可能感兴趣的:(算法,排序算法,算法,数据结构,java)