美文网首页
7大经典排序

7大经典排序

作者: 黄靠谱 | 来源:发表于2019-02-17 17:12 被阅读31次

    概述

    1. 排序原理
    • 选择排序:遍历比较数组最大值,通过游标标记,最后和末位交换。2个for循环和index解决问题。
    • 冒泡排序:遍历比较最大值,不停交换最大值一直到末位,2个for循环解决问题。
    • 插入排序:维护一个递增的数组,通过一个游标,和最大的数比,比最大的大就break;否则游标和比较对象都左移
    • 希尔排序:3层for循环+while,插入排序的改进版本,跨度插入排序形成基本有序数组再插入排
    • 快排排序:每次首位元素通过交换找到它在该数组段中的位置,再递归,引入low high head tail关键标记位。基于这种思路,可以解决无序数组中中位数,和无需数组求中位数的算法,随机第一个元素切分数组,缩小范围的方式来定位查找目标。
    • 归并(分治):精华是拆分问题,把数组拆分成2个小数组最后在递归性的从小问题解决到最大的问题,第二个核心在于2个连续的有序数组的merge方法。利用一个辅助数组。
    • 堆排序:初始化的时候构建最大堆(调用 2/N ++以上的Node挨个swim),然后和末尾交换位置,下标自减,调用sink()即可
    1. 空间复杂度:
    • 归并排序依赖辅助数组,空间复杂度为N(Merge函数调用时,需要先写到辅助数组中,最后再写回到原数组)
    • 快速排序因为递归的次数(N-LogN次)(最差和最好),递归时要保存一些数据
    • 其它排序的空间复杂度都是1,常数级别
    1. 平均时间复杂度
    • 冒泡、选择、插入排序 N²
    • 希尔排序 N<实际<N²
    • 快排、归并、堆排 N*LogN

    选择排序

            for(int i=0;i<length;i++){
                int max=0;
                for(int j=1;j<length-i;j++){
                    if(arr[max]<arr[j]){
                        max=j;
                    }
                }
                Util.exchange(max, length-i-1, arr);
            }
    

    插入排序

            for(int i=1;i<length;i++){
                int index=i;
                for(int j=i-1;j>=0;j--){
                    if(arr[index]>arr[j]) break;
                    Util.exchange(index, j, arr);
                    index--;
                }
            }
    

    Shell

    public static void shellSortByGap(int gap, int[] arr){
            int length=arr.length;
            for(int i=0;i<gap;i++){
                for(int j=i+gap;j<length;j=j+gap){
                    int index=j;
                    while(index>i){
                        if(arr[index]>arr[index-gap]) break;
                        if(arr[index]<arr[index-gap]){
                            Util.exchange(index-gap, index, arr);
                            index-=gap;
                        }
                    }
                }
            }   
        }
    

    Quick

        public static void locate(int low,int high,int[] arr){
            if(low>=high) return;
            int index=low;
            int tail=high;
            while(tail>index){
                if(arr[index]>arr[index+1]){
                    Util.exchange(index, index+1, arr);
                    index++;
                }else if(arr[index]<arr[index+1]){
                    Util.exchange(index+1, tail, arr);
                    tail--;
                }
            }
            locate(low,index-1,arr);
            locate(index+1,high,arr);
        }
    

    heap

        public void sink(int k){
            //check是否有子节点
            while(2*k<=size){
                int bigSonNode=2*k;
                //如果有右子节点且右子节点大于左子节点则 最大子节点指向右子节点
                if((2*k+1<size)&&list.get(2*k)<list.get(2*k+1)){
                    bigSonNode=2*k+1;
                }
                
                //比较左子节点,如果小于,就交换
                if(list.get(k)<list.get(bigSonNode)){
                    Util.exchangeList(k, bigSonNode, list);
                    k=bigSonNode;
                    //检查是否存在右子树,如果右且大于父节点就交换
                  }else{
                      break;
                  }
                
            }
        }
    
        //通过从N/2开始往堆顶进行sink()操作构建堆有序
        public static void buildHeap(MaxPQ pq){
            int N=pq.list.size()-1;
            for(int i=N/2;i>0;i--){
                pq.sink(i);
            }
        }
    
        public static void HeapSort(MaxPQ pq){
            List<Integer> list=pq.getList();
            int size=list.size();
            int tail=size-1;
            for(int i=1;i<size;i++){
                Util.exchangeList(1, tail, list);
                pq.setSize(--tail);
                pq.sink(1);
            }
        }
    

    Merge

        //对一个数组按照下标进行归并排序
        //归并排序的精华所在:问题拆分,把一个大问题拆分为N个性质一样但更简单的小问题
        public static void mergeSort(int[] arr,int start,int end){
            //这也是递归的终结,当数组拆分到只有1个元素的时候,必然是有序的,否则就继续拆分
            if(start<end){
                //拆分大问题为两个小问题
                int mid=(start+end)/2;
                mergeSort(arr,start,mid);
                mergeSort(arr,mid+1,end);
                //小问题解决完之后,最后父问题
                merge(arr,start,mid,end);
            }
        }
    
        //有两个数组,一个数源数组arr1,一个是工具数组是长度一样但是都是null的arr2
        //arr1数组中有2个连续且有序的数组 arr11 和 arr12
        
        //low是arr11的start,mid是arr11的end,mid+1是arr12的start,high是arr12的end
        //设置了3个游标,i为arr11的游标,j为arr12的游标,k为arr2的游标
        
        //merge的思路是当arr11和arr12中从起始位置开始比较,最小的写到arr2中,一直到有一个掏空了
        //假如是arr11先掏空,那么顺序把arr12中的元素顺序写入到arr2中;反之把arr11中剩余元素写入到arr2中
        //最后再把工具数组里的数据按照low high位置写回到arr1中,这样可以arr1可以继续比较了
        public static int[] arr2=new int[20];
        public static void merge(int[] arr1,int low,int mid,int high){
            int i=low,k=low,j=mid+1;
            //当arr11和arr12中从起始位置开始比较,最小的写到arr2中,对应下标移动,一直到有一个掏空了为止
            while(i<=mid&&j<=high){
                if(arr1[i]<arr1[j]){
                    arr2[k++]=arr1[i++];
                }else {
                    arr2[k++]=arr1[j++];}
            }
            //如果是arr12先被掏空,则arr11中剩余元素写入到arr2中,这里可以优化
            while(i<=mid){
                arr2[k++]=arr1[i++];
            }
            //如果是arr11先被掏空,则arr12中剩余元素写入到arr2中,这里可以优化
            while(j<=high){
                arr2[k++]=arr1[j++];
            }
            //arr2排序结束写回到arr1中相应位置
            for (i = low; i <= high; i++) {
                arr1[i] = arr2[i];
            }
        }
    

    相关文章

      网友评论

          本文标题:7大经典排序

          本文链接:https://www.haomeiwen.com/subject/reogeqtx.html