目录
总览:

一、简单排序(适用于少量数据元素排序)。
Comparable接口:
Comparable接口可以排序的原理:执行对象中的compareTo()方法。
操作对象:实现了Comparable接口的对象,并且重写compareTo()方法。
如果我们把对象写入数组中,使用Arrays.sort排序。如果我们把对象写出集合中,使用Collections.sort排序,String类可以直接排序就是因为它实现了comparable,调用sort的时候,实际执行的还是compareTo方法。(如果不用工具类,可以不继承这个接口,可以自定义一个方法实现比较大小)
//student类的重写compareTo方法
@Override
public int compareTo(Student o) {
return this.age - o.age;
}
package _排序_Comparable接口;
public class TestComparable {
public static void main(String[] args) {
//创建两个Student对象并调用getMax()方法,完成测试
Student s1 = new Student();
s1.setName("张三");
s1.setAge(18);
Student s2 = new Student();
s1.setName("李四");
s1.setAge(20);
Comparable max = getMax(s1, s2);
System.out.println(max);
}
public static Comparable getMax(Comparable c1,Comparable c2){
int result = c1.compareTo(c2);
//如果result<0,则c1比c2小
//如果result>0,则c1比c2大
//如果result==0,则c1和c2一样大
if (result >= 0) {
return c1;
}else {
return c2;
}
}
}
1、冒泡排序。
(1)设计冒泡排序算法。

package 排序_冒泡排序;
public class Bubble {
//对数组a中的元素进行排序
public static void sort(Comparable[] a){
for (int i=a.length-1;i>0;i--){
for (int j=0;j<i;j++){
if (greater(a[j], a[j+1])){
exch(a,j,j+1);
}
}
}
}
//比较v元素是否大于w元素
public static boolean greater(Comparable v,Comparable w){
return v.compareTo(w)>0;
}
//数组元素i和j交换位置
public static void exch(Comparable[] a,int i,int j){
Comparable temp;
temp = a[j];
a[j] = a[i];
a[i] = temp;
}
}
测试该冒泡排序算法是否成功:测试成功。
package 排序_冒泡排序;
import java.util.Arrays;
public class BubbleTest {
public static void main(String[] args) {
// int[] a={4,5,6,3,2,1};//出现错误原因:基本数据类型没有继承Comparable接口,要用包装类型数据
Integer[] a={4,5,6,3,2,1};
Bubble.sort(a);
System.out.println(Arrays.toString(a));//转化成字符串类型输出
}
}
(2)冒泡排序—时间复杂度分析。
执行次数为什么要再加一次:因为执行的代码有两语句。

2、选择排序。
(1)设计选择排序算法。

package 排序_选择排序;
public class Selection {
//对数组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 (geater(a[minIndex],a[j])){
minIndex = j;
}
}
//交换最小元素所在索引minIndex处的值和i索引的值
exch(a,i,minIndex);
}
}
//比较v元素是否大于w元素
private static boolean geater(Comparable v,Comparable w){
return v.compareTo(w) > 0;
}
//数组元素i和j交换位置
private static void exch(Comparable[] a,int i,int j){
Comparable temp;
temp = a[i];
a[i] = a[j];
a[j] = temp;
}
}
测试该选择排序算法是否成功:测试成功。
package 排序_选择排序;
import java.util.Arrays;
public class SelectionTest {
public static void main(String[] args) {
Integer[] a = {4,6,8,7,9,2,10,1};
Selection.sort(a);
System.out.println(Arrays.toString(a));
}
}
(2)选择排序—时间复杂度分析。

3、插入排序。
(1)设计插入排序算法。

package 排序_插入排序;
public class Insertion {
//对数组a中的元素进行排序
public static void sort(Comparable[] a){
for (int i = 1; i < a.length; i++) {
for (int j = i; j > 0; j--) {
if (greater(a[j-1],a[j])){
exch(a,j-1,j);
}else {
break;
}
}
}
}
//比较v元素是否大于w元素
private static boolean greater(Comparable v,Comparable w){
return v.compareTo(w) > 0;
}
//数组元素i和j交换位置
private static void exch(Comparable[] a,int i,int j){
Comparable temp;
temp = a[i];
a[i] = a[j];
a[j] = temp;
}
}
测试该插入排序算法是否成功:测试成功。
package 排序_插入排序;
import java.util.Arrays;
public class InsertionTest {
public static void main(String[] args) {
Integer[] a = {4,3,2,10,12,1,5,6};
Insertion.sort(a);
System.out.println(Arrays.toString(a));
}
}
(2)插入排序—时间复杂度分析。

