1. 快速排序:快速排序的基本思想:通过一趟排序将待排记录分隔成独立的两部分,其中一部分记录的关键字均比另一部分的关键字小,比另一部分的关键字大,则可分别对这两部分记录继续进行排序,以达到整个序列有序。
2. 单路快速排序的时间,空间复杂度及稳定性:
1>. 时间复杂度:O(nlogn);因为我们这里单路快速排序的过程中向下递归分割数组的过程时间复杂度为:O(logn);我们在划分数组部分的时候还要遍历数组,时间复杂度为:O(n);因此单路快速排序的时间复杂度为:O(nlogn)。
2>. 空间复杂度:S(1);因为我们在单路快速排序时是对原数组进行直接排序,并没有其他创建新的数组。
3>. 稳定性: 稳定;我们在单路快速排序时,并不会出现大跨度的交换元素。
3. 我们可以创建一个父类:父类中封装数组的创建,排序,交换位置等方法,其他的排序方法的类都继承于这个父类,重写父类中的方法。
public abstract class Sort {
public int[] arr;
public Sort(){};
//有参构造方法 创建一个数组的副本
public Sort(int[] arr){
this.arr = new int[arr.length];
for(int i = 0; i < arr.length; i++){
this.arr[i] = arr[i];
}
}
//对数组进行排序
public abstract void sort(int[] arr);
//交换数据位置
public void swap(int[] arr, int i, int j){
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
4. 单路快速排序的过程:
//快速排序---单路快速排序:时间复杂度:O(nlogn) 空间复杂度:S(1) 算法稳定性:不稳定
/*
单路快排的思想:我们先选出一个元素可以是开始位置的元素v
然后将数组向下划分 将小于v的元素全部放在v元素在数组中位置左边 将大于v的元素全部放在v元素在数组中位置右边
分完之后再选出从两边的数组中分别选出一个元素也可以是子数组开始位置的元素v
然后将数组继续向下划分 将左边子数组中小于v的元素全部放在v元素在数组中位置左边 将左边子数组中大于v的元素全部放在v元素在数组中位置右边
将右边子数组中小于v的元素全部放在v元素在数组中位置左边 将右边子数组中大于v的元素全部放在v元素在数组中位置右边
依次向下递归 直至v左右子数组的长度都为1 最后数组长度为1的元素排起来就是有序的(默认以升序排列数组)
*/
public class QuickSort01 extends Sort{
public QuickSort01(int[] arr){
super(arr);
}
@Override
public void sort(int[] arr) {
quickSort(arr, 0, arr.length - 1);
System.out.println(Arrays.toString(arr)); //打印排序后的数组
}
//单路快速排序
private void quickSort(int[] arr, int L, int R) {
if(L >= R){ //判断递归临界条件
return;
}
//对数组向下划分 返回那个选出的元素v的位置
int p = partition(arr, L, R);
quickSort(arr, L, p - 1); //向下递归 从原数组左端 到v元素位置减1的位置(表示小于v元素的全部元素的下标范围)
quickSort(arr, p + 1, R); //向下递归 从原数组v元素位置加1 到原数组左端(表示大于v元素的全部元素的下标范围)
}
/*
i表示向后遍历的元素的位置 也表示 大于v的的数据范围的右边界 但是不包括i
小于v的范围:[L + 1, j]
大于v的范围:[j + 1, i)
[L + 1, j] < v < [j + 1, i)
*/
//对数组进行划分 使选出的v元素右边部分的元素全部小于v v元素左边部分的元素全部大于v
private int partition(int[] arr, int L, int R) {
// int v = arr[L];
/*优化一下 随机让后面的数字和第一个数字换一下
尽量避免极端情况(一直递增的数组 如果是一直递增的数组 时间复杂度就为O(n^2))
*/
swap(arr, L, (int) (Math.random() * (R - L + 1) + L));
int v = arr[L];
int j = L; //j从数组左端开始 表示小于v元素的部分的右端边界
//i从j+1开始 向后遍历 i不仅表示向后遍历的元素的位置 也表示大于v的的数据范围的右边界 但不包含
for(int i = L + 1; i <= R; i++){
/*
如果i位置的元素小于v 就将大于v元素的部分的第一个元素位置(也就是j+1) 与i位置的元素交换位置
就相当于是将小于v的元素拼到小于v的部分的最后 将大于v的部分的第一个元素放在i位置上(相当于拼接在大于v的部分的最后)
如果i位置的元素大于v 就将i指针后移即可 相当于将大于v的部分的范围向后扩大1
*/
if(arr[i] < v){
swap(arr, j + 1, i);
j++;
}
}
/*最后将选出的元素与小于v的部分的最后一个元素交换位置 将v元素交换到小于v的部分和大于v的部分之间
实现v元素右边部分的元素全部小于v v元素左边部分的元素全部大于v
最后j位置就是v元素的位置 返回即可 再向下递归数组
*/
swap(arr, L, j);
return j;
}
}
5. 测试类:测试类中只测试排序方法是否能成功。
public class TestSort {
public static void main(String[] args) {
int[] arr = {9, 5, 4, 6, 7, 2, 1, 3, 8, 10};
QuickSort01 quickSort01 = new QuickSort01(arr);
quickSort01.sort(arr);
}
}