一、选择排序的思想及优化思路(升序为例)
思想:假设给定一个大小为n的数组,从中选出最大的值(记录下标)与下标n - i的值进行交换(i∈[1, n - 1],i为循环次数),即遍历一次交换一次,每次遍历确定一个最大值。
优化思路:使用一种快速查找最值的方法可降低选择排序的时间复杂度,例如使用堆这种数据结构,可在O(logn)的情况下找到最值
二、代码实现
普通代码,cmp()功能为array[maxIndex] - array[begin],swap()功能为交换两个下标所在值。
for (int end = array.length - 1; end > 0 ; end--) {
int maxIndex = 0;
for (int begin = 1; begin <=end ; begin++) {
//=则为稳定排序
if (cmp(maxIndex, begin) <= 0){
maxIndex = begin;
}
}
swap(maxIndex, end);
}
优化思路代码
int heapSize;
@Override
protected void sort() {
heapSize = array.length;
//原地建堆
for (int i = (heapSize >> 1) - 1; i >= 0; i--) {
siftDown(i);
}
while (heapSize > 1) {
swap(0, --heapSize);
siftDown(0);
}
}
private void siftDown(int index) {
E parent = array[index];
//第一个叶子节点下标即非叶子节点个数
int half = heapSize >> 1;
while (index < half) {
//默认左边
int childIndex = (index << 1) + 1;
E child = array[childIndex];
int rightIndex = childIndex + 1;
//right
if (rightIndex < heapSize && cmpOfValue((Integer) array[childIndex], (Integer) array[rightIndex]) < 0) {
childIndex = rightIndex;
child = array[rightIndex];
}
//拿到字节点最大值放入child后与父节点比较,若子节点大则覆盖父节点,且将下标移动至原最大子节点处
if (cmpOfValue((Integer) parent, (Integer) child) < 0) {
array[index] = child;
index = childIndex;
} else break;
}
array[index] = parent;
}
三、复杂度分析
普通代码循环执行次数(n-1)*(n-1)即O(n²),一次确定一个最大值,不存在提前退出的情况,最坏与最好的情况一致。
优化代码后使用堆这种数据结构,
步骤①原地建堆,复杂度O(logn)
②进行遍历(复杂度O(n)),将堆顶元素(最大值),与末尾元素交换,进行下滤(保证堆性质,复杂度O(logn))同时减少堆可访问容量,
故复杂度为O(nlogn)