选择排序同冒泡排序一样,平均时间复杂度为O(n2)。其关键就是每遍历一次序列,就找出一个最小值(最大值)排在序列的某一端,这样做 n 次遍历后,序列就有序了。
思路
实现思路其实很简单,就是先做一次完整遍历,找出最小值与第一位交换。
然后开始第二次遍历(此时应从第二位开始,因为第一位是刚才遍历出的最小值),找出这趟的最小值与第二位交换。
顺着这个思路,做完 n 趟,所得的序列就有序了。
优化
简单选择排序不管已知序列是否部分有序,它都要做完整的遍历,这点不像冒泡,所以为了改进,就得另想思路。
锦标赛排序 就是通过两两比较,最后角逐出最大值或者最小值,来替换原来寻找最小值或最大值的过程。优势在哪呢?在于后一遍的寻值可以依赖于前一遍。
用到的数据结构是二叉树,使得平均时间复杂度可以下降到 O(nlogn) 级别。
实现思路
一开始我并没有理解核心所在,仅是为了找到最小值而找,利用了递归来实现一趟竞标赛的过程,接着还要找到最小值的下标,对原序列进行元素交换,具体实现可见代码中的 tournamentSort(int[] src) 方法。
后来还是利用二叉树的思路,对原序列构建一棵完全二叉树,使得原序列的元素都为叶子节点。(这里的转换是,首先求树高,再用树高算总节点数,然后用数组表示这棵二叉树)
接下去就是做一轮锦标赛,然后将胜出的位置标记为无效,使得它不参与下一次锦标赛,然后循环 n 次,可以得到结果。具体实现可见代码中的 tournamentSortV2(int[] src)。不过,我后来想想还是不对,虽说锦标赛过程正确,但前后次的锦标赛显然没有关联,还是没有节省时间。
再后来,我参考资料继续理解 小灰的锦标赛排序 抓住核心就是后一次的比较,源于前一次的标记位置,所以在 V2 的基础上我又改了 V3,主要是对后一次寻找的逻辑调整。具体实现可见代码 tournamentSortV3(int[] src),我没有用链表形式的二叉树来实现,而是用数组进行对比判断,我觉得应该是达到要求的,至于链表形式的还要再理解理解。
修改于 2021/05/29
之前发现 V3 的代码实现和小灰的不同,于是抽时间参考小灰的代码实现,又重新理解了一遍锦标赛排序,并写了 V4 方法,关键点有以下几个,
- 先对数组建立一根完全二叉树,在建树的过程中需要知道,树的最大层级,总结点数的计算。
- 首次锦标赛需要全部进行对比排序。
- 每次取最小值后,只需按最小值所在路径进行排序调整。
- 因为是以数组形式来表示的二叉树,所以如何确定节点是左还是右,父与子的位置关系计算很重要。