二、高级排序。
1、希尔排序。
(1)设计希尔排序算法。

package 排序_希尔排序;
public class Shell {
//对数组a中的元素进行排序
public static void sort(Comparable[] a){
//1.根据数组a的长度,确定增长量h的初始值;
int h = 1;
while (h < a.length / 2){
h = h*2 + 1;
}
//2.希尔排序
while (h >= 1) {
//排序
//2.1找到待插入的元素
for (int i = h; i < a.length; i++) {
//2.2把待插入的元素插入到有序数列中
for (int j = i; j >= h; j-=h) {
//待插入元素是a[j],比较a[j]和a[j-h]
if (greater(a[j-h],a[j])){
exch(a,j-h,j);
}else {
//待插入元素已经找到合适的位置后,结束循环。
break;
}
}
}
//减小h的值
h = h/2;
}
}
//比较v元素是否大于w元素
private static boolean greater(Comparable v,Comparable w){
//降序的话,就写 w.compareTo(v) > 0;
return v.compareTo(w) > 0;
}
//数组元素i和j交换位置
private static void exch(Comparable[] a,int i,int j){
Comparable temp;
temp = a[i];
a[i] = a[j];
a[j] = temp;
}
}
测试该希尔排序算法是否成功:测试成功。
package 排序_希尔排序;
import java.util.Arrays;
public class ShellTest {
public static void main(String[] args) {
Integer[] a = {9,1,2,5,7,4,8,6,3,5};
Shell.sort(a);
System.out.println(Arrays.toString(a));
}
}
(2)希尔排序—时间复杂度分析。

log(n):增量降到1时的次数。
n:每次的比较次数。
package 排序_希尔排序;
import java.io.*;
import java.util.ArrayList;
import java.util.Collections;
public class SortCompare {
//调用不同的测试方法,完成测试
public static void main(String[] args) throws IOException {
//1.创建一个ArrayList集合,保存读取出来的整数
ArrayList<Integer> list = new ArrayList<>();
//2.创建缓存读取流BufferedReader,读取数据,并存储到ArrayList中
BufferedReader br = new BufferedReader(new InputStreamReader(SortCompare.class.getClassLoader().getResourceAsStream("reverse_arr.txt")));
String line = null;
while ((line = br.readLine()) != null) {
//line是字符串,把line转换成Integer,存储到集合中
Integer i = Integer.valueOf(line);
list.add(i);
}
br.close();
//3.把ArrayList集合转化成数组
Integer[] a = new Integer[list.size()];
list.toArray(a);
//4.调用测试代码完成测试
// testShell(a);//希尔排序执行的时间为:45毫秒
testInsertion(a);//插入排序执行的时间为:38397毫秒
}
//测试希尔排序
public static void testShell(Integer[] a){
//1.获取执行之前的时间
long start = System.currentTimeMillis();
//2.执行算法代码
Shell.sort(a);
//3.获取执行之后时间
long end = System.currentTimeMillis();
//4.算出程序执行的时间并输出
System.out.println("希尔排序执行的时间为:"+(end - start)+"毫秒");
}
//测试插入排序
public static void testInsertion(Integer[] a){
//1.获取执行之前的时间
long start = System.currentTimeMillis();
//2.执行算法代码
排序_插入排序.Insertion.sort(a);
//3.获取执行之后时间
long end = System.currentTimeMillis();
//4.算出程序执行的时间并输出
System.out.println("插入排序执行的时间为:"+(end - start)+"毫秒");
}
}
2、归并排序。
(1)递归。
package 排序_递归;
public class TestFactorial {
public static void main(String[] args) {
//求N的阶乘
System.out.println(factorial(12));
}
public static long factorial(int n){
if (n == 1 || n == 0) {
return 1;
}else {
return n * factorial(n - 1);
}
}
}
(2)设计归并排序算法。

