算法-排序-02

快速排序

原理

从序列中选择一个轴点元素(pivot)
	假设每次选择 0 位置的元素为轴点元素
利用 pivot 将序列分割成 2 个子序列
	1. 将小于 pivot 的元素放在pivot前面(左侧)
	2. 将大于 pivot 的元素放在pivot后面(右侧)
	3. 等于pivot的元素放哪边都可以
对子序列进行上述操作, 直到不能再分割(子序列中只剩下1个元素)

# 极限情况 : 轴点构造后, 轴点两边极度不均衡, 例 : [0] [pivot] [length - 1]

轴点构造

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-T83TcknJ-1604243291801)(…/img/快速排序.PNG)]

代码

/**
 * 门面方法
 */
public static Integer[] quickSort(Integer[] array) {
    return quickSort(array, 0, array.length);
}
/**
 * 核心方法
 */
public static Integer[] quickSort(Integer[] array, int begin, int end) {
    // 一个元素直接返回
    if (end - begin < 2) {
        return array;
    }
    Integer[] sortArray = array;
    // 轴点构造, 并获取下次轴点构造的参数, mid 为轴点的现在位置下标
    int mid = pivotIndex(array, begin, end);
	// 左右部分快速排序
    quickSort(array, begin, mid);
    quickSort(array, mid + 1, end);

    return array;
}

/**
 * 对 array[begin, end) 进行轴点构造
 */
private static int pivotIndex(Integer[] array, int begin, int end) {
    // 防止出现极限情况
    Util.swap(begin,begin + (int) (Math.random() * (end - begin)),array);
    Integer value = array[begin];
    // 获取末尾元素下标
    end--;
    // 开始轴点构造, begin 和 end 重叠结束
    while (begin < end) {
        // array[end] 的交换
        while (begin < end) {
            // array[end] 值小, 需要交换, 防止极限情况
            if (array[end] <= value) {
                array[begin] = array[end];
                break;
            } else {
                end--;
            }
        }

        // array[begin] 的交换
        while (begin < end) {
            // array[begin] 值大, 需要交换, 防止极限情况
            if (array[begin] > value) {
                array[end] = array[begin];
                break;
            } else {
                begin++;
            }
        }
    }
	// value 重新进入数组
    array[begin] = value;
    return begin;
}

希尔排序

原理

# 希尔排序 / 递减增量排序 (Diminishing Increment Sort)
	把序列看作是一个矩阵,分成 𝑚 列,逐列进行排序, m 从某个整数逐渐减为 1 , 当 𝑚 为 1 时,整个序列将完全有序

# 底层排序
不难看出来, 逆序对的数量在逐渐减少
因此希尔排序底层一般使用插入排序对每一列进行排序,也很多资料认为希尔排序是插入排序的改进版

在这里插入图片描述

代码

/**
 * 门面
 */
public static Integer[] shellSort(Integer[] array) {
    Integer[] sortArray = array;
    ArrayList<Integer> steps = step(sortArray);
	// 循环步数
    for (Integer step : steps) {
        shellSort(sortArray, step);
    }
    return sortArray;
}
/**
 * 核心方法, 底层是插入排序, 但是修改插入步数
 */
private static Integer[] shellSort(Integer[] array, int step) {

    for (int i = step; i < array.length; i++) {
        int flag = i;
        // 获取要查入对象的值
        int value = array[flag];
        // 迭代, 凡是不符合要求的, 往后移动一位
        while (flag >= step && value < array[flag - step]) {
            array[flag] = array[flag - step];
            flag = flag - step;
        }
        // 正式插入
        array[flag] = value;
    }
    return array;
}
/**
 * 获取步数集合 steps : {length / 2, steps[i - 1] / 2, ......}, 上一个元素除二
 */
private static ArrayList<Integer> step(Integer[] array) {
    ArrayList<Integer> steps = new ArrayList<>();
    int step = array.length;

    while ((step = (step / 2)) >= 1) {
        steps.add(step);
    }

    return steps;
}

计数排序

原理

# 计数排序、桶排序、基数排序,都不是基于比较的排序
	它们是典型的用空间换时间,在某些时候,平均时间复杂度可以比 O(nlogn) 更低

# 原理 : 
	统计每个整数在序列中出现的次数,进而推导出每个整数在有序序列中的索引

