数据结构与算法(java版)第二季 - 4 快速、希尔排序(未完)

28 篇文章 1 订阅
18 篇文章 2 订阅

目录

快速排序(Quick Sort)

希尔排序(Shell Sort)


快速排序(Quick Sort)

1960年由 查尔斯·安东尼·理查德·霍尔(Charles Antony Richard Hoare,缩写为C. A. R. Hoare) 提出
昵称为 东尼·霍尔(Tony Hoare)

快速排序 – 执行流程

①从序列中选择一个轴点元素(pivot)
✓ 假设每次选择0位置的元素为轴点元素

② 利用 pivot 将序列分割成 2 个子序列
将小于 pivot 的元素放在pivot前面(左侧)
将大于 pivot 的元素放在pivot后面(右侧)
等于pivot的元素放哪边都可以
③ 对子序列进行 ① ② 操作
✓ 直到不能再分割( 子序列中只剩下1个元素
这里快速排序的本质是逐渐将每一个元素转换为轴点元素坐标。就是进行一个逐渐递归的过程,每个元素成为轴点的过程都是差不多的。一般的begin和end是左闭右开的。

上述的扫描过程:

①首先将6a进行一个备份;
②首先将end-1,扫描到7,其大于6,将end--;
③发现end的这个位置的数字是比原来的6a小的,将5覆盖掉原来的begin位置,在这个时候将begin进行一个++操作;
依次进行上述的操作,注意:这里我们将两个相同的元素之中,将一样的元素看成是比原来的元素小的,为什么这样做呢?后面会说到的。

从左往右进行扫描,如果要是扫描到比轴点坐标小于等于的,则不发生改变,要是扫描到比轴点大,那么就是会发生一个位置的调换。同理,从右往左进行相应的扫描。

代码如下所示:

package com.mj.sort.cmp;

import com.mj.sort.Sort;

public class QuickSort1<T extends Comparable<T>> extends Sort<T> {
    @Override
    protected void sort() {
        sort(0, array.length);
    }

    /*
    对于[begin,end)之中的元素进行相应的
     */
    private void sort(int begin, int end) {
        if (end - begin < 2) return;

        // 确定轴点位置 O(n)
        int mid = pivotIndex(begin, end);

        // 对子序列进行快速排序
        sort(begin, mid);
        sort(mid + 1, end);
    }

    /**
     * 构造出 [begin, end) 范围的轴点元素
     *
     * @return 轴点元素的最终位置
     */
    private int pivotIndex(int begin, int end) {
        // 随机选择一个元素跟begin位置进行交换
        swap(begin, begin + (int) (Math.random() * (end - begin)));

        // 备份begin位置的元素---这里就是在图示之中的6a
        T pivot = array[begin];

        // end指向最后一个元素---将最后的一个位置进行--操作
        end--;

        //下面是两种操作,分情况进行讨论
        while (begin < end) {
            while (begin < end) {
                if (cmp(pivot, array[end]) < 0) { // 右边元素 > 轴点元素
                    end--;
                } else { // 右边元素 <= 轴点元素 右边的元素是小于或者等于轴点的大小的时候,将右边的值之间进行覆盖掉左边的begin位置的值
                    array[begin++] = array[end];
                    break;
                }
            }
            //下面是进行掉头的一段代码,使用的是进行掉头的方式.
            while (begin < end) {
                if (cmp(pivot, array[begin]) > 0) { // 左边元素 < 轴点元素
                    begin++;
                } else { // 左边元素 >= 轴点元素
                    array[end--] = array[begin];
                    break;
                }
            }
        }

        //使用备份的轴点元素放入到最终的位置之中
        array[begin] = pivot;
        //返回轴点元素
        return begin;
    }
}

这里注意上面代码之中的等于的情况:

进行掉头换方向,为什么要这么做呢?

最坏情况的出现,使得相应的使用了五次的轴点过程,是非常麻烦的.

我们在使用这种方式之后是仍然能够做到相应的平均分割的过程实现,为了避免出现下面的最坏的情况出现,什么是所谓的最坏情况,下面是进行了相应的解释. 

 

在轴点左右元素数量比较均匀的情况下,同时也是最好的情况
T n = 2 ∗ T (n/2) + O (n) = O(nlogn)[这里是和归并排序差不多的]

 为什么这里的复杂度是相应的nlog(n)见下面的递推式与复杂度关系

如果轴点左右元素数量极度不均匀,最坏情况

  T (n) = T (n − 1) + O (n) = O(n^2)极其不均匀的情况出现

 为了避免上面的情况的出现,使用随机选择元素的方式作为轴点元素,只是需要将最初的位置之中的代码进行相应的交换过程.

这里的Math.random()是不包含1的----[0,1) 

上面的不均匀过程极端等情况的出现可能会导致在这里栈溢出的问题,如果要是尽量让他在栈溢出的范围以内.

希尔排序(Shell Sort)

1959年由 唐纳德·希尔(Donald Shell)[没有找到这个人的图片] 提出
希尔排序把序列看作是一个 矩阵 ,分成 𝑚 列,逐列进行排序
从某个整数逐渐减为1 ---  当 𝑚 为1时,整个序列将完全有序
因此,希尔排序也被称为 递减增量排序 (Diminishing Increment Sort)-------迷迷糊糊的感觉,后面会进行解释这里的含义
矩阵的列数取决于步长序列(step sequence)
比如,如果步长序列为{1,5,19,41,109,...},就代表依次分成109列、41列、19列、5列、1列进行排序
不同的步长序列,执行效率也不同

希尔排序的例子[看了很长时间,没看懂,后面补上]

栗子一:

希尔本人给出的步长序列是 𝑛/(2^ 𝑘) ,比如 𝑛 为16时,步长序列是{1, 2, 4, 8}

 ①将上面的序列分成八列进行排序

 这个地方没有懂,但是知道这个过程就是可以的,后面依次类推

②分成四列进行排序

 ③分成两列进行排序

④分成一列进行排序

问:为什么一开始直接分成一列不就好了吗?

①直接分成一列是没有意义的,那干脆就不要分了[老师原话].
②上面的过程之中,从八列变成一列的过程之中,逆序对是越来越少的.[前面是有意义的]
③并且希尔排序底层一般使用插入排序对每一列进行排序,也很多资料认为希尔排序是插入排序的改进版

栗子二:

假设是存在相应的11个元素的,步长序列是{1,2,5}

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值