//从小到大
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
Map
,Set
,List
等集合中。他们都提共了一个排序方法 sort()
,要对数据排序直接使用这个方法就行,但是要保证集合中的对象是 可比较的。
怎么让一个对象是 可比较的,那就需要该对象实现 Comparable<T>
接口啦。然后重写里面的 compareTo()
方法。我们可以看到Java中很多类都是实现类这个接口的 如:Integer
,Long
等等。。。
假设我们有一个学生类,默认需要按学生的年龄字段 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的时候执行相似的操作。如图:
当全部元素处理完之后,数组便成了这个样子: