vlambda博客
学习文章列表

【058期】数据结构与算法(三):选择排序

【058期】数据结构与算法(三):选择排序

【058期】数据结构与算法(三):选择排序

2020年 百日百更原创 Java最全面试题库之往期回顾







开篇介绍

大家好,我是Java最全面试题库提裤姐今天这篇是数据结构与算法那的第三篇,主要总结了选择排序相关的问题;在后续,会沿着第一篇开篇的知识线路一直总结下去,做到日更!如果我能做到百日百更,希望你也可以跟着百日百刷,一百天养成一个好习惯。


Q:

选择排序

选择排序(Selection Sort)的基本思想是:
每一趟从待排序的记录中选出关键字最小的记录,顺序放在已排好序的子文件的最后,直到全部记录排序完毕;选择在排序分为两种:
直接选择排序(或称简单选择排序)和堆排序

Q:

直接选择排序

数组分成有序区和无序区,初始时整个数组都是无序区,然后每次从无序区选一个最小的元素直接放到有序区的最后,直到整个数组变有序区。
基本思想是:
第i趟排序开始时,当前有序区和无序区分别为
R[1..i-1]R[i..n](1≤i≤n-1),该趟排序则是从当前无序区中选出关键字最小的记录R[k],将它与无序区的第1个记录R[i]交换,使R[1..i]R[i+1..n]分别变为新的有序区和新的无序区。因为每趟排序均使有序区中增加了一个记录,且有序区中的记录关键字均不大于无序区中记录的关键字,即第i趟排序之后R[1..i].keys≤R[i+1..n].keys,所以进行n-1趟排序之后有R[1..n-1].keys≤R[n].key,也就是说,经过n-1趟排序之后,整个文件R[1..n]递增有序。

注意,第1趟排序开始时,无序区为R[1..n],有序区为空。

特性:

  • 时间复杂度:O(n^2)

  • 空间复杂度:O(1)

  • 稳定性:不稳定

直接选择排序的过程如图所示,图中方括号表示无序区。

【058期】数据结构与算法(三):选择排序
直接选择排序.png

代码实现:

public static void SelectSort(int[] intArray, int length) {
    int i, j, minIndex, temp;
    for (i = 0; i < length - 1; i++) {
        //默认取无序区第一个元素为最小值
        minIndex = i;

        //循环遍历无序区,查找到无序区最小元素
        for (j = i + 1; j < length; j++) {
            if (intArray[minIndex] > intArray[j])
                minIndex = j;
        }

        if (i != minIndex) {
            temp = intArray[i];
            intArray[i] = intArray[minIndex];
            intArray[minIndex] = temp;
        }
    }
}

Q:

堆排序

直接选择排序中,为了从R[1..n]中选出关键字最小的记录,必须进行n-1次比较,然后在R[2..n]中选出关键字最小的记录,又需要做n-2次比较。
事实上,后面这n-2次比较中,有许多比较可能在前面的n-1次比较中已经做过,但由于前一趟排序时未保留这些比较的结果,所以后一趟排序时又重复执行了这些比较操作。堆排序可以克服这一缺点。

堆排序( Heap Sort)是一树形选择排序,特点:在排序过程中,将R[1..n]看成是一棵完全二叉树的顺序存储结构,利用完全二叉树中双亲结点和孩子结点之间的内在关系,在当前无序区中选择关键字最大(或最小)的记录。

基本思想是:
1、将带排序的序列构造成一个大顶堆,根据大顶堆的性质,当前堆的根节点(堆顶)就是序列中最大的元素;
2、将堆顶元素和最后一个元素交换,然后将剩下的节点重新构造成一个大顶堆;
3、重复步骤2,如此反复,从第一次构建大顶堆开始,每一次构建,我们都能获得一个序列的最大值,然后把它放到大顶堆的尾部。最后,就得到一个有序的序列了

特性:

  • 时间复杂度:O(nlogn)

  • 空间复杂度:O(1)

  • 稳定性:不稳定

小顶堆:每个节点的值都小于或者等于它的左右子节点的值。

【058期】数据结构与算法(三):选择排序
小根堆.png

大顶堆:每个节点的值都大于或者等于它的左右子节点的值。

【058期】数据结构与算法(三):选择排序
大根堆.png

建堆过程:

【058期】数据结构与算法(三):选择排序
建堆过程.png

大根堆排序过程:

【058期】数据结构与算法(三):选择排序
大根堆排序.png

代码实现:

public class HeapSort {

    public static void heapSort(int[] arr) {
        if (arr == null || arr.length == 0) {
            return;
        }
        int len = arr.length;
        // 构建大顶堆,这里其实就是把待排序序列,变成一个大顶堆结构的数组
        buildMaxHeap(arr, len);

        // 交换堆顶和当前末尾的节点,重置大顶堆
        for (int i = len - 1; i > 0; i--) {
            swap(arr, 0, i);
            len--;
            heapify(arr, 0, len);
        }
    }

    private static void buildMaxHeap(int[] arr, int len) {
        // 从最后一个非叶节点开始向前遍历,调整节点性质,使之成为大顶堆
        for (int i = (int)Math.floor(len / 2) - 1; i >= 0; i--) {
            heapify(arr, i, len);
        }
    }

    private static void heapify(int[] arr, int i, int len) {
        // 先根据堆性质,找出它左右节点的索引
        int left = 2 * i + 1;
        int right = 2 * i + 2;
        // 默认当前节点(父节点)是最大值。
        int largestIndex = i;
        if (left < len && arr[left] > arr[largestIndex]) {
            // 如果有左节点,并且左节点的值更大,更新最大值的索引
            largestIndex = left;
        }
        if (right < len && arr[right] > arr[largestIndex]) {
            // 如果有右节点,并且右节点的值更大,更新最大值的索引
            largestIndex = right;
        }

        if (largestIndex != i) {
            // 如果最大值不是当前非叶子节点的值,那么就把当前节点和最大值的子节点值互换
            swap(arr, i, largestIndex);
            // 因为互换之后,子节点的值变了,如果该子节点也有自己的子节点,仍需要再次调整。
            heapify(arr, largestIndex, len);
        }
    }

    private static void swap (int[] arr, int i, int j) {
        int temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
    }
}





“一个专注于分享各类Java面试题的号主,长按二维码关注我吧 ”

祝大家都能拿到心仪的offer!






【058期】数据结构与算法(三):选择排序


【058期】数据结构与算法(三):选择排序
【058期】数据结构与算法(三):选择排序

等风也等你



文章都看完了不点个 吗