HMJAVA数据结构与算法3【排序】
1 简单排序
1.1 Comparable接口

package com.haifei.demo01sort; public class Student implements Comparable<Student>{ private String username; private int age; public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override public String toString() { return "Student{" + "username='" + username + '\'' + ", age=" + age + '}'; } //定义排序规则 @Override public int compareTo(Student o) { return this.getAge()-o.getAge(); } }

package com.haifei.demo01sort.test; import com.haifei.demo01sort.Student; public class Test01Comparable { public static void main(String[] args) { Student s1 = new Student(); s1.setUsername("zhangsan"); s1.setAge(20); Student s2 = new Student(); s2.setUsername("lisi"); s2.setAge(18); Comparable max = getMax(s1, s2); System.out.println(max); // Student{username='zhangsan', age=20} } 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.2 冒泡排序

package com.haifei.demo01sort; 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])){ //比较索引j和索引j+1处的值 exch(a, j, j+1); } } } } /* 比较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 = a[i]; a[i] = a[j]; a[j] = temp; } }

package com.haifei.demo01sort.test; import com.haifei.demo01sort.Bubble; import java.util.Arrays; public class Test02Bubble { public static void main(String[] args) { // int[] arr = {4,5,6,3,2,1}; //自定义的sort方法需要传入的参数是Comparable[] Integer[] arr = {4,5,6,3,2,1}; //使用int的包装类,因为public final class Integer extends Number implements Comparable<Integer> Bubble.sort(arr); System.out.println(Arrays.toString(arr)); //[1, 2, 3, 4, 5, 6] } }
1.3 选择排序

package com.haifei.demo01sort; public class Selection { //对数组a中的元素进行排序 public static void sort(Comparable[] a){ for (int i=0; i<a.length-2; i++){ int minIndex = i; //记录最小元素所在的索引,默认为参与选择排序的第一个元素所在的位置 for (int j=i+1; j<a.length; j++){ if (greater(a[minIndex], a[j])){ //比较最小索引minIndex处的值和j索引处的值 minIndex = j; } } exch(a, i, minIndex); //交换最小元素所在索引minIndex处的值和索引i处的值 } } //比较元素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 = a[i]; a[i] = a[j]; a[j] = temp; } }

package com.haifei.demo01sort.test; import com.haifei.demo01sort.Selection; import java.util.Arrays; public class Test03Selection { 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)); //[1, 2, 4, 6, 7, 8, 10, 9] } }
1.4 插入排序

package com.haifei.demo01sort; public class Insertion { //对数组a中的元素进行排序 public static void sort(Comparable[] a){ for (int i=1; i<a.length; i++){ //当前元素为a[i],依次和i前面的元素比较,找到一个小于等于a[i]的元素 for (int j=i; j>0; j--){ if (greater(a[j-1], a[j])){ //比较索引j处的值和j-1处的值 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 = a[i]; a[i] = a[j]; a[j] = temp; } }

package com.haifei.demo01sort.test; import com.haifei.demo01sort.Insertion; import java.util.Arrays; public class Test04Insertion { 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)); //[1, 2, 3, 4, 5, 6, 10, 12] } }
2 高级排序
2.1 希尔排序

package com.haifei.demo01sort; public class Shell { //对数组a中的元素进行排序 public static void sort(Comparable[] a){ //1.根据数组a的长度,确定增长量h的初始值; int h = 1; while(h<a.length/2){ h=2*h+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){ 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; } }

package com.haifei.demo01sort.test; import com.haifei.demo01sort.Shell; import java.util.Arrays; public class Test05Shell { 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)); //[1, 2, 3, 4, 5, 5, 6, 7, 8, 9] } }

package com.haifei.demo01sort.test; import com.haifei.demo01sort.Insertion; import com.haifei.demo01sort.Shell; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.util.ArrayList; public class Test06SortCompare { public static void main(String[] args) throws IOException { //1.创建一个ArrayList集合,保存读取出来的整数 ArrayList<Integer> list = new ArrayList<>(); //2.创建缓存读取流BufferedReader,读取数据,并存储到ArrayList中; BufferedReader reader = new BufferedReader(new InputStreamReader(Test06SortCompare.class.getClassLoader().getResourceAsStream("reverse_arr.txt"))); String line=null; while((line=reader.readLine())!=null){ //line是字符串,把line转换成Integer,存储到集合中 int i = Integer.parseInt(line); list.add(i); } reader.close(); /* 项目中新加入外部文件时,可能找不到文件而报空指针,需要在项目名上右键rebuild module一下 */ //3.把ArrayList集合转换成数组 Integer[] a = new Integer[list.size()]; list.toArray(a); //4.调用测试代码完成测试 // testInsertion(a);//21906毫秒 // testShell(a);//20毫秒 } //测试希尔排序 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.2 归并排序
1、递归

package com.haifei.demo01sort.test; /** * 递归 */ public class Test07Factorial { public static void main(String[] args) { System.out.println(factorial(5)); //120 // System.out.println(factorial(100000)); //java.lang.StackOverflowError } //求n的阶乘 public static long factorial(int n){ if (n == 1){ return 1; } return n * factorial(n-1); } }
2、归并排序

package com.haifei.demo01sort; 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 t = a[i]; a[i] = a[j]; a[j] = t; } /* 对数组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之间的数据进行分为两个组 int mid = lo+(hi-lo)/2;// 5,9 mid=7 //分别对每一组数据进行排序 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++]; }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 com.haifei.demo01sort.test; import com.haifei.demo01sort.Merge; import java.util.Arrays; public class Test08Merge { public static void main(String[] args) { Integer[] a = {8,4,5,7,1,3,6,2}; Merge.sort(a); System.out.println(Arrays.toString(a)); //[1, 2, 3, 4, 5, 6, 7, 8] } }

package com.haifei.demo01sort.test; import com.haifei.demo01sort.Insertion; import com.haifei.demo01sort.Merge; import com.haifei.demo01sort.Shell; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.util.ArrayList; public class Test06SortCompare { public static void main(String[] args) throws IOException { //1.创建一个ArrayList集合,保存读取出来的整数 ArrayList<Integer> list = new ArrayList<>(); //2.创建缓存读取流BufferedReader,读取数据,并存储到ArrayList中; BufferedReader reader = new BufferedReader(new InputStreamReader(Test06SortCompare.class.getClassLoader().getResourceAsStream("reverse_arr.txt"))); String line=null; while((line=reader.readLine())!=null){ //line是字符串,把line转换成Integer,存储到集合中 int i = Integer.parseInt(line); list.add(i); } reader.close(); /* 项目中新加入外部文件时,可能找不到文件而报空指针,需要在项目名上右键rebuild module一下 */ //3.把ArrayList集合转换成数组 Integer[] a = new Integer[list.size()]; list.toArray(a); //4.调用测试代码完成测试 // testInsertion(a);//21906毫秒 // testShell(a);//20毫秒 testMerge(a);//56毫秒 } //测试希尔排序 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)+"毫秒"); } //测试归并排序 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)+"毫秒"); } }
2.3 快速排序

package com.haifei.demo01sort; 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 t = a[i]; a[i] = a[j]; a[j] = t; } //对数组内的元素进行排序 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; } } //判断 left>=right,如果是,则证明元素扫描完毕,结束循环,如果不是,则交换元素即可 if (left>=right){ break; }else{ exch(a,left,right); } } //交换分界值 exch(a,lo,right); return right; } }

package com.haifei.demo01sort.test; import com.haifei.demo01sort.Quick; import java.util.Arrays; public class Test09Quick { public static void main(String[] args) { Integer[] a= {6, 1, 2, 7, 9, 3, 4, 5, 8}; Quick.sort(a); System.out.println(Arrays.toString(a)); //[1, 2, 3, 4, 5, 6, 7, 8, 9] } }
2.4 排序的稳定性