package 排序_归并排序;
public class Merge {
//归并排序需要的辅助数组
private static Comparable[] assist;
//比较v元素是否小于w元素
private static boolean less(Comparable v,Comparable w){
return v.compareTo(w) < 0;
}
//数组元素i和j交换位置
private static void exch(Comparable[] a,int i,int j){
Comparable temp;
temp = a[i];
a[i] = a[j];
a[j] = temp;
}
//对数组a中的元素进行排序
public static void sort(Comparable[] a){
//1.初始化辅助数组assist
assist = new Comparable[a.length];
//2.定义一个lo变脸阿甘,和hi变量,分别记录数组中最小的索引和最大的索引
int lo = 0;
int hi = a.length - 1;
//3.调用sort重载方法完成数组a中,从索引lo到索引到索引hi的元素的排序
sort(a,lo,hi);
}
//对数组a中从lo到hi的元素进行排序
private static void sort(Comparable[] a,int lo,int hi){
//做安全性校验
if (hi <= lo) {//注意这里,之前这里写错了
return;
}
//对lo到hi之间的元素进行分为两个组,(hi+lo)/2,对于index,减法是安全的,加法是不安全的(相加可能超出int范围)
int mid = lo + (hi - lo)/2;
//分别对每一组数据进行排序
sort(a,lo,mid);
sort(a,mid+1,hi);
//再把两个组中的数据进行归并
merge(a,lo,mid,hi);
}
//对数组中,从lo到mid为一组,从mid+1到hi为一组,对这两组数据进行归并
private static void merge(Comparable[] a,int lo,int mid,int hi){
//定义三个指针
int i = lo;
int p1 = lo;
int p2 = mid + 1;
//遍历,移动p1指针和p2指针,比较对应索引处的值,找出小的那个,放到辅助数组的对应的索引处
while (p1 <= mid && p2 <= hi) {//注意这里是用&&(如果用||会出错)
//比较对应索引处的值
if (less(a[p1],a[p2])){
assist[i++] = a[p1++];//赋值给i索引后,再加1
}else {
assist[i++] = a[p2++];
}
}
//遍历,如果p1的指针没有走完,那么顺序移动p1指针,把对应的元素放到辅助数组的对应索引处
while (p1 <= mid) {
assist[i++] = a[p1++];
}
//遍历,如果p2的指针没有走完,那么顺序移动p2指针,把对应的元素放到辅助数组的对应索引处
while (p2 <= hi) {
assist[i++] = a[p2++];
}
//把辅助数组中的元素拷贝到原数组中
for (int index = lo; index <= hi; index++) {
a[index] = assist[index];
}
}
}
测试该归并排序算法是否成功:测试成功。
package 排序_归并排序;
import java.util.Arrays;
public class TestMerge {
public static void main(String[] args) {
Integer[] a = {8, 4, 5, 7, 1, 3, 6, 2,3};
Merge.sort(a);
System.out.println(Arrays.toString(a));
}
}
(3)归并排序—时间复杂度分析。(下面的k是层数或第k层)

package 排序_归并排序;
import 排序_希尔排序.Shell;
import java.io.*;
import java.util.ArrayList;
public class SortCompare {
//调用不同的测试方法,完成测试
public static void main(String[] args) throws IOException {
//1.创建一个ArrayList集合,保存读取出来的整数
ArrayList<Integer> list = new ArrayList<>();
//2.创建缓存读取流BufferedReader,读取数据,并存储到ArrayList中
BufferedReader br = new BufferedReader(new InputStreamReader(排序_希尔排序.SortCompare.class.getClassLoader().getResourceAsStream("reverse_arr.txt")));
String line = null;
while ((line = br.readLine()) != null) {
//line是字符串,把line转换成Integer,存储到集合中
Integer i = Integer.valueOf(line);
list.add(i);
}
br.close();
//3.把ArrayList集合转化成数组
Integer[] a = new Integer[list.size()];
list.toArray(a);
//4.调用测试代码完成测试
// testShell(a);//希尔排序执行的时间为:45毫秒
// testMerge(a);//归并排序执行的时间为:113毫秒
}
//测试希尔排序
public static void testShell(Integer[] a){
//1.获取执行之前的时间
long start = System.currentTimeMillis();
//2.执行算法代码
Shell.sort(a);
//3.获取执行之后时间
long end = System.currentTimeMillis();
//4.算出程序执行的时间并输出
System.out.println("希尔排序执行的时间为:"+(end - start)+"毫秒");
}
//测试归并排序
public static void testMerge(Integer[] a){
//1.获取执行之前的时间
long start = System.currentTimeMillis();
//2.执行算法代码
排序_归并排序.Merge.sort(a);
//3.获取执行之后时间
long end = System.currentTimeMillis();
//4.算出程序执行的时间并输出
System.out.println("归并排序执行的时间为:"+(end - start)+"毫秒");
}
}
3、快速排序。
(1)设计快速排序算法。

