飞道的博客

漫画:什么是 “锦标赛排序” ?

231人阅读  评论(0)


—————  第二天  —————


————————————

如图中所示,我们把原本的冠军选手5排除掉,在四分之一决赛和他同一组的选手6就自然获得了直接晋级。

接下来的半决赛,选手7打败选手6晋级;在总决赛,选手7打败选手3晋级,成为了新的冠军。

因此我们可以判断出,选手7是总体上的亚军。

假如给定如下数组,要求从小到大进行升序排列:

第一步,我们根据数组建立一颗满二叉树,用于进行“锦标赛式”的多层次比较。数组元素位于二叉树的叶子结点,元素数量不足时,用空结点补齐。

第二步,像锦标赛那样,让相邻结点进行两两比较,把数值较小的结点“晋升“到父结点。

如此一来,树的根结点一定是值最小的结点,把它复制到原数组的最左侧:

第三步,删除原本的最小结点,也就是值为1的结点。然后针对该结点所在路径,进行重新比较和刷新。

如此一来,树的根结点换成了第二小的结点,把它复制到原数组的下一个位置:

第四步,删除原本第二小的结点,也就是值为2的结点。然后针对该结点所在路径,进行重新比较和刷新。

如此一来,树的根结点换成了第三小的结点,把它复制到原数组的下一个位置:

像这样不断删除剩余的最小结点,局部刷新二叉树,最终完成了数组的升序排列:


   
  1. public class TournamentSort {
  2.     public static void tournamentSort( int[] array) {
  3.         Node[] tree = buildTree(array);
  4.          for( int i= 0; i<array.length; i++){
  5.             array[i] = tree[ 0].data;
  6.              if(i<array.length -1) {
  7.                  //当前最小元素所对应的叶子结点置空
  8.                 tree[tree[ 0].index] = null;
  9.                  //重新选举最小元素
  10.                 updateTree(tree[ 0].index, tree);
  11.             }
  12.         }
  13.     }
  14.      //排序前为数组构建二叉树,并选举最小值到树的根结点
  15.     public static Node[] buildTree( int[] array) {
  16.          //计算叶子层的结点数
  17.          int leafSize = nearestPowerOfTwo(array.length);
  18.          //计算二叉树的总结点数
  19.          int treeSize = leafSize *  2 -  1;
  20.         Node[] tree =  new Node[treeSize];
  21.          //填充叶子结点
  22.          for( int i= 0; i<array.length; i++){
  23.             tree[i+leafSize -1] =  new Node(i+leafSize -1, array[i]);
  24.         }
  25.          //自下而上填充非叶子结点
  26.          int levelSize = leafSize;
  27.          int lastIndex = treeSize -1;
  28.         while(levelSize >  1){
  29.              for( int i= 0; i<levelSize; i+= 2){
  30.                 Node right = tree[lastIndex-i];
  31.                 Node left = tree[lastIndex-i -1];
  32.                 Node parent = left;
  33.                  if(left != null && right != null) {
  34.                     parent = left.data<right.data?left:right;
  35.                 } else  if (left == null){
  36.                     parent = right;
  37.                 }
  38.                  if(parent != null){
  39.                      int parentIndex = (lastIndex-i -1)/ 2;
  40.                     tree[parentIndex] =  new Node(parent.index, parent.data);
  41.                 }
  42.             }
  43.             lastIndex -= levelSize;
  44.             levelSize = levelSize/ 2;
  45.         }
  46.          return tree;
  47.     }
  48.      //重新选举最小元素
  49.     public static void updateTree( int index, Node[] tree){
  50.         while(index !=  0){
  51.             Node node = tree[index];
  52.             Node sibling = null;
  53.              if((index& 1) ==  1){
  54.                  //index为奇数,该结点是左孩子
  55.                 sibling = tree[index+ 1];
  56.             } else {
  57.                  //index为偶数,该结点是右孩子
  58.                 sibling = tree[index -1];
  59.             }
  60.             Node parent = node;
  61.              int parentIndex = (index -1)/ 2;
  62.              if(node != null && sibling != null) {
  63.                 parent = node.data<sibling.data?node:sibling;
  64.             } else  if (node == null){
  65.                 parent = sibling;
  66.             }
  67.             tree[parentIndex] = parent==null ? null :  new Node(parent.index, parent.data);
  68.             index = parentIndex;
  69.         }
  70.     }
  71.      //获得仅大于number的完全平方数
  72.     public static  int nearestPowerOfTwo( int number) {
  73.          int square =  1;
  74.         while(square < number){
  75.             square = square<< 1;
  76.         }
  77.          return square;
  78.     }
  79.      //结点类
  80.     private static class Node {
  81.          int data;
  82.          int index;
  83.         Node( int index,  int data){
  84.             this.index = index;
  85.             this.data = data;
  86.         }
  87.     }
  88.     public static void main(String[] args) {
  89.          int[] array = { 9, 3, 7, 1, 5, 2, 8, 10, 11, 19, 4};
  90.         tournamentSort(array);
  91.         System.out. println(Arrays.toString(array));
  92.     }
  93. }

在这段代码中,二叉树的存储方式并非传统的链式存储,而是采用数组进行存储。因此,该二叉树的每一个父结点下标,都可以由(孩子下标-1)/2 来获得。

—————END—————

喜欢本文的朋友,欢迎关注公众号 程序员小灰,收看更多精彩内容

点个[在看],是对小灰最大的支持!

转载:https://blog.csdn.net/bjweimengshu/article/details/114528400
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场