排序算法总结与代码实现


一、冒泡排序

在这里插入图片描述
时间复杂度:o(n^2)
空间复杂度: o(1)
冒泡排序是原地排序算法,并且是稳定的排序算法

代码

public class BubbleSorter {

    public void sort(int[] data){
        if(data == null || data.length <= 1)    return;
        for(int round = 1 ; round <= data.length ; round++){//控制冒泡排序的轮数
            int compareTimes = data.length - round;
            for(int i = 0 ; i < compareTimes ; i++){//控制每轮的比较次数
                if(data[i] > data[i+1]){//前一个元素大于后一个元素的话进行交换操作
                    int tmp = data[i];
                    data[i] = data[i+1];
                    data[i+1] = tmp;
                }
            }
        }
    }
}

冒泡排序的优化:通过减少交换的次数
思路:如果在某一轮我们发现元素已经是有序的了我们就不用进行后面的排序操作了。

优化后代码

public class BubbleSorter {

    public void sort(int[] data){
        if(data == null || data.length <= 1)    return;
        for(int round = 1 ; round <= data.length ; round++){//控制冒泡排序的轮数
            boolean hasSwap = false;//hasSwap进行标记
            int compareTimes = data.length - round;
            for(int i = 0 ; i < compareTimes ; i++){//控制每轮的比较次数
                if(data[i] > data[i+1]){//前一个元素大于后一个元素的话进行交换操作
                    int tmp = data[i];
                    data[i] = data[i+1];
                    data[i+1] = tmp;
                    hasSwap = true;
                }
            }
            if(!hasSwap)    break;
        }
    }
}

二、选择排序

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
选择排序的特点:
时间复杂度:o(n^2)
空间复杂度: o(1)
选择排序是原地排序算法
选择排序是不稳定的排序算法:所以选择排序使用场景少

代码:

//选择排序
public class SelectionSorter {
    public void sort(int[] data){
        if(data == null || data.length <= 1)    return;
        for(int i = 0 ; i < data.length ; i++){//控制选择排序的轮数
            //找到[i,n)中最小元素的索引
            int minNumIndex = i;
            for(int j = i + 1 ; j < data.length ; j++){
                if(data[j] < data[minNumIndex]){
                    minNumIndex = j;
                }
            }
            //将data[i]和最小元素进行交换
            int tmp = data[i];
            data[i] = data[minNumIndex];
            data[minNumIndex] = tmp;
        }
    }
}

三、插入排序

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
插入排序:
时间复杂度:o(n^2)
空间复杂度:o(1)
原地排序算法,是稳定的排序算法

插入排序代码:

public class InsertionSorter {
    public void sort(int[] data){
        if(data == null || data.length <= 1)    return;
        for(int i = 1 ; i < data.length ; i++){//控制插入排序的轮数
            for(int j = i ; j > 0 ; j--){//j至少是1
                if(data[j] < data[j-1]){
                    int tmp = data[j];
                    data[j] = data[j-1];
                    data[j-1] = tmp;
                }else{
                    //data[j] >= data[j-1]直接跳出循环
                    break;
                }
            }
        }
    }
}

插入排序的优化

public void sort(int[] data){
        if(data == null || data.length <= 1)    return;
        for(int i = 1 ; i < data.length ; i++){//控制插入排序的轮数
            int j;
            int tmp = data[i];
            for(j = i ; j > 0 ; j--){//j至少是1
                if(tmp < data[j-1]){
                    //将较大的元素总是向后移动一位(赋值操作)
                    data[j] = data[j-1];
                }else{
                    //data[j] >= data[j-1]直接跳出循环
                    break;
                }
            }
            //找到data[i]需要插入的位置
            data[j] = tmp;
        }
    }

插入排序性能>选择排序性能>冒泡排序性能。
我们基本不用:选择排序和冒泡排序。
基于赋值的插入排序性能高于基于交换的插入排序。

四、希尔排序

希尔排序是插入排序的优化版本

引入:
大规模乱序的数组使用插入排序会很慢,因为它只会交换相邻的元素

希尔排序特点:
时间复杂度:最坏是o(n^2) 。有些情况是 o(n^3/2)
空间复杂度:o(1)
希尔排序是不稳定的排序算法

