打赏

数据结构与算法系列——排序(6)_树形选择排序

1. 工作原理(定义)

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

  首先对n个记录的关键字进行两两比较,然后在其中[n/2](向上取整)个较小者之间再进行两两比较,如此重复,直至选出最小关键字的记录为止。

  胜者树和败者树是完全二叉树,是树形选择排序的变形。

2. 算法步骤 

 这个过程可以用一棵有n个叶子结点的完全二叉树表示。如图中的二叉树表示从8个关键字中选出最小关键字的过程:

  

  8个叶子结点中依次存放排序之前的8个关键字,每个非终端结点中的关键字均等于其左、右孩子结点中较小的那个关键字,则根结点中的关键字为叶子结点中的最小关键字

  在输出最小关键字之后,根据关系的可传递性,欲选出次小关键字,仅需将叶子结点中的最小关键字(13)改为“最大值”,然后从该叶子结点开始,和其左右兄弟的关键字进行比较,修改从叶子结点到根结点的路径上各结点的关键字,则根结点的关键字即为次小值

 

  同理,可依次选出从小到大的所有关键字。

3. 图示

为了减少简单选择排序,我们利用前n-1次比较信息,减少下次选择。类似于锦标赛。根据锦标赛传递关系。亚军只能从被冠军击败的人中选出。 

实际算法中,我们把需要比较的记录全部作为叶子,然后从叶子开始两两比较,从底向上最后形成一棵完全二叉树。在我们选择出最小关键字后,根据关系的传递,只需要将最小关键字的叶子节点改成无穷大,重新从底到上比较一次就能够得出次小关键字。 

 


然而虽然树形选择比较能够减少比较次数,却增加了辅助空间的使用。为了弥补此缺憾,威廉姆斯于1964年提出了堆排序。

  具体的例子:

对 n 个关键字两两比较,直到选出最小关键字为止,一趟排序结束

        

反复这个过程,仅需将叶子结点的最小关键字改为最大值∞,即可

然后从该叶子结点开始,继续和其左右兄弟的关键字比较,找出最值

4. 性能分析

1. 时间复杂度

时间复杂度:由于含有 n 个叶子结点的完全二叉树的深度为,则在树形选择排序中,除了最小关键字外,每选择一个次小关键字仅需进行   次比较,故时间复杂度为 O(n logn)。

缺点:  1、与“∞”的比较多余;  2、辅助空间使用多。

为了弥补这些缺点,1964年,堆排序诞生。

2. 空间复杂度

  选择排序过程中,需要临时变量存储待排序元素,因此空间复杂度为O(n)。

3. 算法稳定性 

  选择排序是不稳定的算法,在选择数值和交换过程中它们的顺序可能会发生变化。

6. 具体代码

import java.util.Arrays;

public class TreeSelectionSort{
    public static void main(String[] args) {
        int[] arr = {8,7,6,5,4,3,2,1};
        System.out.println(Arrays.toString(treeSelectionSort(arr)));
    }

    public static int[] treeSelectionSort(int[] mData) {
        int n = mData.length;
        int MinValue = Integer.MIN_VALUE;
        // 得到树上节点的个数
        int baseSize = 1;
        while (baseSize < n) {
            baseSize *= 2;
        }
        int treeSize = baseSize * 2 - 1;// 树的大小
        // 初始化树,从下标1-treeSize,忽略下标0
        int[] tree = new int[treeSize+1]; 
        int i = 0;
        for (; i < n; i++) {
            tree[treeSize - i] = mData[i];
        }
        for (; i < baseSize; i++) {
            tree[treeSize - i] = MinValue;
        }
        // 构造一棵树
        for (i = treeSize; i > 1; i -= 2) {
            tree[i / 2] = (tree[i] > tree[i - 1] ? tree[i] : tree[i - 1]);
        }
        
        //每一趟得到一个最大值
        while (n > 0 ) {
            // 根节点是最大值
            int max = tree[1]; 
            // 倒着放入数组之后得到的正向数组就是 从小到大
            mData[--n] = max;  
            // 将叶子节点中最大值的位置赋值为最小值
            int maxIndex = treeSize;
            while (tree[maxIndex] != max) {
                maxIndex--;
            }
            tree[maxIndex] = MinValue;
            // 重新调整胜者树的值
            while (maxIndex > 1) {
                if (maxIndex % 2 == 0) {
                    tree[maxIndex / 2] = (tree[maxIndex] > tree[maxIndex + 1] ? tree[maxIndex] : tree[maxIndex + 1]);
                } else {
                    tree[maxIndex / 2] = (tree[maxIndex] > tree[maxIndex - 1] ? tree[maxIndex] : tree[maxIndex - 1]);
                }
                maxIndex /= 2;
            }
        }
        return mData;
    }
}

7. 参考网址

  1. 简单选择排序 Selection Sort 和树形选择排序 Tree Selection Sort
  2. 常见的五类排序算法图解和实现(选择类:简单选择排序,锦标赛排序,树形选择排序,堆排序)
  3. 选择排序之树形选择排序(TreeSelectionSort)
  4. https://baike.baidu.com/item/%E6%A0%91%E5%BD%A2%E9%80%89%E6%8B%A9%E6%8E%92%E5%BA%8F/5720280?fr=aladdin
posted @ 2019-05-23 16:36  海米傻傻  阅读(2268)  评论(0编辑  收藏  举报