数据结构-高级排序

来源:互联网 发布:淘宝网男士皮欧上衣 编辑:程序博客网 时间:2024/06/02 04:21

简述     

高级排序算法:希尔排序和快速排序。

其执行效率分别为希尔排序的O(N*(logN)^2)和快速排序的O(N*logN)。

归并排序相较于简单排序中冒泡排序、选择排序和插入排序的执行速度要快,但是需要原始数据空间的两倍。而希尔排序和快速排序不需要大量的辅助空间。

具体实现

希尔排序

package com.jikefriend.arraysort;/** * 希尔排序,其基于插入排序,不适合大规模数据排序 * *  希尔排序通过加入插入排序中元素之间的间隔,并在这些有间隔的元素中进行插入排序, *  从而使数据项能大跨度地移动。当这些数据项排过一趟序后,希尔排序算法减小数据项 *  的间隔再进行排序,依此进行下去 * *  进行这些排序时数据项之间的间隔称为增量 *  对于10个元素,通过 h = h * 3 + 1 确定间隔,有4和1 *  间隔为4时对(0,4,8)(1,5,9)(2,6)(3,7)分别排序,实际不是按此分组排序, *  而是先排第一组的前两个数据项,然后是第二组的前两个数据项,以此类推, *  当所有组的前两个数据项有序后,算法返回再对三个数据项的组进行排序,因此4-增量的实际执行顺序是 *  (0,4)(1,5)(2,6)(3,7)(0,4,8)(1,5,9) *  间隔为1时,排序后,算法结束 * * 排序间隔序列的子数组,逐渐减小间隔,直至为1 * 注意:后续的排序不撤销前期排序的结果 */public class ShellSort {    private int[] theArray;    private int nElems;    public ShellSort(int max)    {        theArray = new int[max];        nElems = 0;    }    public void insert(int value)    {        theArray[nElems++] = value;    }    public void display()    {        System.out.print("A=");        for (int i = 0; i < nElems; i++)            System.out.print(theArray[i] + " ");        System.out.println();    }    public void shellSort()    {        int inner, outer;        int temp;        int h = 1;        while (h <= nElems / 3)                              //找到间隔序列的h初始值            h = h * 3 + 1;        while (h > 0)                                        //根据公式 h = (h - 1) / 3 递减,直到h = 1        {            for (outer = h; outer < nElems; outer++)         //以h为间隔对数组排序            {                temp = theArray[outer];                inner = outer;                while (inner > h - 1 && theArray[inner - h] >= temp)                {                    theArray[inner] = theArray[inner - h];                    inner -= h;                }                theArray[inner] = temp;            }            h = (h - 1) / 3;        }    }    public static void main(String[] args) {        int maxSize = 10;        ShellSort arr = new ShellSort(maxSize);        for (int i = 0; i < maxSize; i++) {            arr.insert((int)(Math.random() * 99));        }        arr.display();        arr.shellSort();        arr.display();    }}

划分

package com.jikefriend.arraysort;/** * 划分 *  划分数据是把数据分成两组, *  使所有关键字大于特定值的数据项在一组, *  所有关键字小于特定值的数据项在一组。 * * 注意: *  划分是不稳定的,即每一组中的数据项并不是按照它原来的顺序排列的。 *  事实上,划分往往会颠倒组中一些数据项的顺序。 */public class PartitionArray {    private int[] theArray;    private int nElems;    public PartitionArray(int max)    {        theArray = new int[max];        nElems = 0;    }    public void insert(int value)    {        theArray[nElems++] = value;    }    public int size()    {        return nElems;    }    public void display()    {        for (int i = 0; i < nElems; i++)            System.out.print(theArray[i] + " ");        System.out.println();    }    /**     * 划分算法由两个指针开始工作,分别指向数组的两头。     * 左边的指针向右移动,右边的指针向左移动。     * @param left     * @param right     * @param pivot     * @return     */    public int partition(int left, int right, int pivot)    {        int leftPtr = left - 1;        int rightPtr = right + 1;        while (true)        {            /** leftPtr当前的值如果比特定值小,则继续右移,因为这个数据项已经处在正确的一侧,直至找到比特定值大的数据项停止移动 */            while (leftPtr < right && theArray[++leftPtr] < pivot)  //找出比特定值大的数据项                ;            /** rightPtr当前的值如果比特定值大,则继续左移,因为这个数据项已经处在正确的一侧,直至找到比特定值小的数据项停止移动 */            while (rightPtr > left && theArray[--rightPtr] > pivot) //找出比特定值小的数据项                ;            /** 当两个while都退出循环时,leftPtr和rightPtr都指向在数组的错误一方位置上的数据,交换这两个数据 */            if (leftPtr >= rightPtr)                                //如果两个指针相遇,则划分结束                break;            else                                                    //如果两个指针还没有相遇,则交换两个数据项                swap(leftPtr, rightPtr);                            //使小的数据项在特定值的左侧,大的数据项在右侧        }        return leftPtr;    }    private void swap(int dex1, int dex2)    {        int temp = theArray[dex1];        theArray[dex1] = theArray[dex2];        theArray[dex2] = temp;    }    public static void main(String[] args) {        int maxSize = 16;        PartitionArray arr = new PartitionArray(maxSize);        for (int i = 0; i < maxSize; i++) {            arr.insert((int)(Math.random() * 199));        }        arr.display();        int pivot = 99;        System.out.print("Pivot is " + pivot);        int size = arr.size();        int partDex = arr.partition(0, size - 1, pivot);        System.out.println(", Partition is at index " + partDex);        arr.display();    }}

快速排序

使用最右端数据项作为枢纽值

package com.jikefriend.arraysort;/** * 快速排序,O(N*logN),最坏O(N^2) * 基本步骤: *  1、使用划分算法把数组或子数组划分成左边(较小的关键字)的一组和右边(较大的关键字)的一组; *  2、调用自身对左边的一组进行排序; *  3、再次调用自身对右边的一组进行排序。 * * 基值情况:当数组只有一个数据项时,认为已经有序。 * * 目标值(枢纽)的选取思想: *  1、应该选择具体的一个数据项的关键字的值作为枢纽; *  2、可以选择任意一个数据项作为枢纽。为了简便,我们假设总是选择划分的子数组最右端的数据项作为枢纽; *  3、划分完成之后,如果枢纽被插入到左右子数组之间的分界处,那么枢纽就落在排序之后的最终位置上。 */public class QuickSort1 {    private int[] theArray;    private int nElems;    public QuickSort1(int max)    {        theArray = new int[max];        nElems = 0;    }    public void insert(int val)    {        theArray[nElems++] = val;    }    public void display()    {        for (int i = 0; i < nElems; i++)            System.out.print(theArray[i] + " ");        System.out.println();    }    public void quickSort()    {        recQuickSort(0, nElems - 1);    }    private void recQuickSort(int left, int right)    {        if (right - left <= 0)                                   //当数组的大小 <= 1时,认为已经有序            return;        else        {            int pivot = theArray[right];                         //选择数组的最右端数据项作为枢纽值            int partition = partition(left, right, pivot);       //划分数组,使小于枢纽值的数据项在左边子数组,大于枢纽值的在右边子数组            recQuickSort(left, partition - 1);                   //递归调用该方法对左边数组排序            recQuickSort(partition + 1, right);                  //递归调用该方法对右边数组排序        }    }    /**     * 根据枢纽值划分数组     * @return 完成划分后枢纽值的位置     */    private int partition(int left, int right, int pivot)    {        int leftPtr = left - 1;        int rightPtr = right;        while (true)        {            while (theArray[++leftPtr] < pivot)                 //由于枢纽值选取数组最右端,所以leftPtr不会发生数据越界                ;            while (rightPtr > 0 && theArray[--rightPtr] > pivot)                ;            if (leftPtr >= rightPtr)                break;            else                swap(leftPtr, rightPtr);        }        swap(leftPtr, right);                                  //right是枢纽值所在的位置,即数组最右端,完成划分后,将枢纽值保存在分界处        return leftPtr;    }    private void swap(int dex1, int dex2)    {        int temp = theArray[dex1];        theArray[dex1] = theArray[dex2];        theArray[dex2] = temp;    }    public static void main(String[] args) {        int maxSize = 10;        QuickSort1 arr = new QuickSort1(maxSize);        for (int i = 0; i < maxSize; i++) {            arr.insert((int)(Math.random() * 99));        }        arr.display();        arr.quickSort();        arr.display();    }}

使用三数据项取中法选取枢纽值

package com.jikefriend.arraysort;/** * 快速排序,O(N*logN),最坏O(N^2) * “三数据项取中”划分 *  找到数组里第一个、最后一个以及中间位置数据项的居中数据项值,作为枢纽值。 * * 由于在选择枢纽的同时,对该三个数据项进行了排序,保证了最左端的数据项小于枢纽值, * 最右端的大于枢纽值,两端指针避免的越界。 */public class QuickSort2 {    private int[] theArray;    private int nElems;    public QuickSort2(int max)    {        theArray = new int[max];        nElems = 0;    }    public void insert(int val)    {        theArray[nElems++] = val;    }    public void display()    {        for (int i = 0; i < nElems; i++)            System.out.print(theArray[i] + " ");        System.out.println();    }    public void quickSort()    {        recQuickSort(0, nElems - 1);    }    private void recQuickSort(int left, int right)    {        int size = right - left + 1;        if (size < 10)                                           //当数组的大小 < 10时,使用插入排序            insertionSort(left, right);        else        {            int pivot = medianOf3(left, right);                  //三数据项取中来选取枢纽值            int partition = partition(left, right, pivot);       //划分数组,使小于枢纽值的数据项在左边子数组,大于枢纽值的在右边子数组            recQuickSort(left, partition - 1);                   //递归调用该方法对左边数组排序            recQuickSort(partition + 1, right);                  //递归调用该方法对右边数组排序        }    }    private int medianOf3(int left, int right)    {        int center = (left + right) / 2;        if (theArray[left] > theArray[center])  //对left和center排序            swap(left, center);        if (theArray[left] > theArray[right])   //对left和right排序            swap(left, right);        if (theArray[center] > theArray[right]) //对center和right排序            swap(center, right);        swap(center, right - 1);                //将选定的枢纽值放到数组最右端-1        return theArray[right - 1];    }    /**     * 根据枢纽值划分数组     * @return 完成划分后枢纽值的位置     */    private int partition(int left, int right, int pivot)    {        int leftPtr = left;        int rightPtr = right - 1;        while (true)        {            while (theArray[++leftPtr] < pivot)                 //由于枢纽值选取数组最右端,所以leftPtr不会发生数据越界                ;            while (theArray[--rightPtr] > pivot)                ;            if (leftPtr >= rightPtr)                break;            else                swap(leftPtr, rightPtr);        }        swap(leftPtr, right - 1);                                //right - 1是枢纽值所在的位置,即数组最右端-1,完成划分后,将枢纽值保存在分界处        return leftPtr;    }        private void swap(int dex1, int dex2)    {        int temp = theArray[dex1];        theArray[dex1] = theArray[dex2];        theArray[dex2] = temp;    }    /**     * 插入排序,处理小划分     */    private void insertionSort(int left, int right)    {        int in, out;        for (out = left + 1; out <= right; out++)        {            int temp = theArray[out];            in = out;            while (in > left && theArray[in -1] >= temp)            {                theArray[in] = theArray[in - 1];                --in;            }            theArray[in] = temp;        }    }    public static void main(String[] args) {        int maxSize = 10;        QuickSort2 arr = new QuickSort2(maxSize);        for (int i = 0; i < maxSize; i++) {            arr.insert((int)(Math.random() * 99));        }        arr.display();        arr.quickSort();        arr.display();    }}

摘自《Java数据结构算法(第二版)》 [美] Robert Lafore 著

0 0
原创粉丝点击