核心思想:
使得数组中任意间隔h的元素有序然后对全局进行排序

比如每隔四个元素进行一次排序

间隔h的取值逻辑:h的取值是递增序列
经过数学上的证明:

在这里插入图片描述
在这里插入图片描述
希尔排序代码

public class ShellSorter {

    public void sort(int[] data){
        if(data == null || data.length <= 1)    return;
        //1.计算递增序列
        int n = data.length;
        List<Integer> list = new ArrayList<>();
        int k = 1;
        int h;
        do{
            h = (int)(Math.pow(3,k) - 1) / 2;
            if(h > n / 3)   break;
            list.add(h);//1 , 4 , 13 , 121....
            k++;
        }while (h <= n / 3);

        //2.希尔排序
        for(k = list.size() - 1 ; k >= 0 ; k--){
            h = list.get(k);//h是递增序列的元素
            //将数组变成h有序
            for(int i = h ; i < n ; i++){
                for(int j = i ; j >= h ; j -= h){
                    if(data[j] < data[j-h]){
                        int tmp = data[j];
                        data[j] = data[j-h];
                        data[j-h] = tmp;
                    }else {
                        break;
                    }
                }
            }
        }
    }


    public static void main(String[] args) {
        int[] data = new int[]{19,11,28,32,51,7,8,4,212,4,99,3,3,4,5,89,3030,29,0,293,49,45,29,98,9};
        new ShellSorter().sort(data);
        System.out.println(Arrays.toString(data));
    }
}

希尔排序的优化
空间复杂度是o(1)

public void sort1(int[] data){
        if(data == null || data.length <= 1)    return;
        //1.计算递增序列
       int n = data.length;
       int h = 1;
       while(h < n / 3) h = 3 * h + 1;
       while (h >= 1){
           //2.希尔排序
               for(int i = h ; i < n ; i++){
                   for(int j = i ; j >= h ; j -= h){
                       if(data[j] < data[j-h]){
                           int tmp = data[j];
                           data[j] = data[j-h];
                           data[j-h] = tmp;
                       }else {
                           break;
                       }
                   }
               }
            h /= 3;
       }
    }

五、归并排序(面试重点)

时间复杂度:o(nlongn)
空间:o(n)
归并排序不是原地排序算法
归并排序是稳定的排序算法

在这里插入图片描述
在这里插入图片描述
merge操作:
在这里插入图片描述

递归实现归并排序

public class MergeSorter {

    private void sort(int[] data){
        if(data == null || data.length < 2) return;
        sort(data,0,data.length-1);//大问题
    }

    //用于解决子问题,给子数组进行排序
    private void sort(int[] data , int left , int right){
        //递归终止条件:只有一个元素return
        if(left == right) return;

        int mid = (left + right) / 2;
        sort(data,left,mid);
        sort(data,mid+1,right);
        merge(data,left,mid,right);//合并操作

    }

    //合并两个有序数组的方法
    private void merge(int[] data, int left, int mid, int right) {
        int num = right - left + 1;
        //先申请临时数组
        int[] tmp = new int[num];
        int i = left;//遍历前半部分
        int j = mid + 1;//遍历后半部分
        int tmpPos = 0;//
        while (i <= mid && j <= right){
            if(data[i] <= data[j])  tmp[tmpPos++] = data[i++];
            else    tmp[tmpPos++] = data[j++];
        }

        while(i <= mid) tmp[tmpPos++] = data[i++];//左边还有元素,右边没有元素
        while(j <= right)   tmp[tmpPos++] = data[j++];//右边还有元素,左边没有元素

        //把临时数组的元素tmp拷贝回原始的数组中
        for(tmpPos = 0 ; tmpPos < tmp.length ; left++ , tmpPos++ ){
            data[left] = tmp[tmpPos];
        }

    }
}

空间上进行优化:
一次性申请一个大的数组,合并的时候我们就不申请临时数组了

代码:

//优化:内存上的优化
public class MergeSorter2 {
    private void sort(int[] data){
        if(data == null || data.length < 2) return;
        int[] tmp = new int[data.length];//一次性申请所有空间
        sort(data,0,data.length-1,tmp);
    }

