高级排序

希尔排序:

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

三数据项取中 划分:

posted @ 2013-04-17 15:00  Kyle_Java  阅读(560)  评论(0编辑  收藏  举报