package 排序_快速排序;
public class Quick {
//比较v元素是否小于w元素
private static boolean less (Comparable v,Comparable w){
return v.compareTo(w) < 0;
}
//数组元素i和j交换位置
private static void exch (Comparable[] a,int i,int j){
Comparable temp = a[i];
a[i] = a[j];
a[j] = temp;
}
//对数组内的元素进行排序
public static void sort(Comparable[] a){
int lo = 0;
int hi = a.length - 1;
sort(a,lo,hi);
}
//对数组a中从索引lo到索引hi之间的元素进行排序
private static void sort(Comparable[] a,int lo,int hi){
//安全性校验(以及递归出口,递归退出条件)
if (hi <= lo) {
return;
}
//需要对数组中lo索引到hi索引处的元素进行分组(左子组和右子组)
int partition = partition(a, lo, hi);//返回的是分组的分界值所在的索引,分界值位置变换后的索引
//让左子组有序
sort(a,lo,partition-1);
//让右子组有序
sort(a,partition+1,hi);
}
//对数组a中,从索引lo到索引hi之间的元素进行分组,并返回分组界限对应的索引
public static int partition(Comparable[] a,int lo,int hi){
//确定分界值
Comparable key = a[lo];
//定义两个指针,分别指向待切分元素的最小索引和最大索引处的下一个位置
int left = lo;
int right = hi + 1;
//切分
while (true) {
//先从右往左扫描,移动right指针,找到一个比分界值小的元素,停止。
while (less(key,a[--right])) {
//这里没必要判断,当它等于分界值的时候,循环也就结束了,压根不会进来
//if (right == lo){ break; }
}
//再从左往右扫描,移动left指针,找到一个比分界值大的元素,停止
while (less(a[++left],key)) {
//if (left == hi){ break; }
//修改过后的,可以减少不必要的循环
if (left >= right){
break;
}
}
//判断left>=right,如果是,则证明元素扫描完毕,结束循环,如果不是,则交换元素即可
if (left >= right) {
break;
}else {
exch(a,left,right);
}
}
exch(a,lo,right);//注意,这里必须是right,因为left可能比right大(因为相等时left如果再++)
//exch(a,lo,left);//错误,原因使用left和分界量交换位置
return right;
}
}
测试该快速排序算法是否成功:测试成功。(如果是3,1,2,3第一次分组的时候数组也是3,1,2,3,是不影响的)
package 排序_快速排序;
import java.util.Arrays;
public class QuickTest {
public static void main(String[] args) {
// Integer[] a = {6,1,2,7,9,3,4,5,8};
Integer[] a = {3,1,2,3,4,3,2,3,3,2,3,2,2,1,1,1,2,3,4,5,5,5,4,4,3,3,3};
Quick.sort(a);
System.out.println(Arrays.toString(a));//[1, 2, 3, 4, 5, 6, 7, 8, 9]
//[1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5]
}
}
(2)快速排序—时间复杂度分析。
注意:这里的时间复杂度分析是:O(nlogn)
1:每层(认为每层的个数都是n个)的遍历复杂度为O(n)
2:这里说的切分的次数是指层数(切了多少层,并不是指切分的次数):O(logn)

4、排序算法的稳定性。
(1)稳定性的定义。

(2)常见排序算法的稳定性。