    private void sort(int[] data , int left , int right,int[] tmp){
        if(left == right) return;
        int mid = (left + right) / 2;
        sort(data,left,mid,tmp);
        sort(data,mid+1,right,tmp);
        merge(data,left,mid,right,tmp);

    }

	//开辟数组操作在最开始就执行了
    //进行merge操作不再开辟临时数组
    private void merge(int[] data, int left, int mid, int right,int[] tmp) {

        int tmpPos = left;//不同
        int i = left;
        int j = mid + 1;
        while (i <= mid && j <= right){
            if(data[i] <= data[j])  tmp[tmpPos++] = data[i++];
            else    tmp[tmpPos++] = data[j++];
        }
        while(i <= mid) tmp[tmpPos++] = data[i++];
        while(j <= right)   tmp[tmpPos++] = data[j++];

        //拷贝(不同)
        for(tmpPos = left ; tmpPos <= right ; left++ , tmpPos++ ){
            data[left] = tmp[tmpPos];
        }

    }
}

merge操作的的另一种实现方法:

1.将原始的数组先拷贝到临时的数组
2.将临时数组的元素归并到原始的数组中

代码如下:

public class MergeSorter2 {
    private void sort(int[] data){
        if(data == null || data.length < 2) return;
        int[] tmp = new int[data.length];//一次性申请所有空间
        sort(data,0,data.length-1,tmp);
    }

    private void sort(int[] data , int left , int right,int[] tmp){
        if(left == right) return;
        int mid = (left + right) / 2;
        sort(data,left,mid,tmp);
        sort(data,mid+1,right,tmp);
        merge(data,left,mid,right,tmp);

    }

  
    private void merge2(int[] data, int left, int mid, int right,int[] tmp){
        for(int i = left ; i <= right ; i++){
            tmp[i] = data[i];
        }
        int i = left;
        int j = mid + 1;
        for(int k = left ; k <= right ; k++){
            if(i == mid + 1){//左边没有元素右边有元素
                data[k] = tmp[j++];
            }else  if(j == right + 1){//左边有元素右边没有元素
                data[k] = tmp[i++];
            }else if(tmp[i] <= tmp[j]){
                data[k] = tmp[i++];
            }else{
                data[k] = tmp[j++];
            }
        }
    }

}

归并排序的非递归的代码实现
自低朝上的归并排序(非递归)

public class MergeSorter {

    //自低朝上的归并排序
    public void sortBU(int[] data){
        if(data == null || data.length <= 1)    return;
        int len = data.length;
        for(int size = 1 ; size < len ; size *= 2){
            for(int left = 0 ; left < len - size ; left += 2 * size){
                int right = Math.min(left + 2 * size - 1 , len - 1);
                int mid = left + size - 1;
                merge(data , left , mid , right);
            }
        }
    }


    //合并两个有序数组的方法
    private void merge(int[] data, int left, int mid, int right) {
        int num = right - left + 1;
        //先申请临时数组
        int[] tmp = new int[num];
        int i = left;//遍历前半部分
        int j = mid + 1;//遍历后半部分
        int tmpPos = 0;//
        while (i <= mid && j <= right){
            if(data[i] <= data[j])  tmp[tmpPos++] = data[i++];
            else    tmp[tmpPos++] = data[j++];
        }

        while(i <= mid) tmp[tmpPos++] = data[i++];//左边还有元素,右边没有元素
        while(j <= right)   tmp[tmpPos++] = data[j++];//右边还有元素,左边没有元素

        //把临时数组的元素tmp拷贝回原始的数组中
        for(tmpPos = 0 ; tmpPos < tmp.length ; left++ , tmpPos++ ){
            data[left] = tmp[tmpPos];
        }

    }
    
}

方法2:

public class MergeSorter {

    //自低朝上的归并排序
    public void sortBU(int[] data){
        if(data == null || data.length <= 1)    return;
        int len = data.length;
        int[] tmp = new int[len];
        for(int size = 1 ; size < len ; size *= 2){
            for(int left = 0 ; left < len - size ; left += 2 * size){
                int right = Math.min(left + 2 * size - 1 , len - 1);
                int mid = left + size - 1;
                merge(data , left , mid , right,tmp);
            }
        }
    }


