【058期】数据结构与算法(三):选择排序
开篇介绍
大家好,我是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)
稳定性:
不稳定
直接选择排序的过程如图所示,图中方括号表示无序区。
代码实现:
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)
稳定性:
不稳定
小顶堆:每个节点的值都小于或者等于它的左右子节点的值。
大顶堆:每个节点的值都大于或者等于它的左右子节点的值。
建堆过程:
大根堆排序过程:
代码实现:
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!
等风也等你
文章都看完了不点个 吗