34-咸鱼学Java-快速排序递归与非递归

时间:2021-11-26 00:28:46

简介

快速排序由C. A. R. Hoare在1962年提出。它的基本思想是:通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。—–百度百科
其主要原理是每次都能确定一个元素的确定位置。
时间复杂度 好情况(无序) O(nlog2n) 怀情况(有序)O(n^2)
每次Partition O(n^2)

递归方式代码

/** * 快速排序主函数 * @param array */
public static void quickSort(int[] array)
{
    //第一次调用快排
    Quick(array, 0, array.length-1);
}


/** * 返回基准下标,相当于每次为一个元素找到其位置,将数组分为两部分 * @param array 数组 * @param start 开始下标 * @param end 结束下标 * @return 基准下标 */
public static int partion(int[] array,int low,int hight)
{   
    //找到一个基准
    int temp = array[low];  
    //遍历直到所有遍历完
    while(low<hight)
    {   
        //从右向左找比temp小的
        while(low<hight&&array[hight]>=temp)
        {
            --hight;
        }
        //如果相遇了,或交错了,直接跳出
        if(low>=hight)
        {
            break;
        }
        //如果符合条件,将小的放到当前low的位置。
        else
        {
            array[low] = array[hight];
        }
        //从左向右找比temp大的
        while(low<hight&&array[low]<=temp)
        {
            ++low;
        }
        //如果遍历完了,跳出
        if(low>=hight)
        {
            break;
        }
        //如果符合条件,将大的放到当前hight位置
        else
        {
            array[hight] = array[low];
        }
    }
    //将基准放入low
    array[low] = temp;
    //返回low
    return low;
}



/** * 快速排序递归 * @param array 数组 * @param start 开始下标 * @param end 结束下标 */
public static void Quick(int[] array,int start,int end)
{   //先找到基准下标
    int par = partion(array, start, end);
    //判断基准的左边是不是还有1个以上元素,如果是再调用快排
    if(par>start+1)
    {
        Quick(array, start, par-1);
    }
    //判断基准的右边是不是还有1个以上元素,如果是再调用快排
    if(par<end-1)
    {
        Quick(array, par+1, end);
    }
}

测试

public static void main(String[] args) {
    int [] a = {1,4,5,2,3,2,456,23,13};
    quickSort(a);
    System.out.println(Arrays.toString(a));
}

结果[1, 2, 2, 3, 4, 5, 13, 23, 456]

非递归代码

/** * 快速排序非递归 * @param array 目标数组 */
public static void quickSort1(int[] array)
{
    LinkStack a = new LinkStack();
    int low = 0;
    int hight = array.length-1;
    int par = partion(array, low, hight);
    if(par>low+1)
    {
        a.push(low);
        a.push(par-1);
    }
    if(par<hight-1)
    {
        a.push(par+1);
        a.push(hight);
    }
    //出栈
    while(!a.isEmpty())
    {
        hight = a.pop();
        low = a.pop();
        par = partion(array, low, hight);
        if(par>low+1)
        {
            a.push(low);
            a.push(par-1);
        }
        if(par<hight-1)
        {
            a.push(par+1);
            a.push(hight);
        }
    }
}


/** * 返回基准下标,相当于每次为一个元素找到其位置,将数组分为两部分 * @param array 数组 * @param start 开始下标 * @param end 结束下标 * @return 基准下标 */
public static int partion(int[] array,int low,int hight)
{   
    //找到一个基准
    int temp = array[low];  
    //遍历直到所有遍历完
    while(low<hight)
    {   
        //从右向左找比temp小的
        while(low<hight&&array[hight]>=temp)
        {
            --hight;
        }
        //如果相遇了,或交错了,直接跳出
        if(low>=hight)
        {
            break;
        }
        //如果符合条件,将小的放到当前low的位置。
        else
        {
            array[low] = array[hight];
        }
        //从左向右找比temp大的
        while(low<hight&&array[low]<=temp)
        {
            ++low;
        }
        //如果遍历完了,跳出
        if(low>=hight)
        {
            break;
        }
        //如果符合条件,将大的放到当前hight位置
        else
        {
            array[hight] = array[low];
        }
    }
    //将基准放入low
    array[low] = temp;
    //返回low
    return low;
}

测试

public static void main(String[] args) {
    int [] a = {1,4,5,2,3,2,456,23,13};
    quickSort1(a);
    System.out.println(Arrays.toString(a));
}

结果[1, 2, 2, 3, 4, 5, 13, 23, 456]

Partition解析

此方法中最难理解的就是partition,及找基准的过程,此时用6,4,5,8,2【low=0,hight=4】此数列进行举例分析。

次数 操作前数组 low hight temp 操作 操作后数组
1 6,4,5,8,2 0 4 6 从后向前找比6小的,找到2,hight保持不变为4,将2放置于下标0处 2,4,5,8,2
2 2,4,5,8,2 0 4 6 从前向后找比6大的,找到8,low变为3,将8放置于下标4处 2,4,5,8,8
3 2,4,5,8,8 3 4 6 从后向前找比6小的过程中发现low>hight了,整个循环跳出,将temp6放置于low下标处 2,4,5,6,8

这一次partition过后,会发现6之前都是比它小的,6之后都是比它大的。

非递归解析

递归和非递归的其本质区别其实只是:递归所用的是系统的栈,而且所有的现场都是收到保护的。在使用非递归进行快排的时候,其实只需要自己构造一个栈,然后根据需要的数据进行入栈出栈即可。