    //merge操作不开辟新数组
    private void merge(int[] data, int left, int mid, int right,int[] tmp) {
        int tmpPos = left;
        int i = left;
        int j = mid + 1;
        while (i <= mid && j <= right){
            if(data[i] <= data[j])  tmp[tmpPos++] = data[i++];
            else    tmp[tmpPos++] = data[j++];
        }
        while(i <= mid) tmp[tmpPos++] = data[i++];
        while(j <= right)   tmp[tmpPos++] = data[j++];

        //拷贝
        for(tmpPos = left ; tmpPos <= right ; left++ , tmpPos++ ){
            data[left] = tmp[tmpPos];
        }

    }

}

六、快速排序(面试重点)

时间复杂度:o(nlogn)
空间复杂度:o(logn) :系统调用栈(递归)
快速排序是原地排序算法
快速排序是不稳定的排序算法
如果对已经有序的数组进行排序,时间复杂度退化成o(n^2)
在这里插入图片描述
步骤:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

快速排序代码:

public class QuickSorter {
    public void sort(int[] data){
        if(data == null || data.length < 2) return;
        sort(data,0,data.length-1);
    }
    //子问题
    private void sort(int[] data , int lo , int hi){
        if(lo >= hi)    return;
        //分区
        int j = partition(data,lo,hi);
        //对左边数组排序
        sort(data,lo,j-1);
        sort(data,j+1,hi);
    }

    //分区
    private int partition(int[] data, int lo, int hi) {
        int pivot = data[hi];
        //小于pivot最后一个元素的下一个元素
        int less = lo;
        //大于pivot最后一个元素
        int great = lo;

        for( ; great <= hi - 1 ; great++){
            if(data[great] < pivot){
                //data[less]和data[great]交换
                int tmp = data[less];
                data[less] = data[great];
                data[great] = tmp;
                less++;
            }
        }

        //data[less]和data[hi]交换
        int tmp = data[less];
        data[less] = data[hi];
        data[hi] = tmp;
        return less;

    }
    
}

优化方法:
最理想的分区点可以是:
1.三数取中
2.随机法


三路快排参考图:
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
三路快排代码:

public class ThreeWayQuickSorter {
    
    public void sort(int[] data){
        if(data == null || data.length < 2) return;
        sort(data,0,data.length-1);
    }

    private void sort(int[] data , int lo , int hi){
        if(lo >= hi) return;

        //分区:
        int pivot = data[hi];
        int less = lo;
        int great = hi;

        int i = lo;
        while(i <= great){
            if(data[i] < pivot){
                int tmp = data[i];
                data[i] = data[less];
                data[less] = tmp;
                less++;
                i++;
            }else if(data[i] > pivot){
                int tmp = data[i];
                data[i] = data[great];
                data[great] = tmp;
                great--;
            }else{
                i++;
            }
        }

        sort(data,lo, less - 1);
        sort(data,great + 1,hi);
    }



    public static void main(String[] args) {
        int[] data = new int[]{33, 33, 18, 18, 21, 1, 98, 100};
        new ThreeWayQuickSorter().sort(data);
        System.out.println(Arrays.toString(data));
    }
}

六、桶排序

时间复杂度:o(nlog(n/m))
空间复杂度:o(n)

1.桶排序不是原地排序算法
2.桶排序是不是稳定的排序算法

2.1取决于对桶内元素进行排序的方法是不是稳定的排序算法

3.桶排序对数据的要求十分苛刻:

3.1桶与桶之间必须是有序的
3.2待排序的数据最好均匀的分配到每个桶中

桶排序的应用:外部排序

外部排序:指对存储在磁盘中的数据进行排序
特点:数据量大,内存有限,无法将数据全部加载到内存中。

桶排序应用图:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
桶排序的过程图:
在这里插入图片描述
桶排序时间复杂度分析图:
在这里插入图片描述

桶排序代码:

public class BucketSorter {

