你知道和你不知道的选择排序

1. 什么是选择排序?

首先贴上从wiki上弄下来的关于选择排序的定义。java

选择排序(Selection sort)是一种简单直观的排序算法。它的工做原理以下。首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,而后,再从剩余未排序元素中继续寻找最小(大)元素,而后放到已排序序列的末尾。以此类推,直到全部元素均排序完毕。web

更加直白的解释是,每次都从数组中选出最大或者最小的元素,而后放到数组的左边。算法

2. 选择排序的过程展现

老规矩,咱们仍是经过动图来看一下选择排序的过程。如下的gif来自于wiki。数组

而后咱们再经过我制做的gif,配上数据再了解一下过程。假设咱们的待排序数组仍是[5, 1, 3, 7, 6, 2, 4]。服务器

3. 选择最小值的算法

咱们使用Java来实现最多见的,选择最小值的选择排序,其代码以下。微信

private void selectionSort(int[] arr) {
  int min;
  int minIndex;
  for (int i = 0; i < arr.length - 1; i++) {
    min = arr[i];
    minIndex = -1;
    for (int j = i; j < arr.length; j++) {
      if (arr[j] < min) {
        min = arr[j];
        minIndex = j;
      }
    }
    // 排序结束 交换位置
    if (minIndex != -1) {
      exchange(arr, i, minIndex);
    }
  }
}

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

int[] arr = new int[]{5, 1, 3, 7, 6, 2, 4};
selectionSort(arr);
System.out.println(Arrays.toString(arr)); // [1, 2, 3, 4, 5, 6, 7]
复制代码

假设数组的长度为7,那么算法就须要进行6轮。若是数组的长度为n,则算法须要进行n - 1轮。app

每一轮,算法都会从剩下的待排序元素中,选出最小的元素,并将其与当前数组下标为i也就是有序序列的起始位置的元素交换。这样一来,通过反复的排序,最终造成有序数组。frontend

4. 选择最大值的算法

上面实现了选择最小值的代码,接下来咱们继续实现选择最大值的代码。微服务

private void selectionSort(int[] arr) {
  int max;
  int maxIndex;

  for (int i = 0; i < arr.length - 1; i++) {
    max = Integer.MIN_VALUE;
    maxIndex = -1;
    for (int j = 0; j < arr.length - i; j++) {
      if (max < arr[j]) {
        max = arr[j];
        maxIndex = j;
      }
    }

    // 排序结束 交换位置
    if (maxIndex != -1) {
      exchange(arr, maxIndex, arr.length - i - 1);
    }
  }
}

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

int[] arr = new int[]{5, 1, 3, 7, 6, 2, 4};
selectionSort(arr);
System.out.println(Arrays.toString(arr)); // [1, 2, 3, 4, 5, 6, 7]
复制代码

这个思想与选择最小值的算法彻底同样,只不过是选择了最大值,每次都将剩余序列的最大值放到数组的有序序列的最左边。工具

那么到此,选择排序最多见的两种写法咱们都已经实现了。有的兄弟可能会想,这篇博客是否是结束了。其实咱们能够从上面两个算法中想到能够优化的点。

既然咱们有两个选择,一种选择最小值,另一种选择最大值。那么咱们为何不一样时进行两个操做呢?

下面咱们就来实现这种算法。

5. 同时选择最大值和最小值

private void selectionSort(int[] arr) {
  int min;
  int max;
  int minIndex;
  int maxIndex;

  for (int i = 0; i <= arr.length / 2; i++) {
    min = Integer.MAX_VALUE;
    max = Integer.MIN_VALUE;
    minIndex = -1;
    maxIndex = -1;
    for (int j = i; j < arr.length - i; j++) {
      if (arr[j] < min) {
        min = arr[j];
        minIndex = j;
      }
      if (arr[j] > max) {
        max = arr[j];
        maxIndex = j;
      }
    }
    // 排序结束 交换位置
    if (minIndex != -1) {
      if (maxIndex == i) {
        maxIndex = minIndex;
      }
      exchange(arr, i, minIndex);
    }

    if (maxIndex != -1) {
      exchange(arr, maxIndex, arr.length - 1 - i);
    }
  }
}

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

int[] arr = new int[]{5, 1, 3, 7, 6, 2, 4};
selectionSort(arr);
System.out.println(Arrays.toString(arr)); // [1, 2, 3, 4, 5, 6, 7]
复制代码

由于选择最大值和最小值同时进行,相对于上面两种算法,同时选择算法在执行次数上比前两种算法减小了50%。

在运行时间上相对于选择最小值和最大值分别减小了39.22%和62.20%。

6. 总结

如下是对同一个长度为10000的随机乱序数组使用三种算法的状况。

[0 - 10000] 的乱序数组 取最小值 取最大值 同时取最大值最小值
100次平均执行时间(ms) 51 82 31
执行次数(次) 50004999 50004999 25005000

最后咱们看一下选择排序算法的时间复杂度。

  • 最好的状况为O(n ^ 2). 即便整个数组都是有序的,选择排序也会执行完选择最大值或者最小值的过程,只是不会去进行元素交换。
  • 最坏的状况为O(n ^ 2). 同上,会执行完选择最大值或者最小值的过程,而且每次都须要进行元素交换。

其空间复杂度为O(n),上面三种算法都属于原地排序算法,除了交换元素使用了一个辅助空间以外,没有额外申请空间,同时选择排序是不稳定排序。

往期文章:

相关:

  • 微信公众号: SH的全栈笔记(或直接在添加公众号界面搜索微信号LunhaoHu)