• 高级排序


    希尔排序:

    针对于插入排序来说,复制的次数太多,在标记的左边部分数据项都是排过序的,在每次往右移动,新的数据项要移动到左边正确的位置,造成中间数据项都必须往右移动一位,这步骤对每个数据项都执行了将近N次复制,平均每次移动 N/2,总共是 N2/2次复制。因此排序的执行效率是O(N2)。

    希尔排序通过加大插入排序中元素之间的间隔(增量 - h),并在这些间隔中进行插入排序,从而使数据项能大跨度的移动。当这些数据项排过一趟之后,希尔排序减少数据项的间隔再进行排序,依次下去,最终h=1。

    public class ShellSort {
    
        public static void main(String[] args) {
            int[] array = { 1, 65, 48, 9, 5, 2, 33, 6, 45, 88, 11, 2, 59, 4 };
            int arraySize = array.length;
    
            int inner, outer;
            int temp;
            int h = 1;
            while (h <= arraySize / 3) {
                h = h * 3 + 1;
            }
            while (h > 0) {
                for (outer = h; outer < arraySize; outer++) {
                    temp = array[outer];
                    inner = outer;
                    while (inner > h - 1 && array[inner - h] >= temp) {
                        array[inner] = array[inner - h];
                        inner -= h;
                    }
                    array[inner] = temp;
                }
                h = (h - 1) / 3;
            }
    
            for (int r : array) {
                System.out.println(r);
            }
        }
        
    }

    希尔排序比插入排序快很多,当 h 指越大,数据项每一趟排序需要移动的元素个数很少,但数据项移动的距离很长,这是非常有效率的。当 h 减小时,每一趟排序需要移动的元素个数增多,但此时数据项已经很接近它们排序后的最终位置,这对于插入排序可以更好的效率。

    例子中生成间隔序列用 h = h * 3 +1,也可以运用其它的间隔序列如:N/2,但有一个绝对条件,逐渐减小的间隔最后一定等于1,因此最后一个排序是一次普通的插入排序。

    效率:除了一些特殊的情况下,还没有人能够从理论上分析希尔排序的效率,估计它的时间级从 O(N3/2) 到 O(N7/6)。

    划分算法

    划分数据就是把数据分为两组,所有大于特定值(pivot)的数据项分为一组,所有小于特定值的在另外一组。

    public class ArrayPar {
    
        private final int[] array = { 1, 65, 48, 9, 5, 2, 33, 6, 45, 88, 11, 2, 59,
                4 };
    
        private final int arraySize = array.length;
    
        private int partitionIt(int left, int right, long pivot) {
            int leftPtr = left - 1;
            int rightPtr = right + 1;
            while (true) {
                while (leftPtr < right && array[++leftPtr] < pivot)
                    ;
                while (rightPtr > left && array[--rightPtr] > pivot)
                    ;
                if (leftPtr >= rightPtr) {
                    break;
                } else {
                    swap(leftPtr, rightPtr);
                }
            }
            return leftPtr;
        }
    
        private void swap(int dex1, int dex2) {
            int temp;
            temp = array[dex1];
            array[dex1] = array[dex2];
            array[dex2] = temp;
        }
    
        public void display() {
            System.out.print("Sort : ");
            for (int i : array) {
                System.out.print(i + " ");
            }
        }
    
        public void partition() {
            int pivot = 50;
            int partDex = partitionIt(0, arraySize - 1, pivot);
            System.out.println("Partition is at index : " + partDex);
        }
    
        public static void main(String[] args) {
            ArrayPar ap = new ArrayPar();
            ap.partition();
            ap.display();
        }
    
    }

    打印结果:
    Partition is at index : 11
    Sort : 1 4 48 9 5 2 33 6 45 2 11 88 59 65

    效率:划分算法的运行时间是O(N),两个指针开始在数组的两端,然后以或大或小的恒定速度相向移动,停止移动并且在移动的过程中交换。当两个指针相遇,划分完成。更特别的是,每一次划分都有N+1或N+2的比较,因为两端指针相遇加起来一共走了N步,但彼此已经越过了,所以会在划分完成之前多出一两次比较。比较的次数不取决于数据如何排列。但交换的次数取决于数据的排列,假设数据是逆序排列,并且把数据划分一半,那么每一对都需要交换,也就是 N/2 次交换。所以对于任意的数据,在一次划分中交换次数都小于N/2。

    快速排序

    在大多情况下,快速排序都是最快的,执行时间为O(N*logN)。步骤如下:

    • 把数组或子数组分为左边一组(小于pivot)和右边一组(大于pivot),以最右边的数据项为pivot。
    • 调用自身对左边一组进行排序。
    • 再调用自身对右边一组进行排序。
    public class QuickSort {
    
        private final int[] array = { 1, 65, 48, 9, 5, 2, 33, 6, 45, 88, 11, 2, 59,
                4 };
    
        private final int arraySize = array.length;
    
        private int partitionIt(int left, int right, long pivot) {
            int leftPtr = left - 1;
            int rightPtr = right;
            while (true) {
                while (array[++leftPtr] < pivot)
                    ;
                while (rightPtr > 0 && array[--rightPtr] > pivot)
                    ;
                if (leftPtr >= rightPtr) {
                    break;
                } else {
                    swap(leftPtr, rightPtr);
                }
            }
            swap(leftPtr, right);   // 以最右边的数据项作为特定值,最后与划分点交换。
            return leftPtr;
        }
    
        private void swap(int dex1, int dex2) {
            int temp;
            temp = array[dex1];
            array[dex1] = array[dex2];
            array[dex2] = temp;
        }
    
        public void display() {
            System.out.print("Sort : ");
            for (int i : array) {
                System.out.print(i + " ");
            }
        }
    
        private void recQuickSort(int left, int right) {
            if (right - left <= 0) {
                return;
            }
            int pivot = array[right];
            int partition = partitionIt(left, right, pivot);
            recQuickSort(left, partition - 1);
            recQuickSort(partition + 1, right);
        }
    
        public void quickSort() {
            recQuickSort(0, arraySize - 1);
        }
    
        public static void main(String[] args) {
            QuickSort ap = new QuickSort();
            ap.quickSort();
            ap.display();
        }
    
    }

     以上的快速排序中,存在很大的问题:N个数据项的数组最理想的是每次划分能分成一半一半的数据项,最坏的划分情况是一个子数组只有一个数据项,另一个子数组含有N-1个数据项,每趟都是如此(在划分前已是有序排序,无论正逆,才出现的情况),那么这种情况下算法执行效率则降低到O(N2),还有就是调用递归可能发生溢出。

    三数据项取中 划分:

  • 相关阅读:
    Qomolangma实现篇(二):命名空间和别名子系统的实现
    关于Borland's IDE:发生了就发生了吧!
    Qomolangma实现篇(六):Qomo的OOP框架的实现技术
    Qomolangma实现篇(八):Qomo中的AOP框架
    经典的《JavaScript 权威指南》中的“对象”不经典
    Qomo OpenProject beta1 发布!
    弹出当前索引号案例
    tab栏切换效果案例
    [USACO18DEC]Sort It Out P
    [ABC163F]path pass i
  • 原文地址:https://www.cnblogs.com/xuekyo/p/3026319.html
Copyright © 2020-2023  润新知