    public void sort(int[] data){
        if(data == null || data.length <= 1)    return;

        //1.创建几个空的桶
        int maxValue = data[0];
        for(int i = 0 ; i < data.length ; i++){
            maxValue = Math.max(maxValue,data[i]);
        }
        int bucketNum = maxValue / 10 + 1;
        ArrayList<Integer>[] buckets = new ArrayList[bucketNum];

        //2.将所有元素添加到对应的bucket里面
        for(int i = 0 ; i < data.length ; i++){
            //计算当前元素应该被分配到那一个桶里面
            int index = data[i] / 10;
            if(buckets[index] == null){
                buckets[index] = new ArrayList<>();
            }
            buckets[index].add(data[i]);
        }

        //3.对桶内的每一个元素进行排序
        for(int i = 0 ; i < bucketNum ; i++){
            ArrayList<Integer> bucketData = buckets[i];
            if(bucketData != null){
                //桶内排序
                Collections.sort(bucketData);
            }
        }

        //4.从buckets中拿到排序后的数组
        int index = 0;
        for(int i = 0 ; i < bucketNum ; i++){
            ArrayList<Integer> bucketData = buckets[i];
            if(bucketData != null){
                for(int j = 0 ; j < bucketData.size() ; j++){
                    data[index++] = bucketData.get(j);
                }
            }
        }
    }
}

七、计数排序

计数排序其实是桶排序的一种特殊情况:

	计数排序中的桶内元素值是一样的,所以我们只需要记录桶内元素个数即可

在这里插入图片描述
在这里插入图片描述
步骤图:

计数:
在这里插入图片描述
计数累加:
在这里插入图片描述
从原数组的最后一个位置开始从后往前扫描元素:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
拷贝操作:
在这里插入图片描述
在这里插入图片描述

代码实现:

public class CountingSort {

    /**
     * 时间复杂度:o(n+k)
     * 空间复杂度:o(n)
     * 顺序遍历会导致排序不稳定
     * 逆序遍历使得排序稳定
     * @param data
     */
    public void sort(int[] data){

        if(data == null || data.length <= 1)    return;
        //1.找到数组中的最大值
        int max = data[0];
        for(int i = 1 ; i < data.length ; i++){//o(n)
            max = Math.max(max , data[i]);
        }
        int[] count  = new int[max+1];
        //2.计数
        for(int i = 0 ; i < data.length ; i++){//o(n)
            count[data[i]]++;
        }
        //3.计数累加
        for(int i = 1 ; i < count.length ; i++){//o(k)
            count[i] += count[i-1];
        }
        //4.计算每一个元素在排序数组中的位置
        int[] output = new int[data.length];
        for(int i = data.length - 1 ; i >= 0 ; i--){
            int j = data[i];
            int k = count[j] - 1;
            output[k] = data[i];
            count[j]--;
        }

        //5.拷贝数组
        for(int i = 0 ; i < data.length ; i++){
            data[i] = output[i];
        }

    }

}

考虑负数的计数排序:
在这里插入图片描述
在这里插入图片描述
可以考虑负数的计数排序代码:

	1.需要记录最小值
	2.在定位count数组时要使用data[i] - min 代替不考虑负数时的 data[i]
/**
 * @author zzyuan
 * @create 2021-07-01 11:29
 */
public class CountingSort {

    /**
     * 时间复杂度:o(n+k)
     * 空间复杂度:o(n)
     * 顺序遍历会导致排序不稳定
     * 逆序遍历使得排序稳定
     * @param data
     */
    public void sort(int[] data){

        if(data == null || data.length <= 1)    return;
        //1.找到数组中的最大值
        int max = data[0];
        int min = data[0];
        for(int i = 1 ; i < data.length ; i++){//o(n)
            max = Math.max(max , data[i]);
            min = Math.min(min , data[i]);
        }
        int[] count  = new int[max - min +1];
        //2.计数
        for(int i = 0 ; i < data.length ; i++){//o(n)
            count[data[i] - min]++;
        }
        //3.计数累加
        for(int i = 1 ; i < count.length ; i++){//o(k)
            count[i] += count[i-1];
        }
        //4.计算每一个元素在排序数组中的位置
        int[] output = new int[data.length];
        for(int i = data.length - 1 ; i >= 0 ; i--){
            int j = data[i];
            int k = count[j - min] - 1;
            output[k] = data[i];
            count[j - min]--;
        }

        //5.拷贝数组
        for(int i = 0 ; i < data.length ; i++){
            data[i] = output[i];
        }

    }

