//这里准备用策略模式写一个排序系统,从小到大排序 //先写一个测试类 public class Ztest{ //不用括号Ztest() public static void main (String[] args){ Sorter testsorter = new MergeSorter(); Integer[] a = new Integer[]{5,5,8,9,34,3,56,6,7,0}; //只有String和Integer默认实现了Comparable方法 //然后注意这里初始化非基本类型数组采用的方式 testsorter.sort(a); //自动识别类型 for(Integer i:a) System.out.println(i); //这里是打印i,不是a[i] } } //定义一个基类,不同的策略是它的子类中的同名方法 //行业上更习惯把基类搞成接口 interface Sorter{ //不用public,默认就是 public <T extends Comparable<T>> void sort(T[] list); //(这个T extends Comparable<T> 非常有意思,它的意思是:所有Comparable类的子类可以作为实际参数赋值给T,另外,这个类本身需要参数,而且在这里,这两个参数必须一样) } //【1】第一个是简单的选择排序 //思路非常简单直接,第一步找出最小的(或最大的)元素,放在一头,然后依次重复,最后排序结束 //然后怎么找最小的,使用比较,用一个temp值和一个temp序号作为比较器。不对,不用一个temp值作为比较器,只需要一个temp序号 class ChooseSorter implements Sorter{ public <T extends Comparable<T>> void sort(T[] list){ for(int j = 0;j<list.length-2;j++){ int minnumb = j ; for(int i = 1;i<list.length-1;i++){ if (list[i].compareTo(list[minnumb])<0){ minnumb = i; } } //第一重循环到这里就找出了第一个最小值,然后进行交换 Integer temp = list[minnumb]; list[minnumb] = list[j]; list[j] = temp; //然后在外面再套一层循环,OK } } } //【2】第二个是插入排序 //【3】第三个是归并排序 //归并排序真的非常吊,感觉把分冶思想用到极致了,据说这个其实比快排要快 //归并的具体方法是,先分,再边合边排序 //【3.1】 class MergeSorter implements Sorter{ public <T extends Comparable<T>> void sort(T[] list){ //这里和第二个方法间可以使用方法重载,也可以另外命名方法,我这里就直接重载了 T[] temp = (T[]) new Comparable[list.length]; //归并排序不能原地排序,还需要一个同等大小的数组空间 //这个东西我觉得可以作为类的对象在方法中被赋值之后被调用,也可以在方法中申明,然后不断作为参数传递(免得不断新建,节省空间?),是一样的 //这个东西的另一个实现方式是先把list复制到temp中,最后在merge的时候一个个覆盖回list中,这种方法要快一些,只是注意复制的时候不要复制错了 //然后这里的这个temp的声明形式也是非常有意思 sort(temp,list,0,list.length-1);//以后记得还是用length-1,然后判断边界用<=,因为不这样做的话在其他的复杂一点的边界计算中容易出错 } public <T extends Comparable<T>> void sort(T[] temp,T[] list,int start ,int end){ if (start == end){ //先写跳出循环 return; //直接跳出这个函数就行了,不用返回什么 } else if(start < end){ int mid = start+(end-start)/2; //防止溢出int,先定义也省的算两次 //这三句是核心算法 sort(temp,list,start,mid); //分冶 sort(temp,list,mid+1,end); merge(temp,list,start,mid,end); //合并的同时排序 } } //merge方法说起来非常简单,但是实现起来还是有点费劳力的,而且有些坑(边界,如果是i<=k之后还有++,就意味着这时候i=k+1) public <T extends Comparable<T>> void merge(T[] temp,T[] list,int start ,int mid,int end){ int i,j,k; for (i=start;i<=end;i++){ temp[i] = list[i]; //先复制进temp } //这是merge方法的核心 for (i=0,j=0,k=0;start+i<=mid&&mid+1+j<=end;){ //不要写错,++不要写到这里 //有三个指针,不要漏了 //这里的跳出语句是一个且判断,感觉有些笨拙了,应该有更加灵活一些的方法 if (temp[start+i].compareTo(temp[mid+1+j]) < 0){ list[start+k]=temp[start+i]; i++;k++; //这是两个语句,之间不能是逗号,必须分号 }else { //相等的情况放到这也没问题 list[start+k]=temp[mid+1+j]; j++;k++; } } //看k指针有没有指到头,也就是start+k是否等于end,等于就巧了,直接返回,要是不等于,就说明有个子数组还有数,再看另外两个指针,判断出哪个没完就把哪个复制过去 if (start+k == end+1) return; //卧槽,调出来了,这个边界也是非常蛋疼啊 else{ if (start+i == end+1){ //第一个完了,于是把第二个的后边复制过去 for (;mid+1+j<=end;j++,k++) list[start+k]=temp[mid+1+j]; }else{ for (;start+i<=mid;i++,k++) list[start+k]=temp[start+i]; } } } } //【4】第四个是快排 class QuickSorter implements Sorter{ @Override public <T extends Comparable<T>> void sort(T[] list){ } } //【5】第五个是冒泡排序 //冒泡的关键不是移动某个值,而是关注位置 //按顺序从前往后比较并交换每相邻两个位置中的值 //【5.1】 class BubbleSorter implements Sorter{ @override public <T extends Comparable<T>> void sort(T[] list){ int i,j; T temp; //这块不要申明错了,放到Int里边去了 //这两个for循环和里边的交换判断 是核心算法 for (i=list.length-2;i>=0;i--){ //这里i和j的边界琢磨起来还是好麻烦啊 //另外默认list有 .length 属性 for (j=0;j<=i;j++){ if(list[j].compareTo(list[j+1])>0){ //如果左边的数大于右边的数,就交换这两个数 //如果不大于的话就直接过,不用其他的了,break什么的也不用 //下面的交换应该很熟练了 temp = list[i]; list[i] = list[i+1]; list[i+1] = temp; } } } } } //【5.2】一个改进,加入一个状态tag class BubbleSorter implements Sorter{ @Override //O要大写 public <T extends Comparable<T>> void sort(T[] list){ int i,j; T temp; boolean tag = true; //在这加入一个状态机,当有一轮冒泡的时候出现一次交换都没有的情况的话,就说明前面的都是有序的了,就不用再冒泡了 //可以通过在后边加一个If判断来break,更简单的方法是把判断放到for语句中 //开始的时候设置状态机初始值 for (i=list.length-2;i>=0&&(tag==true);i--){ //这个tag==true其实可以直接是tag tag = false; //在进入下层循环之前修改状态机,只有下层循环中的if语句执行了,修改回来了状态机,才能继续上层循环 //为什么不把这句放到下一层循环中if之前,因为只需要一轮冒泡中有一次交换就行了 for (j=0;j<=i;j++){ if(list[j].compareTo(list[j+1])>0){ temp = list[j]; list[j] = list[j+1]; list[j+1] = temp; tag = true; } } } } }
时间: 2024-11-03 12:27:24