在这里插入图片描述

代码

// 获取数组最大值最小值
private static Integer[] getMaxAndMin(Integer[] array) {
    Integer[] maxAndMin = {array[0], array[0]};

    for (int i = 1; i < array.length; i++) {
        if (array[i] > maxAndMin[0]) {
            maxAndMin[0] = array[i];
        }
        if (array[i] < maxAndMin[1]) {
            maxAndMin[1] = array[i];
        }
    }

    return maxAndMin;
}
/**
 * 计算 count 数组
 */
private static int[] count(Integer[] array) {
    Integer[] maxAndMin = getMaxAndMin(array);
    int[] count = new int[maxAndMin[0] - maxAndMin[1] + 1];
	// count 数组存放将要排序数组的各个元素个数
    for (int i = 0; i < array.length; i++) {
        count[array[i] - maxAndMin[1]]++;
    }
	// count 数组存放未来排好序数组的下标
    for (int i = 1; i < count.length; i++) {
        count[i] = count[i - 1] + count[i];
    }

    return count;
}
/**
 * 核心
 */
public static Integer[] countSort(Integer[] array) {
    Integer[] sortArray = new Integer[array.length];
    int[] count = count(array);
    Integer[] maxAndMin = getMaxAndMin(array);
	// 按照 count 数组将 array 数组放入 sortArray 数组
    for (int i = array.length - 1; i >= 0; i--) {
        sortArray[--count[array[i] - maxAndMin[1]]] = array[i];
    }

    return sortArray;
}

基数排序

原理

基数排序非常适合用于整数排序 (尤其是非负整数)
# 执行流程:
	依次对个位数、十位数、百位数、千位数、万位数...进行排序 (从低位到高位)

代码

public static Integer[] radixSort(Integer[] array) {
    Integer[] maxAndMin = getMaxAndMin(array);
	// 在最大值位数范围内进行计数排序
    for (int i = 1; i <= maxAndMin[0]; i = i * 10) {
        array = radixSort(array, i);
    }
    return array;
}

private static Integer[] radixSort(Integer[] array, int divider) {
    // 已知一定在 [0,9] 范围内
    Integer[] sortArray = new Integer[array.length];
    int[] count = new int[10];
	// 计数排序
    for (int i = 0; i < array.length; i++) {
        count[array[i] / divider % 10]++;
    }
    for (int i = 1; i < count.length; i++) {
        count[i] = count[i - 1] + count[i];
    }
    for (int i = array.length - 1; i >= 0; i--) {
        sortArray[--count[array[i] / divider % 10]] = array[i];
    }
    return sortArray;
}

桶排序

原理

# 执行流程
1. 创建一定数量的桶(比如用数组、链表作为桶)
2. 按照一定的规则(不同类型的数据,规则不同),将序列中的元素均匀分配到对应的桶
3. 分别对每个桶进行单独排序
4. 将所有非空桶的元素合并成有序序列


# 没有答案, 按照不同情况会有不同结果

代码

public static Integer[] bucketSort(Integer[] array) {
    Integer[] maxAndMin = getMaxAndMin(array);
    Integer[] sortArray = new Integer[array.length];

    List<Integer>[] buckets = new List[maxAndMin[0] + 1];
	// 将数组元素放入桶
    for (int i = 0; i < array.length; i++) {
        List<Integer> bucket = buckets[array[i]];
        if (bucket == null) {
            bucket = new LinkedList<Integer>();
            buckets[array[i]] = bucket;
        }
        bucket.add(array[i]);
    }
	// 桶排序, 并且将桶中元素放入数组中
    int flag = 0;
    for (int i = 0; i < buckets.length; i++) {
        if (buckets[i] == null) continue;
        // 桶内排序
        Integer[] sort = buckets[i].toArray(new Integer[buckets[i].size()]);
        sort = insertSort(sort);
        // 放入
        for (int j = 0; j < sort.length; j++) {
            sortArray[flag++] = sort[j];
        }
    }
    return sortArray;
}

nue;
// 桶内排序
Integer[] sort = buckets[i].toArray(new Integer[buckets[i].size()]);
sort = insertSort(sort);
// 放入
for (int j = 0; j < sort.length; j++) {
sortArray[flag++] = sort[j];
}
}
return sortArray;
}




















  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值