经典排序算法,重写排序方法

//从小到大
    public static void main(String[] args) {
        int [] a = {8,6,5,9,11,2,4};
        kuaipai(a);
        for(int i = 0 ;i<a.length;i++){
            System.out.println(a[i]);
        }
    }
//交换两个数
    public static void swap(int[] a , int i , int j){
        int t = a[i];
        a[i] = a[j];
        a[j] = t;
    }

冒泡排序

每次选一个最大的泡泡,放到后面

对冒泡排序的一个改进就是:加入一个标志变量exchange,如果某一趟没有交换数据,那么说明已经排好序

    public static void   maopao(int[] a){
        int len = a.length;
        for(int i = 0;i<len-1;i++){
            for(int j = 0;j<len-i-1;j++){
                if(a[j]>a[j+1]) swap(a,j,j+1);
            }
        }
    }

选择排序

从后面把最小的选出来,放到前面

    public static void xuanze(int[] a){
        int len = a.length;
        for(int i = 0;i<len-1;i++){
            for(int j = i+1;j<len;j++){
                if(a[j]<a[i]) swap(a,i,j);
            }
        }
    }

直接插入排序

把当前数插入到前面有序数组中

    public static void zhijiecharu(int[] a){
        int len = a.length;
        for(int i = 1;i<len;i++){
            for(int j = i;j>0 && a[j]<a[j-1];j--){
                swap(a,j,j-1);
            }
        }
    }

归并排序

    public static void guibing(int[] a){
        int len = a.length;
        mergesort(a,0,len-1);
    }
    public static void mergesort(int[] a , int start ,int end){
        if(start<end){
            int mid = (start+end)/2;
            mergesort(a,start,mid);
            mergesort(a,mid+1,end);
            mergePaihaoxu(a,start,mid,mid+1,end);
        }
    }
    public static void mergePaihaoxu(int[] a, int start1 , int end1 , int start2 ,int end2){
        int i = start1;
        int j = start2;
        int [] t = new int[end2-start1+1];
        int k = 0;
        while (i<=end1 && j<=end2){
            if(a[i]<a[j]){
                t[k++] = a[i++];
            }
            else t[k++] = a[j++];
        }
        if(i<=end1) t[k++] = a[i++];
        if(j<=end2) t[k++] = a[j++];
        for(int m = 0;m<t.length;m++){
            a[m+start1]=t[m];
        }
    }

快排

不稳定:由于关键字的比较和交换是跳跃进行的,因此,快速排序是一种不稳定的排序方法。

时间复杂度,最好O(nlogn),平均O(nlogn),

最坏O(n*n),当数据几乎有序时是最差的,这时退为冒泡,空间复杂度O(nlogn)

就空间复杂度来说,主要是递归造成的栈空间的使用,最好情况,递归树的深度为log2n,其空间复杂度也就为O(logn),最坏情况,需要进行n‐1递归调用,其空间复杂度为O(n),平均情况,空间复杂度也为O(logn)。

有个易错点

    public static void kuaipai(int [] a){
        int len = a.length;
        qsort(a, 0 , len -1);
    }
    public static void qsort(int[] a,int i , int j){
        int pos;
        if(i<j){
            pos = partition(a,i,j);
            qsort(a,i,pos-1);
            qsort(a,pos+1,j);
        }
    }
    public static int partition(int[] a , int low , int high){
        //选择第一个元素作为基准
        int key = a[low];
        int i = low;
        int j = high;
        while (i<j){
            while (a[i]<=key && i<j) i++;
            //swap(a,i,j);
            while (a[j]>=key && i<j) j--;
            swap(a,i,j);
        }
        if(a[i]>a[low]) i--;//易错
        swap(a,low,i);
        return i;
    }

不受初始元素影响的排序:选择、堆、归并

 

Arrays.sort对于基本数据类型用快速排序,对象数组用改进的归并排序

Collection.sort(List<T> list)按自然顺序排序,元素必须有可比较性,实现comaprable接口

Collection.sort(List<T> list , comparator<q super T>)

摘自--去看博客:https://blog.csdn.net/qq_23179075/article/details/78753136

MapSetList 等集合中。他们都提共了一个排序方法 sort(),要对数据排序直接使用这个方法就行,但是要保证集合中的对象是 可比较的

怎么让一个对象是 可比较的,那就需要该对象实现 Comparable<T> 接口啦。然后重写里面的 compareTo()方法。我们可以看到Java中很多类都是实现类这个接口的 如:IntegerLong 等等。。。

假设我们有一个学生类,默认需要按学生的年龄字段 age 进行排序 代码如下:

public class Student implements Comparable<Student> {
    private int id;
    private int age;
    private String name;

