zoukankan      html  css  js  c++  java
  • 高级排序

    希尔排序:

    针对于插入排序来说,复制的次数太多,在标记的左边部分数据项都是排过序的,在每次往右移动,新的数据项要移动到左边正确的位置,造成中间数据项都必须往右移动一位,这步骤对每个数据项都执行了将近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),还有就是调用递归可能发生溢出。

    三数据项取中 划分:

  • 相关阅读:
    mysql 函数 存储过程 事件(event) job 模板
    protobuf 无proto 解码 decode 语言 java python
    mitmproxy fiddler 抓包 填坑
    android adb 常用命令
    android机器人 模拟 踩坑过程
    RabbitMQ添加新用户并支持远程访问
    Windows下RabbitMQ安装及配置
    Java mybatis mysql 常用数据类型对应关系
    easyExcel 踩坑
    linux防火墙查看状态firewall、iptable
  • 原文地址:https://www.cnblogs.com/xuekyo/p/3026319.html
Copyright © 2011-2022 走看看