Java 八大排序算法 总结 (完整代码可直接运行)

0.算法复杂度比较

在这里插入图片描述

1.直接插入排序

插入排序(Insertion Sort) 是一种简单直观的排序算法。它的工作原理是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。插入排序在实现上,在从后向前扫描过程中,需要反复把已排序元素逐步向后挪位,为最新元素提供插入空间。

class Insert {
    
    
    public static int[] insert(int[] arr) {
    
    
        int length = arr.length;
        for (int i=0; i<arr.length; i++) {
    
    
            int tmp = arr[i];
            int j;//j从当前的i开始向前遍历
            for (j=i; j>0 && arr[j-1]>tmp; j--) {
    
    //i是当前值,如果arr[j-1]比当前值大,arr[j-1]就要向后移一位。
                arr[j] = arr[j-1];
            }
            arr[j] = tmp;//当前的j就是应该插入的位置;
        }
        return arr;
    }
    public static void main(String[] args){
    
    
        int[] arr = new int[]{
    
    3,6,8,4,5,7,11};
        arr = insert(arr);
        System.out.println(Arrays.toString(arr));
    }    
}

参考:
https://blog.csdn.net/qq_28081081/article/details/80594386

2.Shell排序

希尔排序(Shell Sort) 也叫做缩小增量排序,是插入排序的改进版本。 它通过先设置一个增量n,大小为数组长度的一半,将间隔为n的元素视作一个组,然后对每个组内部的元素进行从小到大进行插入排序;然后再将增量n缩小一半,再次进行分组插入排序,直到增量n为1,因为增量为1的时候,所有的元素都为同一个组了。

class Shell{
    
    
    public static int[] shellSort(int[] arr){
    
    
        // 1. 获取数组长度
        int length = arr.length;
        // 2.获取初始的间隔长度
        int interval = (int)Math.floor(length/2.0);
        // 3. 不断地缩小间隔的大小,进行分组插入排序
        while (interval>=1) {
    
    
            // 4. 从 arr[interval] 开始往后遍历,将遍历到的数据与其小组进行插入排序
            for (int i=interval;i<length;i++) {
    
    
                int tmp = arr[i];//tmp后值
                int j = i;
                while (j-interval>=0 && arr[j-interval]>tmp){
    
    //前值比后值大的情况
                    arr[j] = arr[j-interval];//后值用前值代替
                    j = j-interval;
                }
                arr[j] = tmp;
            }
            // 5. 缩小间隔
            interval = (int)Math.floor(interval/2.0);
        }
        return arr;
    }
    public static void main(String[] args){
    
    
        int[] arr = new int[]{
    
    3,6,8,4,5,7,11,3,3,3,3,2,1};
        arr = shellSort(arr);
        System.out.println(Arrays.toString(arr));
    }
    
}

参考:https://blog.csdn.net/l_ppp/article/details/108855298?utm_medium=distribute.pc_feed.none-task-blog-vip_agree_hot-1.nonecase&depth_1-utm_source=distribute.pc_feed.none-task-blog-vip_agree_hot-1.nonecase

3.直接选择排序

选择排序(Selection sort) 是一种简单直观的排序算法。它的工作原理是每一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,所以称为选择排序。
设第一个元素为比较元素,依次和后面的元素比较,比较完所有元素找到最小的元素,将它和第一个元素互换。重复上述操作,我们找出第二小的元素和第二个位置的元素互换,以此类推找出剩余最小元素将它换到前面,即完成排序。

class Select {
    
    
    public static int[] select_sort(int[] arr) {
    
    
        //第一层for表示循环选择的遍数
        int length = arr.length;
        for (int i=0; i<length-1; i++) {
    
    
            int iMin = i;//将起始元素设为最小元素
            // 第二层for表示最小元素和后面的元素逐个比较
            for (int j=i+1; j<length; j++) {
    
    
                if (arr[j] < arr[iMin]){
    
    
                    //如果当前元素比最小元素小,则把当前元素角标记为最小元素角标
                    iMin = j;
                }
            }
            // 查找一遍后将最小元素与起始元素互换
            if (iMin!=i){
    
    
                int tmp = arr[iMin];
                arr[iMin] = arr[i];
                arr[i] = tmp;
            }
        }
        return arr;
    }
    public static void main(String[] args){
    
    
        int[] arr = new int[]{
    
    3,6,8,4,5,7,11};
        arr = select_sort(arr);
        System.out.println(Arrays.toString(arr));
    }    
}

参考:https://www.cnblogs.com/pythonbao/p/10787340.html

4.堆排序