    public Student(int id, int age, String name) {
        this.id = id;
        this.age = age;
        this.name = name;
    }
    @Override
    public int compareTo(Student o) {
        //降序
        //return o.age - this.age;
        //升序
        return this.age - o.age;        
    }
    @Override
    public String toString() {
        return "Student{" +
                "id=" + id +
                ", age=" + age +
                ", name='" + name + '\'' +
                '}';
    }
}

这里说一下重写的 public int compareTo(Student o){} 这个方法,它返回三种 int 类型的值: 负整数正整数

返回值含义
负整数当前对象的值 < 比较对象的值 , 位置排在前
当前对象的值 = 比较对象的值 , 位置不变
正整数当前对象的值 > 比较对象的值 , 位置排在后

测试代码:

public static void main(String args[]){
        List<Student> list = new ArrayList<>();
        list.add(new Student(1,25,"关羽"));
        list.add(new Student(2,21,"张飞"));
        list.add(new Student(3,18,"刘备"));
        list.add(new Student(4,32,"袁绍"));
        list.add(new Student(5,36,"赵云"));
        list.add(new Student(6,16,"曹操"));
        System.out.println("排序前:");
        for (Student student : list) {
            System.out.println(student.toString());
        }
        //使用默认排序
        Collections.sort(list);
        System.out.println("默认排序后:");
        for (Student student : list) {
            System.out.println(student.toString());
        }
}

输出日志:

排序前:
Student{id=1, age=25, name='关羽'}
Student{id=2, age=21, name='张飞'}
Student{id=3, age=18, name='刘备'}
Student{id=4, age=32, name='袁绍'}
Student{id=5, age=36, name='赵云'}
Student{id=6, age=16, name='曹操'}
默认排序后:
Student{id=6, age=16, name='曹操'}
Student{id=3, age=18, name='刘备'}
Student{id=2, age=21, name='张飞'}
Student{id=1, age=25, name='关羽'}
Student{id=4, age=32, name='袁绍'}
Student{id=5, age=36, name='赵云'}

比较器的使用

这个时候需求又来了,默认是用 age 排序,但是有的时候需要用 id 来排序怎么办? 这个时候比较器 :Comparator 就排上用场了。

Comparator 的使用有两种方式:

    Collections.sort(list,Comparator<T>);
    list.sort(Comparator<T>);

其实主要是看 Comparator 接口的实现,重写里面的 compare 方法。代码如下:

//自定义排序1
Collections.sort(list, new Comparator<Student>() {
    @Override
    public int compare(Student o1, Student o2) {
        return o1.getId() - o2.getId();
    }
});

compare(Student o1, Student o2) 方法的返回值跟 Comparable<> 接口中的 compareTo(Student o) 方法 返回值意思相同。另一种写法如下:

//自定义排序2
list.sort(new Comparator<Student>() {
    @Override
    public int compare(Student o1, Student o2) {
        return o1.getId() - o2.getId();
    }
});

输出日志:

排序前:
Student{id=1, age=25, name='关羽'}
Student{id=2, age=21, name='张飞'}
Student{id=3, age=18, name='刘备'}
Student{id=4, age=32, name='袁绍'}
Student{id=5, age=36, name='赵云'}
Student{id=6, age=16, name='曹操'}
自定义排序后:
Student{id=1, age=25, name='关羽'}
Student{id=2, age=21, name='张飞'}
Student{id=3, age=18, name='刘备'}
Student{id=4, age=32, name='袁绍'}
Student{id=5, age=36, name='赵云'}
Student{id=6, age=16, name='曹操'}

快速排序、双路快排、三路快排

参考:https://blog.csdn.net/k_koris/article/details/80585979

快排的子过程么,考虑到了e>v,e<v,而e=v的情况没有考虑对吧。看了代码理解了的同学应该清楚,其实我是把等于v这种情况包含进了大于v的情况里面了,那么会出现什么问题?不管是当条件是大于等于还是小于等于v,当数组中重复元素非常多的时候,等于v的元素太多,那么就将数组分成了极度不平衡的两个部分,因为等于v的部分总是集中在数组的某一边。

那么一种优化的方式便是进行双路快排

双路快排是为了让等于v的元素相对平均的分散在两个部分

当然除了快排和双路快排,还有一个更加经典的优化,我们叫它三路快排。

 

三路快排

双路快排将整个数组分成了小于v,大于v的两部分,而三路快排则是将数组分成了小于v,等于v,大于v的三个部分,当递归处理的时候,遇到等于v的元素直接不用管,只需要处理小于v,大于v的元素就好了。某一时刻的中间过程如下图:

当元素e等于v的时候直接纳入绿色区域之内,然后i++处理下一个元素。如图:

当元素e小于v的时候,只需要将元素e与等于e的第一个元素交换就行了,这和刚开始讲的快速排序方法类似。同理,当大于v的时候执行相似的操作。如图:

当全部元素处理完之后,数组便成了这个样子:

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值