锦标赛排序(树形选择排序)

1.介绍

  树形选择排序(Tree Selection Sort),又称锦标赛排序(Tournament Sort),是一种按照锦标赛思想进行选择排序的不稳定排序。

2.实现原理

  如图所示,给定有8个元素的数组,对该数组进行从小到大的排序。

  第一步,如图所示,根据数组建立一颗满二叉树(胜者树),用于进行‘锦标赛事’的多层次比较。所有的数组元素如同打锦标赛一样全部位于二叉树的叶子节点上,因为是满二叉树,所以所有的数组元素都位于最底层,请读者自行思考。元素数量不足时,用空节点补齐。

  第二步,如图所示,像锦标赛一样,让相邻节点相互‘pk’,把数值较小的节点“晋升”到其父节点上,注意,这一排序目标是从小到大,因此是较小的移到父节点,反之,则是较大的移到父节点。

  如此一来,聪明的读者一定可以自己推出第一次打完锦标赛时树的样子了,如图所示,显而易见,树的根节点的值最小,就像锦标赛中冠军一定是本赛季最强的一个道理。

  选出最小值后,将该叶子节点删除(设为无穷大),接着继续打锦标赛(选手们不累吗),当然选手们并不用那么辛苦,我们不必对整棵树进行操作,因为第二小的值一定是在和第一小的值对峙时败下阵来的,因此第二小的数一定在第一小的晋升的路上,所以只需在这条路上再选出一个第一小的数就行了(图就不画了,太难受了)。

  重复上述步骤n轮你就成功的排好序了,Oh Yeah!

3.算法复杂度分析

  创建和填充二叉树仅需O(n),后继每一轮进行局部刷新二叉树需要O(logn),共进行n-1轮,因此总的时间复杂度为:O(nlogn);

  由于二叉树的结点数量和数组元素个数成正比,所以空间复杂度为O(n)。相对于选择排序,锦标赛排序有效地降低了时间复杂度,但使用的辅助存储空间较多,和∞的比较多余。

  实际编程时,除叶子结点外,并不需要在其它结点保存刷新的元素值,可以使用辅助数组idx[]保存刷新的元素值的原始下标位置即可。如图所示,数组idx[]初始化时,idx[8]=0表示结点8的值为a[0],idx[9]=1表示结点9的值为a[1]......

假设现在更新结点7的值,因为左子结点的编号为14(即7<<1),右子结点的编号为15(即(7<<1)+1),它们指向的元素值分别为2749,所以idx[7]的值应为6,即a[6]的值。

 

\完结撒花!!!/

 

等等……是不是漏了些什么……

  

 4.参考程序

 1 #include <bits/stdc++.h>
 2 #define INF 0x3f3f3f
 3 using namespace std;
 4 int a[1000100],idx[1000100];
 5 int siz = 1;
 6 void Adjust(int p,int Ls,int Rs){
 7     idx[p] = a[idx[Ls]]<a[idx[Rs]]?idx[Ls]:idx[Rs];
 8 }
 9 int main(){
10     int n;
11     cin>>n;
12     while (siz<n){
13         siz<<=1; //siz*=2;
14     }
15     for (int i=0;i<n;i++){
16         cin>>a[i];
17         idx[i+siz] = i; //辅助数组记录数组下标 
18     }
19     for (int i=n;i<siz;i++){
20         a[i] = INF; //空节点设为无穷大( 
21         idx[i+siz] = i;
22     }
23     for (int i = siz-1;i;i--)
24         Adjust(i,i<<1,(i<<1)+1); //节点,左儿子,右儿子下标 
25     for (int i=0;i<n;i++){
26         cout<<a[idx[1]]<<' '; //输出 
27         a[idx[1]] = INF;
28         for (int j=(idx[1]+siz)>>1;j;j>>=1){
29             Adjust(j,j<<1,(j<<1)+1);
30         }
31     }
32     return 0;
33 }

 

posted @ 2022-09-04 11:16  reasa  阅读(1565)  评论(1编辑  收藏  举报