zoukankan      html  css  js  c++  java
  • 算法练习排序算法

    //这里准备用类似策略模式写一个排序系统,从小到大排序
    //先写一个测试类
    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;
                    }
                }
            }
        }
    }
  • 相关阅读:
    思源:秒级体验百亿级数据量监控钻取
    禧云Redis跨机房双向同步实践
    谈谈数据中台技术体系
    RCA:收单设备调用云端接口频繁超时排查总结
    技术上的“深淘滩,低作堰”
    企业私有源代码上传github致入侵之大疆案判决了
    那些年我们一起犯过的错
    异地双活的四个误区
    没有预见性你凭什么晋升
    中国IT史上两大严重事故对我们的警醒及预防措施
  • 原文地址:https://www.cnblogs.com/bellkosmos/p/5274319.html
Copyright © 2011-2022 走看看