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

    10种排序算法的Java实现

    import java.util.*;
    
    public class Sort {
        static int min(int a,int b){
            return Math.min(a,b);
        }
        static int max(int a,int b) { return Math.max(a,b); }
        static void swap(int[] a,int i,int j){
            int t=a[i];
            a[i]=a[j];
            a[j]=t;
        }
        static void print(int[] a){
            for(int v:a){
                System.out.print(v+" ");
            }
            System.out.println();
        }
    
        /**
         * 冒泡排序,比较相邻两个元素并交换,每扫一遍将最大的元素交换到最后
         * 平均O(N^2) 最好O(N) 最坏O(N^2) 空间O(1) 稳定(相同元素不会交换)
         * @param a
         */
        static void bubbleSort(int[] a){
            int n=a.length;
            for(int i=n;i>0;i--){
                boolean flag=false;
                for(int j=1;j<i;j++){
                    if(a[j-1]>a[j]){
                        flag=true;
                        swap(a,j-1,j);
                    }
                }
                //将最好时间优化到O(N)
                if(!flag){
                    break;
                }
            }
        }
    
        /**
         * 选择排序,每次选择剩下的最小元素放在正确位置
         * 平均O(N^2) 最好O(N^2) 最坏O(N^2) 空间O(1) 不稳定(比如[5 8 5 2 9],第一个5会跟2交换,改变5的相对位置)
         * @param a
         */
        static void selectSort(int[] a){
            int n=a.length;
            for(int i=0;i<n-1;i++){
                int mn=Integer.MAX_VALUE;
                int idx=i;
                for(int j=i;j<n;j++){
                    if(a[j]<mn){
                        mn=a[j];
                        idx=j;
                    }
                }
                swap(a,i,idx);
            }
        }
    
        /**
         * 插入排序,前面部分是有序数组,把后面部分一个个插入到正确位置
         * 平均O(N^2) 最好O(N) 最坏O(N^2) 空间O(1) 稳定(相同值不会插入)
         * @param a
         */
        static void insertSort(int[] a){
            int n=a.length;
            for(int i=1;i<n;i++){
                if(a[i]>a[i-1]){
                    //将最好时间优化到O(N)
                    continue;
                }
                int t=a[i];
                int j=i-1;
                for(;j>=0;j--){
                    if(t<a[j]){
                        a[j+1]=a[j];
                    }else{
                        break;
                    }
                }
                a[j+1]=t;
            }
        }
    
        /**
         * 希尔排序(缩小增量插入排序)
         * 平均O(N^(1.3~2)) 最好O(N) 最坏O(N^2) 空间O(1) 不稳定(同一个值分在不同的组)
         * @param a
         */
        static void shellSort(int[] a){
            int n=a.length;
            //h为间隔/组数
            for(int h=n/2;h>0;h/=2){
                for(int i=h;i<n;i++){
                    //间隔为h的插入排序,普通插入排序就是间隔为1
                    if(a[i]>a[i-h]){
                        continue;
                    }
                    int t=a[i];
                    int j=i-h;
                    for(;j>0;j-=h){
                        if(t<a[j]){
                            a[j+h]=a[j];
                        }else{
                            break;
                        }
                    }
                    a[j+h]=t;
                }
            }
        }
    
        private static void mergeSort(int[] a,int l,int r){
            int len=(r-l+1);
            //剩一个元素,递归边界
            if(len<2){
                return;
            }
            int mid=(l+r)/2;
            //分
            mergeSort(a,l,mid);
            mergeSort(a,mid+1,r);
            //合并
            int[] b=new int[len];
            for(int idx=0,i=l,j=mid+1;idx<len;idx++){
                if(i==mid+1){
                    b[idx]=a[j++];
                }else if(j==r+1){
                    b[idx]=a[i++];
                }else if(a[i]<a[j]){
                    b[idx]=a[i++];
                }else{
                    b[idx]=a[j++];
                }
            }
            System.arraycopy(b,0,a,l,len);
        }
    
        /**
         * 归并排序,分治法,将两个有序的序列合并
         * 平均O(NlogN) 最好O(NlogN) 最坏O(NlogN) 空间O(N) 稳定
         * 空间算的是最大,所以是O(N)+O(N/2)+O(N/4)+...=O(N)
         * @param a
         */
        static void mergeSort(int[] a){
            int n=a.length;
            mergeSort(a,0,n-1);
        }
    
        //快排的排序其实是在划分这里
        private static int partition(int[] a,int l,int r){
            int t=a[l];
            while(l<r){
                while(l<r && a[r]>=t){
                    r--;
                }
                a[l]=a[r];
                while(l<r && a[l]<t){
                    l++;
                }
                a[r]=a[l];
            }
            a[l]=t;
            return l;
        }
    
        private static void quickSort(int[] a,int l,int r){
            int len=(r-l+1);
            if(len<2){
                return;
            }
            int p=partition(a,l,r);
            quickSort(a,l,p);
            quickSort(a,p+1,r);
        }
    
        /**
         * 快速排序,选定中轴进行划分,在递归划分的过程中进行排序
         * 平均O(NlogN) 最好O(NlogN) 最坏O(N^2) 空间O(logN) 空间最坏O(N) 不稳定(双指针扫的时候会改变相同数的相对位置)
         * @param a
         */
        static void quickSort(int[] a){
            int n=a.length;
            quickSort(a,0,n-1);
        }
    
        /**
         * 将数组a中,根为节点i的子树大顶堆化
         * @param a
         * @param i
         */
        private static void heapify(int[] a,int i,int n){
            int ls=2*i+1;
            int rs=2*i+2;
            int mx=i;
            if(ls<n && a[ls]>a[mx]){
                mx=ls;
            }
            if(rs<n && a[rs]>a[mx]){
                mx=rs;
            }
            if(mx!=i){
                swap(a,i,mx);
                heapify(a,mx,n);
            }
        }
    
        /**
         * 根据数组a前n个数建立大顶堆
         * @param a
         */
        private static void buildMaxHeap(int[] a,int n){
            //从最后一个非叶子节点开始,自底向上
            //最后一个叶子是n-1,最后一个非叶子就是n-1的父节点
            for(int i=(n-1)/2-1;i>=0;i--){
                heapify(a,i,n);
            }
        }
    
        /**
         * 堆排序,根据数组建立堆(数组模拟),每次取出堆顶放在最后,调整剩下的节点重新成为堆
         * 平均O(NlogN) 最好O(NlogN) 最坏O(NlogN) 空间O(1) 不稳定(每次把堆顶放在最后,有跳跃交换)
         * @param a
         */
        static void heapSort(int[] a){
            int n=a.length;
            buildMaxHeap(a,n);
            for(int i=n-1;i>=0;i--){
                swap(a,0,i);
                n--;
                heapify(a,0,n);
            }
        }
    
        /**
         * 计数排序,适合数据范围较小
         * 平均O(n+k) 最好O(n+k) 最坏O(n+k) 空间O(k) 稳定(前缀和优化后属于稳定排序)
         * @param a
         */
        static void countSort(int[] a){
            int n=a.length;
            int mn=Integer.MAX_VALUE;
            int mx=Integer.MIN_VALUE;
            for(int v:a){
                mn=min(mn,v);
                mx=max(mx,v);
            }
            int len=mx-mn+1;
            int[] cnt=new int[len];
            Arrays.fill(cnt,0);
            //计数
            for(int v:a){
                cnt[v-mn]++;
            }
            //前缀和
            for(int i=1;i<len;i++){
                cnt[i]+=cnt[i-1];
            }
            int[] b=new int[n];
            //倒序遍历原数组,保证后面的元素排名靠后
            for(int i=n-1;i>=0;i--){
                b[cnt[a[i]-mn]-1]=a[i];
                cnt[a[i]-mn]--;
            }
            System.arraycopy(a,0,b,0,n);
            //不加优化版,不稳定
            // int idx=0;
            // for(int i=0;i<len;i++){
                // while(cnt[i]-->0){
                    // a[idx++]=i+mn;
                // }
            // }
        }
    
        static ArrayList<Integer> bucketSort(ArrayList<Integer> a, int bucketSize){
            int n=a.size();
            int mn=Integer.MAX_VALUE;
            int mx=Integer.MIN_VALUE;
            for(int v:a){
                mn=min(mn,v);
                mx=max(mx,v);
            }
            int bucketCount=(mx-mn)/bucketSize+1;
            ArrayList<ArrayList<Integer>> bucket=new ArrayList<>(bucketCount);
            ArrayList<Integer> ans=new ArrayList<>();
            for(int i=0;i<bucketCount;i++){
                bucket.add(new ArrayList<>());
            }
            //装桶
            for(int v:a){
                bucket.get((v-mn)/bucketSize).add(v);
            }
            for(int i=0;i<bucketCount;i++){
                //每个桶单独排序再合并,可以调用其他排序算法
                Collections.sort(bucket.get(i));
                ans.addAll(bucket.get(i));
            }
            return ans;
        }
    
        /**
         * 桶排序(升级版计数排序)
         * 平均O(N+k) 最好O(N) 最坏O(N^2) 空间O(N+k) 稳定
         * @param a
         */
        static void bucketSort(int[] a){
            int n=a.length;
            int bucketSize=6;
            ArrayList<Integer> al=new ArrayList<>();
            for(int v:a){
                al.add(v);
            }
            ArrayList<Integer> ar=bucketSort(al,bucketSize);
            for(int i=0;i<n;i++){
                a[i]=ar.get(i);
            }
        }
    
        /**
         * 基数排序,按位,每一位分别进行计数排序
         * 平均O(N*k) 最好O(N*k) 最坏O(N*k) 稳定
         * @param a
         */
        static void radixSort(int[] a){
            int n=a.length;
            int mx=0;
            for(int v:a){
                mx=max(mx,v);
            }
            int dig=0;
            while(mx>0){
                dig++;
                mx/=10;
            }
            ArrayList<ArrayList<Integer>> al=new ArrayList<>();
            for(int i=0;i<10;i++){
                al.add(new ArrayList<>());
            }
            for(int i=0,mod=10,div=1;i<dig;i++,mod*=10,div*=10){
                //每一位
                for(int v:a){
                    int x=(v%mod)/div;
                    al.get(x).add(v);
                }
                int idx=0;
                for(int j=0;j<10;j++){
                    int siz=al.get(j).size();
                    for(int k=0;k<siz;k++){
                        a[idx++]=al.get(j).get(k);
                    }
                    al.get(j).clear();
                }
            }
        }
    
        public static void main(String[] args) {
            int[] a={1,8,6,7,15,11,4,2,13,14,12,10,5,9,3,14,8,8,6,11};
    //        bubbleSort(a);
    //        selectSort(a);
    //        insertSort(a);
    //        shellSort(a);
    //        mergeSort(a);
    //        quickSort(a);
    //        heapSort(a);
    //        countSort(a);
    //        bucketSort(a);
    //        radixSort(a);
            print(a);
        }
    }
    

    分类

    • 基于比较的排序算法
      冒泡排序,选择排序,插入排序,希尔排序,归并排序,快速排序,堆排序
    • 非比较的排序算法
      计数排序,桶排序,基数排序
    • 稳定的排序算法
      冒泡排序,插入排序,归并排序,计数排序,桶排序,计数排序
    • 不稳定的排序算法
      选择排序,希尔排序,快速排序,堆排序

    快排和归并的空间复杂度对比

    快排平均是O(logN) 最坏是O(N),而归并空间是O(N)。
    两者的区别在于快排是先partition,然后再递归下去,每层递归都会需要partition的临时空间O(1),加起来就是O(logN),如果退化到冒泡,就是O(N)。
    而归并是先两个sort递归下去,然后再merge,每层递归用的临时空间都是一样的O(N)级别。

  • 相关阅读:
    12.13 Redis缓存面试题精简版
    12.12 Oracle数据库相关面试题精简版(后续继续完善)
    1.131 IDEA2018版本64位激活
    7.11 读《如何阅读一本书》有感
    Linux下source命令详解(转载)
    Scala 随笔
    SparkStreaming实时流式大数据处理实战总结
    转载:hive的一些udaf
    IDEA的一些常见报错
    hive使用UDF函数
  • 原文地址:https://www.cnblogs.com/zxcoder/p/12259437.html
Copyright © 2011-2022 走看看