算法练习排序算法

//这里准备用类似策略模式写一个排序系统,从小到大排序
//先写一个测试类
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;
                }
            }
        }
    }
}
原文地址:https://www.cnblogs.com/bellkosmos/p/5274319.html