堆排序(Heap Sort) 的基本思想是: 1、将带排序的序列构造成一个大顶堆,根据大顶堆的性质,当前堆的根节点(堆顶)就是序列中最大的元素;
2、将堆顶元素和最后一个元素交换,然后将剩下的节点重新构造成一个大顶堆;
3、重复步骤2,如此反复,从第一次构建大顶堆开始,每一次构建,我们都能获得一个序列的最大值,然后把它放到大顶堆的尾部。最后,就得到一个有序的序列了。

class Heap {
    
    
    public static void swap(int[] arr, int i, int j) {
    
    
        int t = arr[i];
        arr[i] = arr[j];
        arr[j] = t;
    } 
    public static void heapSort(int[] arr){
    
    
        int length = arr.length;
        // 1.构建大顶堆数组
        buildMaxHeap(arr,length);
        // 2.交换堆顶和当前末尾的节点,重置大顶堆
        for (int i=length-1; i>0; i--) {
    
    
            swap(arr,0,i);
            length--;
            heapify(arr,0,length);
        }
        return;
    }
    public static void buildMaxHeap(int[] arr,int length) {
    
    
        int first = (int)Math.floor(length/2.0)-1;//最后一个非叶子结点开始向前遍历
        for (int i=first; i>=0; i--) {
    
    
            heapify(arr,i,length);
        }
        return;
    }
    public static void heapify(int[] arr, int i, int length) {
    
    
        // 先根据堆性质,找出它左右节点的索引
        int left = 2 * i + 1;
        int right = 2 * i + 2;
        // 默认当前节点(父节点)是最大值。
        int largestIndex = i;
        if (left<length && arr[left]>arr[largestIndex]){
    
    
            largestIndex = left;
        }
        if (right<length && arr[right]>arr[largestIndex]){
    
    
            largestIndex = right;
        }
        if (largestIndex != i){
    
    //需要交换arr[i]和arr[largestIndex]
            swap(arr,i,largestIndex);
            //因为互换之后,子节点的值变了,如果该子节点也有自己的子节点,仍需要再次调整。
            heapify(arr, largestIndex, length);
        }
    }
    public static void main(String[] args){
    
    
        int[] arr = new int[]{
    
    7,7,6,5,4,3,1,2};
        heapSort(arr);
        System.out.println(Arrays.toString(arr));
    }
    
    
}

参考:https://blog.csdn.net/qq_28063811/article/details/93034625

5.冒泡排序

冒泡排序(Bubble Sort)
1.比较相邻的元素。如果第一个比第二个大,就交换他们两个。
2.针对所有的元素重复以上的步骤,除了最后一个。
3.持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。

class Bubble{
    
    
    public static int[] bubble(int[] arr){
    
    
        int length = arr.length;
        // 第一层for表示循环选择的遍数
        for (int i=0; i<length-1; i++){
    
    
            for (int j=0; j<length-1-i; j++){
    
    
                if (arr[j] > arr[j+1]){
    
    
                    int tmp = arr[j];
                    arr[j] = arr[j+1];
                    arr[j+1] = tmp;
                }
            }
        }
        return arr;
    }
    public static void main(String[] args){
    
    
        int[] arr = new int[]{
    
    3,6,8,4,5,7,11};
        arr = bubble(arr);
        System.out.println(Arrays.toString(arr));
    }
}

参考: https://www.cnblogs.com/king-ding/p/bubblesort.html

6.快速排序

6.1基本快排

快速排序(Quick Sort) 通过一趟排序将序列分成左右两部分,其中左半部分的的值均比右半部分的值小,然后再分别对左右部分的记录进行排序,直到整个序列有序。
基本快排以首个元素为基准点,设置快慢指针,遇到快指针的值小于基准点的时候,将其与慢指针的值互换位置。最后慢指针的位置最后一个小于temp的元素,将其和start位置的元素(即tmp)互换。

class Quick1 {
    
    
    public static void quickSort(int[] arr, int start, int end){
    
    
        if (start<end){
    
    //递归调用
            int index = basicSort(arr, start, end);
            quickSort(arr, start, index-1);
            quickSort(arr, index+1, end);
        }
        return;
    }
    public static int basicSort(int[] arr, int start, int end){
    
    
        int tmp = arr[start];
        int j = start;                    //慢指针从start开始
        for (int i=start+1; i<end+1; i++){
    
    //快指针从start+1开始
            if (arr[i]<tmp){
    
                  //将比tmp小的尽可能往前移动,即如果arr[i]比tmp小,arr[i]就和arr[j+1]互换位置
                int t = arr[j+1];
                arr[j+1] = arr[i];
                arr[i] = t;
                j++;
            }
        }//最后j指向的位置是最后一个小于temp的元素,将其和start位置的元素(即tmp)互换
        int t2 = arr[j];
        arr[j] = arr[start];
        arr[start] = t2;
        return j;
    }
    public static void main(String[] args){
    
    
        int[] arr = new int[]{
    
    3,3,8,4,5,7,11};
        quickSort(arr,0,arr.length-1);
        System.out.println(Arrays.toString(arr));
    }
}