    public static void main(String[] args) {
        int[] data = new int[]{-33, 33, -18, 18, -21, 1, -98, -100};
        new CountingSort().sort(data);
        System.out.println(Arrays.toString(data));
    }
}

八、基数排序

o(n^2):插入,选择,冒泡
o(nlogn):快排,归并
o(n):桶排序,计数排序

基数排序:
从个位开始,对每一位进行排序

计数排序实现图:
在这里插入图片描述

在这里插入图片描述

基数排序代码:

public class RadixSorter {
    public void sort(int[] data){
        if(data == null || data.length <= 1)    return;
        //1.找到最大值
        int max = data[0];
        for(int i = 1 ; i < data.length ; i++){
            max = Math.max(max,data[i]);
        }
        for(int exp = 1 ; max / exp > 0 ; exp *= 10){
            countSort(data,exp);
        }
    }


    private void countSort(int[] data , int exp){
        //之所以为10,是因为数字只有0-9十个数字
        int[] count = new int[10];

        for(int i = 0; i < data.length ; i++){
            //个位数:(234 / 1) % 10 = 4
            //十位数:(234 / 10) % 10 = 3
            //百位数:(234 / 100) % 10 = 2
            int digit = (data[i] / exp) % 10;//个位数
            count[digit]++;
        }

        for(int i = 1 ; i < 10 ; i++){
            count[i] += count[i-1];
        }

        int[] output = new int[data.length];

        for(int i = data.length - 1 ; i >= 0 ; i--){
            int digit = (data[i] / exp) % 10;
            int k = count[digit] - 1;
            output[k] = data[i];
            count[digit]--;
        }

        for(int i = 0 ; i < data.length ; i++){
            data[i] = output[i];
        }

    }

    public static void main(String[] args) {
        int[] data = new int[]{21314,1812,1111,2191,2910,2819,9990};
        new RadixSorter().sort(data);
        System.out.println(Arrays.toString(data));
    }
}

基数排序的特点:

在这里插入图片描述

九、排序算法总结

1.如何写一个通用的排序算法
2.排序算法的比较
3.分治算法思想

排序算法的比较:
排序算法的比较
排序算法的选择:
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

o(n^2)与o(nlogn)对比:
在这里插入图片描述
如何写一个通用的排序算法
在这里插入图片描述
java内置的排序算法:
在这里插入图片描述

十、引用类型的数据比较

在这里插入图片描述

1.实现Comparable接口

public class TestObjectCompare {
    public static void main(String[] args) {
        Person p1 = new Person("douma", 10);
        Person p2 = new Person("laotong", 20);
        int i = p1.compareTo(p2);
        System.out.println(i);
    }
}

class Person implements Comparable<Person> {
    private String name;
    private int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

    @Override
    public int compareTo(Person other) {
//        if(this.age < other.age){
//            //说明当前对象比other对象小
//            //实际上只要是返回负数就可以了,不一定是-1
//            return -1;
//        }else if(this.age > other.age){
//            return 1;
//        }
//        //return 0 表示两个对象一样大
//        return 0;
        return this.age - other.age;
    }

}

2.实现Comparator定义比较器

public class TestObjectCompare {

    public static void main(String[] args) {
        Person p1 = new Person("douma", 10);
        Person p2 = new Person("laotong", 90);

        Comparator<Person> comparator1 = new Comparator<Person>() {
            @Override
            public int compare(Person o1, Person o2) {
                return o1.getAge() - o2.getAge();
            }
        };
        int res1 = comparator1.compare(p1, p2);
        System.out.println(res1);

        Comparator<Person> comparator2 = ((o1 , o2) -> o1.getAge() - o2.getAge());
        int res2 = comparator2.compare(p1, p2);
        System.out.println(res2);

    }
}

//class PersonComparator implements Comparator<Person>{
//
//
//    @Override
//    public int compare(Person o1, Person o2) {
//        return o1.getAge() - o2.getAge();
//    }
//}

class Person  {
    private String name;
    private int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

}

3.支持引用类型比较的三路快排代码:

