Java排序--快速排序

复习:

一次使得一个数据有序
插入排序:在无序区间选择第一个数,在有序区间遍历,查找适合的位置插入
选择排序:遍历无序区间,查找最大数的下标
冒泡排序:遍历无序区间,通过两两比较,将最大的数挤到最后

堆排序:
      总览:大的选择排序(减治排序,每次选择最大的树放在最后)
            利用堆的特性选择最大的数

快速排序(重点):

总览过程:

1.在待排序区间中选择一个基准值(pivot)
2.Partition:遍历整个待排序期间,将比基准值小的(可能包含==)放在左边,
                              将比基准值大的(可能包含==)放在右边。
3.分治思想
   对左右的小区间分别用同样的方式处理
   **直到:区间长度==1(有序)或者 长度==0(没有数据)

实现:

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

// 待排序区间[left, right]
private static void quickSortInter(int[] a, int left, int right) {
    if (left >= right) {
        // 直到 长度 <= 1
        return;
    }
    // 1. 选择基准值 array[left]
    // 2. 做 partition
    int pivotIndex = partition(a, left, right);
    // 左边小区间 [left, pivotIndex - 1]
    // 右边小区间 [pivotIndex + 1, right]
    // 3. 分别对左右小区间按同样方式处理
    quickSortInter(a, left, pivotIndex - 1);
    quickSortInter(a, pivotIndex + 1, right);
}

如何做 partition:

Hoare法:
过程:
int i=left;
int j=right;
int pivot=a[left]; //基准值

基准值在左,右边先走,即 j++;
当 a[j]<pivot 时,i++;
在这里插入图片描述
当 a[i]>pivot 时
在这里插入图片描述
交换 a[i] 和 a[j]
在这里插入图片描述
当 j 和 i 相遇时,交换 基准值pivot 和 a[i];
此时,在基准值之前的,全部比 pivot 小;在其之后的,全部比 pivot 大;
在这里插入图片描述

private static int partition1(int[] a, int left, int right) {
    int begin = left;
    int end = right;
    int pivot = a[left];
    
    // [left, begin)     <= pivot
    // (end, right]      >= pivot
    while (begin < end) {
        while (begin < end && a[end] >= pivot) {
            end--;
        }
        while (begin < end && a[begin] <= pivot) {
            begin++;
        }
        swap(a, begin, end);
    }
    swap(a, left, begin);
    return begin;
}
private static void swap(int[] a, int i, int j) {
    int t = a[i];
    a[i] = a[j];
    a[j] = t;
}

挖坑法:
基本思路和Hoare 法一致,只是不再进行交换,而是进行赋值(填坑+挖坑)
int begin = left;
int end = right;
int pivot = a[left];
在这里插入图片描述
先将 pivot 复制到一边, pivot 位置上值为空;
基准值在左,右边先走;
在这里插入图片描述
当 a[end]<pivot 时,将 a[end] 放到 pivot 的位置上;
在这里插入图片描述
begin++;
在这里插入图片描述
当 a[begin]>pivot 时,将 a[begin] 放到空的位置上;
在这里插入图片描述
end++;
在这里插入图片描述
在这里插入图片描述
当 begin 和 end 相遇时,将基准值放到这个空位上。
在这里插入图片描述

private static int partition2(int[] a, int left, int right) {
    int begin = left;
    int end = right;
    int pivot = a[left];
    while (begin < end) {
        while (begin < end && a[end] >= pivot) {
            end--;
        }
        a[begin] = a[end];

        while (begin < end && a[begin] <= pivot) {
            begin++;
        }
        a[end] = a[begin];
    }
    a[begin] = pivot;
    return begin;
}

前后遍历法:
int pivot=a[left];
在这里插入图片描述
当 a[i]>pivot 时,i 往后走;
在这里插入图片描述
当 a[i]<pivot 时,a[i] 和 a[j] 交换;
在这里插入图片描述
i++,d++;
直到 i 走完整个数组

private static int partition3(int[] a, int left, int right) {
    // array[left]
    // [left + 1, right]
    int pivot = a[left];
    int d = left + 1;
    for (int i = left + 1; i <= right; i++) {
        if (a[i] < pivot) {
            swap(a, i, d++);
        }
    }

    swap(a, d - 1, left);
    return d - 1;
}
partition 不是做快排,只是快排的一个步骤
partition 一定要所有的数据都和基准值做过比较
   基准值在左,先走右边

partition 的时间复杂度和空间复杂度:
时间复杂度:O(n)
空间复杂度:O(1)

快速排序的时间复杂度和空间复杂度:

时间复杂度:partition 的时间复杂度 * 二叉树的高度
空间复杂度:partition 的空间复杂度 * 二叉树的高度
二叉树的高度:O(log(n))~O(n)

在这里插入图片描述
稳定性:不稳定

退化成单支树和两个因素有关:
1.数组的原顺序
2.基准值的选择

修改选择基准值的办法:
1.选择边上(左或者右)--只有有序/逆序,就是单支树
2.随机选择--不能消除单支树,但减少概率
3.几数取中(三数取中)--消除单支树
array[left], array[mid], array[right] 大小是中间的为基准值

非递归分治(了解):

public static void quickSort(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();
        if (left >= right) {
            continue;
        }
        int pivotIndex = partition(array, left, right);
        stack.push(right);
        stack.push(pivotIndex + 1);
        stack.push(pivotIndex - 1);
        stack.push(left);
    }
}

总结:


1. 在待排序区间选择一个基准值 
      选择左边或者右边 
      随机选取
      几数取中法
2. 做 partition,使得小的数在左,大的数在右
      1. hoare
      2. 挖坑
      3. 前后遍历
      4. 将基准值相等的也选择出来(了解)
3. 分治处理左右两个小区间,直到小区间数目小于一个阈值,使用插入排序

完!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值