19.快速排序

快速排序

1. 定义

快速排序是指通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序。整个排序过程可以递归进行,以此达到整个数据变成有序序列。

2. 算法步骤

  1. 从数列中挑出一个元素,称为“基准”(pivot)(这里将最后一位元素作为基准元素)。
  2. 定义左右两个指针,一个从左往右寻找比基准值大的元素,一个从右往左找比基准值小的元素,找到后交换其位置,直到两个指针相遇,相遇后交换当前 左(右)指针 与 基准位置元素
  3. 调整后,所有比基准值小的元素摆放在基准前面,所有比基准值大的元素摆在基准后面(相同的数可以到任何一边)。在这个分区结束之后,该基准就处于数列的中间位置。这个称为分区(partition)操作。
  4. 递归地(recursively)把小于基准值元素的子数列和大于基准值元素的子数列排序。递归到最底部时,数列的大小是零或一,也就是已经排序好了。这个算法一定会结束,因为在每次的迭代(iteration)中,它至少会把一个元素摆到它最后的位置去。

3.实例分析

数组下标012345678
数组内容5273114816

现在要对上面这个数组进行快速排序

  • 首先我们以最左侧元素 6 作为基准

  • 定义左指针left从 0号位置 开始向右找比基准大的元素 右指针right从 8号位置 找比基准小的元素

  • 找到后,left指针指向 7,right指针指向 1

  • 交换两者位置,交换后数组内容如下所示

    数组下标012345678
    数组内容5213114876
  • 左右指针继续寻找,left指针指向 11,right指针指向 4

  • 交换两者位置,交换后数组内容如下所示

    数组下标012345678
    数组内容5213411876
  • 左右指针继续寻找,left指针指向 11(左指针必须小于右指针),right指针指向 11(左指针必须小于右指针),交换左右指针位置元素(由于指向同一元素,相当于没有交换)

  • 由于左右指针已经相遇,接着交换左(右)指针(11) 与 基准位置元素(6),交换后数组内容如下图所示

    数组下标012345678
    数组内容5213468711

接着递归求解以基准元素为分界线比基准元素小的部分[5,2,1,3,4] 与比基准元素大的部分[8,7,11]

对于[5,2,1,3,4] 这一部分

数组下标01234
数组内容52134
  • 以最左侧元素 4作为基准

  • 定义左指针left从 0号位置 开始向右找比基准大的元素 右指针right从 4号位置 找比基准小的元素

  • 找到后,left指针指向 5,right指针指向 3

  • 交换两者位置,交换后数组内容如下所示

数组下标01234
数组内容32154
  • 左右指针继续寻找,left指针指向 5(左指针必须小于右指针),right指针指向 5(左指针必须小于右指针),交换左右指针位置元素(由于指向同一元素,相当于没有交换)

  • 由于左右指针已经相遇,接着交换左(右)指针(5) 与 基准位置元素(4),交换后数组内容如下图所示

    数组下标01234
    数组内容32145

将其再次按照基准拆为左右两部分,(右边已经有序,循环一次后将直接退出)递归后结果如下

数组下标01234
数组内容12345

对于[8,7,11] 这一部分

数组下标678
数组内容8711
  • 以最左侧元素 11作为基准

  • left指针指向 11(左指针必须小于右指针),right指针指向 11(左指针必须小于右指针),交换左右指针位置元素(由于指向同一元素,相当于没有交换)

  • 交换后数组内容如下图所示

    数组下标678
    数组内容8711

至此排序结束,结果如下

数组下标012345678
数组内容1234567811

4.代码实现

public static void quick(int[] array)
{
    quickHelper(array, 0, array.length - 1);
}

/**
 * 快排递归方法
 * @param array
 * @param left
 * @param right
 */
private static void quickHelper(int[] array, int left, int right)
{
    if (left >= right)
    {
        return;
    }
    //index为整理完毕后,left和right的重合位置
    int index = partition(array, left, right);
    //递归其左右区间
    quickHelper(array, left, index - 1);
    quickHelper(array, index + 1, right);
}

/**
 * 快排非递归方法
 * @param array
 */
public static void quickHelperByLoop(int[] array)
{
    //借助栈,模拟实现递归过程
    Stack<Integer> stack = new Stack<>();
    //先将右侧位置压入栈
    stack.push(array.length - 1);//初始右起点
    //再将左侧位置压入栈
    stack.push(0);//初始左起点
    while (!stack.isEmpty())
    {
        int left = stack.pop();
        int right = stack.pop();
        //如果左边大于等于右边,当前区间已经排序完成(0个或者1个元素)
        if (left >= right)
        {
            continue;
        }
        //划分区间
        int partition = partition(array, left, right);
        //右侧区间
        stack.push(right);
        stack.push(partition + 1);
        //左侧区间
        stack.push(partition - 1);
        stack.push(left);
    }
}

/**
 * 找到分界点位置
 * @param array
 * @param left
 * @param right
 * @return
 */
private static int partition(int[] array, int left, int right)
{
    int base = array[right];
    int i = left;
    int j = right;
    while (i < j)
    {
        //从左往右找到比基准值大的元素
        while (i < j && array[i] <= base)
        {
            i++;
        }
        System.out.println("i = " + i);
        //从右往左找到比基准值小的元素
        while (i < j && array[j] >= base)
        {
            j--;
        }
        System.out.println("j = " + j);
        //找到后交换位置
        swap(array, i, j);
    }
    System.out.println();
    //将分界元素与基准元素交换位置
    swap(array, right, i);
    return i;
}

/**
 * 交换位置
 * @param array
 * @param i
 * @param j
 */
private static void swap(int[] array, int i, int j)
{
    int temp = array[i];
    array[i] = array[j];
    array[j] = temp;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值