参考:https://blog.csdn.net/weixin_37275456/article/details/103348299

6.2二路快排

二路快排和基本快排只有从左向右的指针不同,它使用左右两个指针分别指向start+1 和end ,让数组arr左面的值尽可能小于基准点(首个元素)tmp,右面的值尽可能大于tmp。如果遇到arr[left]>=tmp并且arr[right]<=tmp,那么交换二者的值,然后继续移动,直到右指针小于左指针。最后right指向的位置是最后一个小于temp的元素,将其和start位置的元素(即tmp)互换。

class Quick2 {
    
    
    public static void quickSort2(int[] arr, int start, int end){
    
    
        if (start<end){
    
    
            int index = basicSort(arr, start, end);
            quickSort2(arr, start, index-1);
            quickSort2(arr, index+1, end);
        }
        return;
    }
    public static int basicSort(int[] arr, int start, int end){
    
    
        int tmp = arr[start];
        int left = start + 1;//左指针
        int right = end;//右指针
        while (true){
    
    //让arr左面的值尽可能小于tmp 右面的值尽可能大于tmp
            while (left<=end && arr[left]<tmp){
    
    
                left++;
            }
            while (right>=start+1 && arr[right]>tmp){
    
    
                right--;
            }
            //退出循环条件
            if (left>right) {
    
    
                break;
            }
            //此时说明arr[left]>=tmp并且arr{right]<=tmp,那么交换二者的值
            int t = arr[left];
            arr[left] = arr[right];
            arr[right] = t;
            //然后指针开始移动
            left++;
            right--;    
        }//最后right指向的位置是最后一个小于temp的元素,将其和start位置的元素(即tmp)互换
        int t2 = arr[right];
        arr[right] = arr[start];
        arr[start] = t2;
        return right;
    }
    public static void main(String[] args){
    
    
        int[] arr = new int[]{
    
    3,3,8,4,5,7,11};
        quickSort2(arr,0,arr.length-1);
        System.out.println(Arrays.toString(arr));
    }    
    
}    

参考:https://blog.csdn.net/weixin_37275456/article/details/103348299

6.3三路快排

以首个元素为基准点,将元素分为小于 v ,等于 v , 大于 v 三个部分。而元素i则指向当前进行比较的元素
设三个区间:区间[l+1,lt]是小于v的元素,区间[lt+1,i-1]则表示的是等于v的元素,从最右边的索引r处开始往内形成的区间[gt,r]存放的是大于v的元素。
i的开始索引指向l+1,lt的初始值是l,而gt的初始值则是r+1。
当排序开始时 如果当前i 指向的元素 等于v,i+1; 如果当前i 指向的元素 小于v,那么就将 lt+1 与索引 i处的值进行交换, 然后lt+1, 并且 i+1; 如果当前元素 大于 v,那么 就将 gt - 1 处的元素与 当前元素 交换,然后gt-1.
最后当i 走到 gt 处 即 gt==i 时 ;那就说明 除了第一个元素之外,其余的区间已经分区完毕,只要将首个元素与 lt处的元素进行交换, 然后lt -1 ;我们就形成了想要的三个区间,小于v,等于v,然后是大于v的.