//ThreeWayQuickSorter继承Sorter并且泛型元素E继承Comparable表示可比较类型
public class ThreeWayQuickSorter<E extends Comparable<E>> extends Sorter {
    public void sort(E[] data) {
        if (data == null || data.length <= 1) return;
        sort(data, 0, data.length - 1);
    }

    private void sort(E[] data, int lo, int hi) {
        if (lo >= hi) return;
        // 分区
        E pivot = data[hi];

        int less = lo;
        int great = hi;

        int i = lo;
        while (i <= great) {
            if (data[i].compareTo(pivot) < 0) {
                swap(data, i, less);
                less++;
                i++;
            } else if (data[i].compareTo(pivot) > 0) {
                swap(data, i, great);
                great--;
            } else {
                i++;
            }
        }

        sort(data, lo, less - 1);
        sort(data, great +1, hi);
    }

    public void sort(E[] data, Comparator<E> c) {
        if (data == null || data.length <= 1) return;
        sort(data, 0, data.length - 1, c);
    }

    private void sort(E[] data, int lo, int hi, Comparator<E> c) {
        if (lo >= hi) return;
        // 分区
        E pivot = data[hi];

        int less = lo;
        int great = hi;

        int i = lo;
        while (i <= great) {
            if (c.compare(data[i], pivot) < 0) {
                swap(data, i, less);
                less++;
                i++;
            } else if (c.compare(data[i], pivot) > 0) {
                swap(data, i, great);
                great--;
            } else {
                i++;
            }
        }

        sort(data, lo, less - 1, c);
        sort(data, great +1, hi, c);
    }

    public static void main(String[] args) {
        Person p1 = new Person("douma", 40);
        Person p2 = new Person("laotang", 30);
        Person p3 = new Person("douma1", 32);
        Person p4 = new Person("laotang2", 33);
        Person[] people = new Person[]{p1, p2, p3, p4};
        Comparator<Person> comparator = ((o1, o2) -> o2.getAge() - o1.getAge());
        new ThreeWayQuickSorter().sort(people, comparator);
        System.out.println(Arrays.toString(people));
    }
}

附加代码:Sorter类 (定义了swap方法)

public class Sorter {

    public <T> void swap(T[] nums, int i, int j) {
        T tmp = nums[i];
        nums[i] = nums[j];
        nums[j] = tmp;
    }

    public void swap(Integer[] nums, int i, int j) {
        int tmp = nums[i];
        nums[i] = nums[j];
        nums[j] = tmp;
    }

    public void swap(int[] nums, int i, int j) {
        int tmp = nums[i];
        nums[i] = nums[j];
        nums[j] = tmp;
    }

}

十一、Java内置的引用类型排序功能

public class JavaSorter {
    public static void main(String[] args) {
        int[] data = new int[]{34, 33, 12, 78, 21, 1, 98, 100};
        Arrays.sort(data); // 通用的排序
        System.out.println(Arrays.toString(data));

        Person p1 = new Person("douma", 40);
        Person p2 = new Person("laotang", 30);
        Person p3 = new Person("douma1", 32);
        Person p4 = new Person("laotang2", 33);
        Person[] people = new Person[]{p1, p2, p3, p4};
        //Arrays.sort(people);
        //定义比较器comparator
        Comparator<Person> comparator = ((o1, o2) -> o1.getAge() - o2.getAge());
        
        //Arrays.sort(people, comparator);//传入比较器comparator
        // 小规模数据的话选择插入排序
        // 大规模数据的话选择归并排序
        // (老版本使用的是递归实现的归并,而新版本使用的则不是递归实现的归并)
        //System.out.println(Arrays.toString(people));

        ArrayList<Person> list = new ArrayList<>();
        list.add(p1);
        list.add(p2);
        list.add(p3);
        list.add(p4);
     	//传入比较器comparator
        Collections.sort(list, comparator);
        // 底层:Arrays.sort
        System.out.println(list);
    }
}

引用数据类型要求排序具有稳定性
在这里插入图片描述

十二、分治思想在排序中的体现

在这里插入图片描述


总结

1.归并排序和快排是面试重点,我们要认真记下他的思路和代码实现。
2.自定义比较器也是后面做算法题经常要用的一项技巧。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值