一、Comparable接口
所有可以排序对象所对应的类,都实现了 Comparable 接口(Comparable 中的泛型为要比较的对象类型,即该类的类型),并重写了compareTo()方法。
public int compareTo(Object obj)
- 返回值 = 0:this == o
- 返回值 >0:this > obj
- 返回值 < 0:this < obj
(1)定义 Student 类
public class Student implements Comparable<Student>{
private String name;
private int age;
// 省略 构造器、set、get方法等
@Override
public int compareTo(Student stu) {
// 根据年龄比较
return this.age - stu.age;
}
}
(2)测试
public class Test {
public static Comparable getMax(Comparable c1, Comparable c2){
int result = c1.compareTo(c2);
if(result >= 0){
return c1; // c1大
}else {
return c2;
}
}
public static void main(String[] args) {
Student stu1 = new Student("张三",20);
Student stu2 = new Student("李四",30);
Comparable max = getMax(stu1, stu2);
System.out.println(max); // Student{name='李四', age=30}
}
}
二、冒泡排序 Bubble
其思想是:相邻的元素两两比较,较大的数下沉,较小的数冒起来。整个过程如同气泡冒起,因此被称作冒泡排序。
- 比较相邻的两个元素,如果前一个元素大,就交换位置。
- 依次比较每一对,每遍历一次,就会选出一个相对最大的元素(之前已经选出的元素除外)。
遍历的总对数是 length - 1
。
平均时间复杂度为O(n^2)
,适用于元素个数较少的排序。
public class Bubble {
/**
* 对数组 a 排序
*
* @param a 数组
*/
public static void sort(Comparable[] a) {
// 因为最后一个元素没有下一个,所以第一次遍历的总对数为 length-1,以后每
// 每次遍历的对数,都要减去已经选出的相对最大值个数
for (int i = a.length - 1; i > 0; i--) {
//
for (int j = 0; j < i; j++) {
if (isGreater(a[j], a[j + 1])) {
swap(a, j, j + 1); // a[j] 大,交换
}
}
}
}
/**
* 判断 v 元素 是否大于 w 元素
*
* @param v 元素v
* @param w 元素w
* @return 较大元素
*/
private static boolean isGreater(Comparable v, Comparable w) {
return v.compareTo(w) > 0;
}
/**
* 数组元素 i 和 j 交换位置
*
* @param a 数组
* @param i 索引i
* @param j 索引j
*/
private static void swap(Comparable[] a, int i, int j) {
Comparable temp;
temp = a[i];
a[i] = a[j];
a[j] = temp;
}
public static void main(String[] args) {
Integer[] ints = {4, 5, 6, 3, 2, 1};
Bubble.sort(ints);
System.out.println(Arrays.toString(ints));
}
}
public class BubbleSort {
public static void sort(int[] arr){
int temp = 0; // 在交换的时候使用
// 第一轮比较的次数 i 是 length-1, 以后每次都少 1 次
for (int i = arr.length - 1; i > 0; i--) {
// 比较次数为 i
for (int j = 0; j < i; j++) {
if(arr[j] > arr[j+1]){
temp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = temp;
}
}
}
}
public static void main(String[] args) {
int[] arr = {5,6,8,2,1,5,6,8};
BubbleSort.sort(arr);
System.out.println(Arrays.toString(arr));
}
}
三、选择排序 Selection
其思想是:选出相对最小的元素,放在相对最前面(交换)。
- 每一次遍历,都假定第一个就是最小元素。在依次与其他元素比较,最终找到最小元素所在的索引。
- 最小元素与最前面元素交换。
平均时间复杂度为O(n^2)
,适用于元素个数较少的排序。
public class Selection {
/**
* 对数组 a 排序
*
* @param a
*/
public static void sort(Comparable[] a) {
for (int i = 0; i < a.length - 1; i++) {
// 最小元素所在的索引
int minIndex = i;
// 不用跟自己比较,直接开始跟下一个比
for (int j = i + 1; j < a.length; j++) {
if (isGreater(a[minIndex], a[j])) {
minIndex = j; // a[minIndex] 大,更换索引
}
}
// 交换最小元素所在索引处的值
swap(a,i,minIndex);
}
}
/**
* 判断 v 元素 是否大于 w 元素
*
* @param v
* @param w
* @return
*/
private static boolean isGreater(Comparable v, Comparable w) {
return v.compareTo(w) > 0;
}
/**
* 数组元素 i 和 j 交换位置
*
* @param a
* @param i
* @param j
*/
private static void swap(Comparable[] a, int i, int j) {
Comparable temp;
temp = a[i];
a[i] = a[j];
a[j] = temp;
}
public static void main(String[] args) {
Integer[] ints = {4, 6, 8, 7, 9, 2 ,10, 1};
Selection.sort(ints);
System.out.println(Arrays.toString(ints));
}
}
四、插入排序 Insertion
其思想是:将初始数据分为有序部分和无序部分,依次将无序部分的元素插入有序部分中。
- 把原始数据分为两组:有序、无序。
- 在无序部分中选出一个元素作为待插入元素。
- 倒叙遍历有序部分元素,依次和待插入元素比较,直到找到一个元素小于待插入元素,将其放入这个元素的后面(交换),后面元素后移。(利用冒泡思想实现比较并插入)
平均时间复杂度为O(n^2)
,适用于元素个数较少的排序。
public class Insertion {
/**
* 对数组 a 排序
*
* @param a
*/
public static void sort(Comparable[] a) {
// 默认索引为0的元素为有序部分,无序部分从索引1出开始
for (int i = 1; i < a.length; i++) {
for (int j = i; j > 0; j--) {
if (isGreater(a[j-1], a[j])) {
swap(a,j,j-1); // a[j-1] 大,交换
}else {
break; // a[j-1] 小,说明已经插入好
}
}
}
}
/**
* 判断 v 元素 是否大于 w 元素
*
* @param v
* @param w
* @return
*/
private static boolean isGreater(Comparable v, Comparable w) {
return v.compareTo(w) > 0;
}
/**
* 数组元素 i 和 j 交换位置
*
* @param a
* @param i
* @param j
*/
private static void swap(Comparable[] a, int i, int j) {
Comparable temp;
temp = a[i];
a[i] = a[j];
a[j] = temp;
}
public static void main(String[] args) {
Integer[] ints = {4, 3, 2,10,12,1,5,6};
Insertion.sort(ints);
System.out.println(Arrays.toString(ints));
}
}
五、希尔排序 Shell
希尔排序又称为缩小增量排序,是对插入排序的一种优化版本。优化的地方在于:不用每次插入一个元素时,就和有序部分中的所有元素进行比较。
- 取一个整数 h 作为间隔,所有距离为 h 的元素为一组。
- 每组元素分别排序。
- 缩小间隔 h,直至 h = 1。
间隔 h 的取值:
// 初始值
int h = 1;
while(h < 数组长度 / 2){
h = 2h + 1;
}
// 减小规则
h = h /2;
public class Shell {
/**
* 对数组 a 排序
*
* @param a
*/
public static void sort(Comparable[] a) {
// 确定间隔初始值
int h = 1;
while (h < a.length) {
h = 2 * h + 1;
}
while (h >= 1) {
// 从第一组中的第二个元素,开始往后依次比较
for (int i = h; i < a.length; i++) {
for (int j = i; j >= h; j -= h) {
if (isGreater(a[j - h], a[j])) {
swap(a, j, j - h); // a[j-h] 大于 插入元素 a[j]
}else {
break;
}
}
}
h = h / 2;
}
}
/**
* 判断 v 元素 是否大于 w 元素
*
* @param v
* @param w
* @return
*/
private static boolean isGreater(Comparable v, Comparable w) {
return v.compareTo(w) > 0;
}
/**
* 数组元素 i 和 j 交换位置
*
* @param a
* @param i
* @param j
*/
private static void swap(Comparable[] a, int i, int j) {
Comparable temp;
temp = a[i];
a[i] = a[j];
a[j] = temp;
}
public static void main(String[] args) {
Integer[] ints = {9,1,2,5,7,4,8,6,3,5};
Shell.sort(ints);
System.out.println(Arrays.toString(ints));
}
}
六、归并排序 Merge
其思想是:将元素进行逐层折半分组,然后从最小分组开始比较排序,合并成一个大的分组,逐层进行,最终所有的元素都是有序的。
- 尽可能将数据分为两个元素个数相等的子组,并对每一个子组继续拆分,直至每组元素个数为1。
- 将相邻的两个子组合并,合并过程中进行排序。
平均时间复杂度为O(nlogn)
< O(n^2)
。
public class Merge {
// 归并所需要的辅助数组
private static Comparable[] assist;
/**
* 对数组 a 排序
*
* @param a
*/
public static void sort(Comparable[] a) {
// 初始化辅助数组
assist = new Comparable[a.length];
// 开始索引和结束索引
int start = 0;
int end = a.length - 1;
// 排序
sort(a, start, end);
}
/**
* 对数组 a 中部分元素排序
*
* @param a
* @param start
* @param end
*/
private static void sort(Comparable[] a, int start, int end) {
// 安全性校验
if (end <= start) {
return;
}
// 分组
int end1 = start + (end - start) / 2;
// 对子组排序
sort(a, start, end1);
sort(a, end1 + 1, end); // start2 = end1 + 1
// 归并
merge(a, start, end1, end);
}
/**
* 把两个子组合并成为一个大组,且排序
* 两个子组:a[start1.end1], a[start2,end2]
*
* @param a
* @param start1
* @param end1
* @param end2
*/
private static void merge(Comparable[] a, int start1, int end1, int end2) {
// 定义三个指针
int i = start1;
int p1 = start1;
int p2 = end1 + 1;
// 遍历,移动 p1 p2指针,找出较小的放到 辅助数组对应索引处
while (p1 <= end1 && p2 <= end2) {
if (isLess(a[p1], a[p2])) {
assist[i++] = a[p1++]; // a[p1]小
} else {
assist[i++] = a[p2++]; // a[p2]小
}
}
// 如果 p1 指针没遍历完
while (p1 <= end1) {
assist[i++] = a[p1++];
}
// 如果 p2 指针没遍历完
while (p2 <= end2) {
assist[i++] = a[p2++];
}
// 辅助数组 复制 到原数组
for (int index = start1; index <= end2; index++) {
a[index] = assist[index];
}
}
/**
* 判断 v 元素 是否小于 w 元素
*
* @param v
* @param w
* @return
*/
private static boolean isLess(Comparable v, Comparable w) {
return v.compareTo(w) < 0;
}
public static void main(String[] args) {
Integer[] ints = {8, 4, 5, 7, 1, 3, 6, 2};
Merge.sort(ints);
System.out.println(Arrays.toString(ints));
}
}
七、快速排序 Quick
快速排序是对冒泡排序的一种改进。
- 设定一个分界值(数组第一个元素),将数组分为两部分。
- 将小于分界值的元素放到左边,大于或等于分界值的元素放到右边。左边部分总比右边部分小。
- 分别继续细分,直到整个数组变成有序序列。
平均时间复杂度为O(nlogn)
< O(n^2)
。
public class Quick {
/**
* 对数组 a 排序
*
* @param a
*/
public static void sort(Comparable[] a) {
// 开始索引和借宿索引
int start = 0;
int end = a.length - 1;
// 排序
sort(a, start, end);
}
/**
* 对数组 a 中部分元素排序
*
* @param a
* @param start
* @param end
*/
private static void sort(Comparable[] a, int start, int end) {
// 安全性校验
if (end <= start) {
return;
}
// 分组,得到分界值所在的索引
int partition = partition(a, start, end);
// 对子组排序,不需要对分界值排序
sort(a, start, partition - 1);
sort(a, partition + 1, end);
}
/**
* 将 a[start,edn] 进行分组
*
* @param a
* @param start
* @param end
* @return 分组分界值所在的索引
*/
private static int partition(Comparable[] a, int start, int end) {
// 确定分解值
Comparable key = a[start];
// 定义两个指针
int left = start;
int right = end + 1;
// 切分
while (true) {
// 从右往左,移动right,找到一个比分界值小的元素
while (isLess(key, a[--right])) {
if (right == start) {
break;
}
}
// 从左往右,移动left,找到一个比分界值大的元素
while (isLess(a[++left], key)) {
if (left == end) {
break;
}
}
if (left >= right) {
break; // 扫描完毕
} else {
swap(a, left, right); // 交换,继续扫描
}
}
// 交换分界值
swap(a, start, right);
return right;
}
/**
* 判断 v 元素 是否小于 w 元素
*
* @param v
* @param w
* @return
*/
private static boolean isLess(Comparable v, Comparable w) {
return v.compareTo(w) < 0;
}
/**
* 数组元素 i 和 j 交换位置
*
* @param a
* @param i
* @param j
*/
private static void swap(Comparable[] a, int i, int j) {
Comparable temp;
temp = a[i];
a[i] = a[j];
a[j] = temp;
}
public static void main(String[] args) {
Integer[] ints = {6, 1, 2, 7, 9, 3, 4, 5, 8};
Quick.sort(ints);
System.out.println(Arrays.toString(ints));
}
}