class Quick3 {
    
    
    public static void quickSort3(int[] arr, int start, int end){
    
    
        if (start<end){
    
    
            Map<String,Integer> mp = basicSort(arr, start, end);
            quickSort3(arr, start, mp.get("left"));
            quickSort3(arr, mp.get("right"), end);
        }
        return;
    }
    public static Map<String,Integer> basicSort(int[] arr, int start, int end){
    
    
        //区间 [start+1,left]是小于tmp的元素,区间[left+1,i-1]则表示的是等于tmp的元素,从最右边的索引r处开始往内,形成的区间[right,end]存放的是大于tmp的元素;
        int tmp = arr[start];
        int left = start;     //指针1
        int right = end+1;    //指针2
        for (int i=left+1; i<right; ){
    
      //i是指针3
            if (arr[i]==tmp){
    
      //如果当前i指向的元素 等于tmp,那很好,i+1;
                i++;
            }else if(arr[i]>tmp){
    
    //如果当前元素大于tmp,那么就将right-1处的元素与当前元素 交换,然后gt-1.
                int t = arr[i];
                arr[i] = arr[right-1];
                arr[right-1] = t;
                right--;
            }else{
    
    //如果当前i 指向的元素 小于tmp,那么就将 left+1 与索引 i处的值 进行交换,然后lt+1, 并且 i+1;
                int t2 = arr[i];
                arr[i] = arr[left+1];
                arr[left+1] = t2;
                left++;
                i++;
            }
        }
        int t3 = arr[start];
        arr[start] = arr[left];
        arr[left] = t3;
        // left--;
        Map<String,Integer> mp = new HashMap<>();
        mp.put("left",left);
        mp.put("right",right);
        System.out.println(mp);
        return mp;
    }
    public static void main(String[] args){
    
    
        int[] arr = new int[]{
    
    7,6,5,4,3,2,1};
        quickSort3(arr,0,arr.length-1);
        System.out.println(Arrays.toString(arr));
    } 
}  

参考:https://www.jianshu.com/p/c61d1e82c44b

7.归并排序

归并排序(Merge Sort) 是建立在归并操作上的一种有效,稳定的排序算法,该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。

class Merge{
    
    
    public static int[] divide(int[] arr){
    
    
        if (arr.length<=1) {
    
    //继续分不下去了
            return arr;
        }
        int length = arr.length;
        int mid = (int)Math.floor(length/2.0);
        return merge(divide(Arrays.copyOfRange(arr,0,mid)),Arrays.copyOfRange(arr,mid,arr.length));
    }
    public static int[] merge(int[]x, int[] y){
    
    
        List<Integer> re = new ArrayList<Integer>();
        int px = 0, py = 0;
        while (px<x.length && py<y.length){
    
    
            if (x[px]>y[py]){
    
    
                re.add(y[py]);
                py++;
            }else if(x[px]<y[py]){
    
    
                re.add(x[px]);
                px++;
            }else{
    
    
                re.add(x[px]);
                re.add(y[py]);
                px++;
                py++;
            }
        }
        while (px<x.length) {
    
    
            re.add(x[px]);
            px++;
        }
        while (py<y.length) {
    
    
            re.add(y[py]);
            py++;
        }
        int[] res = re.stream().mapToInt(Integer::valueOf).toArray();
        return res;
    }
    public static void main(String[] args){
    
    
        int[] arr = new int[]{
    
    3,6,8,4,5,7,11};
        arr = divide(arr);
        System.out.println(Arrays.toString(arr));
    }
}

8.计数排序

计数排序不是一个比较排序算法,该算法于1954年由 Harold H. Seward提出,通过计数将时间复杂度降到了O(N)
第一步:找出原数组中元素值最大的,记为max。
第二步:创建一个新数组count,其长度是max加1,其元素默认值都为0。
第三步:遍历原数组中的元素,以原数组中的元素作为count数组的索引,以原数组中的元素出现次数作为count数组的元素值。
第四步:创建结果数组result,起始索引index。
第五步:遍历count数组,找出其中元素值大于0的元素,将其对应的索引作为元素值填充到result数组中去,每处理一次,count中的该元素值减1,直到该元素值不大于0,依次处理count中剩下的元素。
第六步:返回结果数组result

class Bucket {
    
    
    public static int[] bucket(int[] arr) {
    
    
        int length = arr.length;
        int size = Collections.max(Arrays.stream(arr).boxed().collect(Collectors.toList()));//获得最大值
        int[] result = new int[length];
        int[] bucket = new int[size+1];//长度为数组的最大值加一
        for (int i=0; i<arr.length; i++){
    
    //统计每个元素的出现次数
            bucket[arr[i]]++;
        }
        int j = 0;
        for (int i=0; i<bucket.length; i++){
    
    //根据每个元素出现次数得到结果
            int tmp = bucket[i];//tmp是出现次数,i是元素的值
            while (tmp>0){
    
    
                result[j] = i;
                j++;
                tmp--;
            }
        }
        return result;
    }
    public static void main(String[] args){
    
    
        int[] arr = new int[]{
    
    3,3,8,4,5,7,11};
        arr = bucket(arr);
        System.out.println(Arrays.toString(arr));
    }      
}

参考:https://www.cnblogs.com/xiaochuan94/p/11198610.html

猜你喜欢

转载自blog.csdn.net/qq_36937684/article/details/109106348