美文网首页
排序算法

排序算法

作者: pluss | 来源:发表于2018-10-13 08:22 被阅读0次

    常用排序算法总结(一)
    找出数组中出现次数最多的那个数——主元素问题


    Arrays.sort() 对基本类型用快速排序,非基本类型用归并排序,是因为基本类型不需要稳定性的排序,他们的相同值是无差别的。
    Collections.sort()使用的Arrays.sort()

    1. 堆排序
      堆排序是一种选择排序,整体主要由构建初始堆+交换堆顶元素和末尾元素并重建堆两部分组成。
      其中构建初始堆经推导复杂度为O(n),在交换并重建堆的过程中,需交换n-1次,
      而重建堆的过程中,根据完全二叉树的性质,[log2(n-1),log2(n-2)...1]逐步递减,近似为nlogn。
      所以堆排序时间复杂度一般认为就是O(nlogn);
      最坏最好都是O(nlogn);
      辅助空间O(1);
      不稳定;

    2. 快速排序
      快速排序每次递归取一个标准值,根据它来划分序列,划分总的再递归划分左右序列。
      时间复杂度是O(nlogn);
      最好是O(nlogn);
      最坏是O(n^2);倒序,此时需要随机取标准值p。
      因为递归划分序列,所以辅助空间O(logn)~O(n)
      不稳定

    3. 归并排序
      归并排序,递归平分序列到最底层(最底层只有一个数默认是排好序的),然后归并左右序列。
      时间复杂度是O(nlogn);
      最好最坏都是O(nlogn);
      辅助空间O(n);
      稳定

    1. 直接选择排序
      暴力排序,每次遍历数组选出一个最大值
      最好最坏都是O(n^2)
      辅助空间O(1);
      不稳定;

    2. 直接插入排序
      外循环从左到右i,
      内循环比较当前值和后面的值,小于就交换(可以保存当前值,然后把要交换的值直接右移一位,不用真的去交换),否则退出循环
      0到i始终有序
      最好O(n)
      最坏O(n^2)
      平均O(n^2)
      辅助空间O(1)
      稳定

      • 改进:二分插入排序,如果比较的代价比交换的代价大的话,就可以使用这个算法减少比较次数,最优O(nlogn);
        二分法在左边序列中定位当前值要插入的位置,把该位置右边的值都向右移动一个位置,插入。
    3. 冒泡排序
      外循环从大到小 j
      内循环从0到j,当前值和前面的值比较,大于就交换。
      每次内循环结束最大值都会浮到最上方。
      最坏是O(n^2);
      设一个标记标记内循环有没交换过,可以把最优时间复杂度降到O(n);
      稳定;

      • 改进:鸡尾酒排序(定向冒泡排序), 与冒泡排序不同在从低到高然后从高到低。以序列(2,3,4,5,1)为例,鸡尾酒排序只需要访问一次序列就可以完成排序,但如果使用冒泡排序则需要四次。但是在乱数序列的状态下,鸡尾酒排序与冒泡排序的效率都很差劲。
    void CocktailSort(int A[], int n)
    {
        int left = 0;                            // 初始化边界
        int right = n - 1;
        while (left < right)
        {
            for (int i = left; i < right; i++)   // 前半轮,将最大元素放到后面
            {
                if (A[i] > A[i + 1])
                {
                    Swap(A, i, i + 1);
                }
            }
            right--;
            for (int i = right; i > left; i--)   // 后半轮,将最小元素放到前面
            {
                if (A[i - 1] > A[i])
                {
                    Swap(A, i - 1, i);
                }
            }
            left++;
        }
    }
    
    1. 希尔排序
    2. 基数排序(桶排序?)
    3. 计数排序?

    堆排序

    package sort;
    
    import java.util.Arrays;
    
    /**
     * 堆排序是一种选择排序,整体主要由构建初始堆+交换堆顶元素和末尾元素并重建堆两部分组成。
     * 其中构建初始堆经推导复杂度为O(n),在交换并重建堆的过程中,需交换n-1次,
     * 而重建堆的过程中,根据完全二叉树的性质,[log2(n-1),log2(n-2)...1]逐步递减,近似为nlogn。
     * 所以堆排序时间复杂度一般认为就是O(nlogn);
     * 最坏最好都是O(nlogn);辅助空间O(1);不稳定;
     */
    public class HeapSort {
        public static void main(String []args){
            int []arr = {9,10,7,6,5,4,3,2,1};
            sort(arr);
            System.out.println(Arrays.toString(arr));//[1, 2, 3, 4, 5, 6, 7, 9, 10]
        }
        public static void sort(int[] arr) {
            //先构建大顶堆 O(n)
            //从最后一个非叶子节点开始 length/2-1
            //从下至上,从右到左
            for(int i =arr.length/2-1;i>=0;i--) {
                adjustHeap(arr,i,arr.length);
            }
            //调整堆结构,交换堆顶元素与末尾元素
            for(int i=arr.length-1;i>0;i--) {
                swap(arr,0,i);
                adjustHeap(arr,0,i);
            }
    
        }
        /**
         * 调整最大堆
         * 堆大小小于等于3的可以当做是最大堆,所以构建最大堆时可以调用这个方法从下到上调整
         * 循环子层直到子节点不大于父节点。
         * @param arr 数组
         * @param i 父节点
         * @param length 要调整的数组长度,0~length-1
         */
        public static void adjustHeap(int[] arr,int i,int length){
            //如果子结点大于父结点的话,交换两者的位置,又因为交换之后又要判断下一层的父结点和子节点
            //所以可以先把当前节点存起来,等到都比较好位置调好后,再把这个数值放在可以覆盖的节点上。
            //i的左子节点是2i+1
            int temp = arr[i];
            for(int k=2*i+1;k<length;k=2*k+1) {//一层层往下判断,直到父结点大于子节点
                if(k+1<length && arr[k]<arr[k+1]){//如果左子节点小于右子结点,k指向右子结点
                    k++;//k指向最大的子结点
                }
                if(arr[k]>temp) {//如果子结点大于父结点的话,将子结点赋给父结点
                    arr[i]=arr[k];
                    i=k;//继续下一层的判断,下一层的父结点还是temp
                }else {
                    break;//如果子结点小于等于父结点的话,就不需要再调整堆了
                }
            }
            arr[i]=temp;
        }
        public static void swap(int[] arr,int a,int b) {
            int temp=arr[a];
            arr[a] = arr[b];
            arr[b] = temp;
        }
    }
    
    

    快速排序

    package sort;
    
    import java.util.Arrays;
    import java.util.Random;
    import java.util.Stack;
    
    /**
     * 快速排序每次递归取一个标准值,根据它来划分序列,划分总的再递归划分左右序列。
     * 时间复杂度是O(nlogn);
     * 最好是O(nlogn);
     * 最坏是O(n^2);倒序,此时需要随机取标准值p。
     * 因为递归划分序列,所以辅助空间O(logn)??
     */
    public class QucikSort {
    
        public static void main(String []args){
            int []arr = {9,10,7,6,5,4,3,2,1};
            sort(arr,0,arr.length-1);
            System.out.println(Arrays.toString(arr));
        }
    
        /**
         * 从上到下递归,先划分总的,再划分左右序列
         * @param arr
         * @param start
         * @param end
         */
        public static void sort(int[] arr,int start,int end) {
            if(start>=end){
                return;
            }
            int i = partition(arr,start,end);
            sort(arr,start,i-1);
            sort(arr,i+1,end);
    
        }
        //非递归实现,用栈存start和end
        public static void sortStack(int[] arr){
            Stack<Integer> stack = new Stack<Integer>();
            stack.push(0);
            stack.push(arr.length-1);
            while (!stack.isEmpty()){
                int end = stack.pop();
                int start = stack.pop();
                int i = partition(arr,start,end);
                if(start<i-1){
                    stack.push(start);
                    stack.push(i-1);
                }
                if(end>i+1){
                    stack.push(i+1);
                    stack.push(end);
                }
            }
        }
    
        /**
         * 根据选定的标准p来划分序列,小于p的放左边,大于p的放右边,p在中间
         * 头尾指针实现
         * @param arr 待划分数组
         * @param start 要开始划分的下标
         * @param end 结束划分的下标
         * @return 返回最后p所在的下标
         */
        private static int partition(int[] arr,int start,int end){
            if(start>=end){
                return start;
            }
            int ran = (int)(Math.random()*(end-start+1))+start;//随机取标准值p
            swap(arr,ran,start);
    
            int p = arr[start];
            int i = start; // 两个指针,右边指针先开始移动,碰到小于p的数时把它放到左边指针的位置;然后开始移动左指针,操作相反;两指针轮流移动直到i>=j。
            int j = end;
            while(i<j){
                while (i<j&&arr[j]>=p){
                    j--;
                }
                arr[i]=arr[j];
                while (i<j&&arr[i]<=p){
                    i++;
                }
                arr[j]=arr[i];
            }
            arr[i]=p;
            return i;
        }
        private static void swap(int[] arr,int a,int b){
            int temp = arr[a];
            arr[a] = arr[b];
            arr[b] = temp;
        }
    }
    
    
    

    归并排序

    package sort;
    
    import java.util.Arrays;
    
    /**
     * 归并排序,递归平分序列到最底层(最底层只有一个数默认是排好序的),然后归并左右序列
     * 时间复杂度是O(nlogn);
     * 最好最坏都是O(nlogn);
     * 辅助空间O(n);
     * 稳定
     */
    public class MergeSort {
    
        public static void main(String []args){
            int []arr = {9,10,7,6,5,4,3,2,1};
    //        sort(arr,0,arr.length-1);
            sortIteration(arr);
            System.out.println(Arrays.toString(arr));
        }
    
        //非递归实现
        public static void sortIteration(int[] arr){
            //从底到上
            int left;
            int mid;
            int right;
            for(int i=1;i<arr.length;i*=2) {//子序列大小,每轮乘2
                left = 0;
                while(left+i<arr.length) {//后一个子序列存在的话,归并两个序列
                    mid = left+i-1;
                    right = mid+i;
                    right = right<arr.length?right:arr.length-1;
                    merge(arr,left,mid,right);
                    left = right+1;
                }
            }
        }
        public static void sort(int[] arr,int start,int end) {
            if(start>=end){
                return;
            }
            int h = (start+end)/2;
            sort(arr,start,h);
            sort(arr,h+1,end);
            merge(arr,start,h,end);
    
        }
        private static void merge(int[] arr,int start,int h,int end){
            int i = start;//左序列,start~h
            int j = h+1;//有序列,h+1~end
            int[] aux = new int[end-start+1];//辅助数组
            int k = 0;
            while (i<=h&&j<=end){
                while (i<=h&&j<=end&&arr[i]<=arr[j]) {
                    aux[k++]=arr[i++];
                }
                while (i<=h&&j<=end&&arr[j]<arr[i]) {
                    aux[k++]=arr[j++];
                }
            }
            while (i<=h){
                aux[k++]=arr[i++];
            }
            while (j<=end){
                aux[k++]=arr[j++];
            }
            System.arraycopy(aux,0,arr,start,aux.length);
        }
    
        private static void swap(int[] arr,int a,int b){
            int temp = arr[a];
            arr[a] = arr[b];
            arr[b] = temp;
        }
    }
    
    

    代码戳这里

    相关文章

      网友评论

          本文标题:排序算法

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