[golang] 数据结构-树形选择排序(锦标赛排序)

NicoChen · · 1820 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

接上文 简单选择排序
简单选择排序很容易理解,代码也很容易实现。但毕竟比较次数太多。树形选择排序则对这个问题进行了改进。

原理
简单来说,树形选择排序(Tree selection sort)就是在选择完一轮找出最小值后,直接在与最小值比较中稍大的元素里筛选出最小的。这样避免了简单选择查询那种,抛弃了之前比较过的结果,每次都全部重新比较的情况。

流程举例

  • 先列出所有待排序的元素如:8、4、12、7、35、9、22,并用他们组成满二叉树的叶子元素,不足的位置以∞作为补充。
    将元素两两相比较,分别得到较小值:4,7,9,22。再次两两比较,得到4,9。最终比较一次得到最小值4。由此构建出一个完整的满二叉树:
    [golang] 数据结构-树形选择排序(锦标赛排序)

  • 完成一轮比较后,将胜出者4的叶子节点改成∞,然后由它的兄弟节点8继续参加下一轮比较。从这次开始,元素8仅需按构建好的树结构一步步向上与其他胜出的非父节点仅需比较即可,比如这里只需要在和7,9比较,就能得到最小元素是7
    [golang] 数据结构-树形选择排序(锦标赛排序)

  • 然后将元素7的叶子节点改成∞,其兄弟节点12与8、9节点比较,即可得到8:
    [golang] 数据结构-树形选择排序(锦标赛排序)

  • 以此类推,最终得到最后一个元素35:
    [golang] 数据结构-树形选择排序(锦标赛排序)

时间复杂度
由于每次仅需与胜出的其他节点仅需比较,所以时间复杂度相较简单选择排序的O(n^2)降低到O(nlogn)。
但是由于储存了每次各胜出节点的数据,所以需要更多的储存空间,而且其中n-1次的与∞的比较行为较为多余。

代码实现

package main

import (
    "fmt"
    "math"
)

// 声明一个节点的结构体,包含节点数值大小和是否需要参与比较
type node struct {
    // 数值大小
    value int
    // 叶子节点状态
    available bool
    // 叶子中的排序,方便失效
    rank int
}

func main() {
    var length = 10
    var mm = make(map[int]int, length)
    var o []int

    // 先准备一个顺序随机的数(qie)组(pian)
    for i := 0; i < length; i++ {
        mm[i] = i
    }
    for k, _ := range mm {
        o = append(o, k)
    }

    fmt.Println(o)
    treeSelectionSort(o)
}

func treeSelectionSort(origin []int) []int {
    // 树的层数
    var level int
    var result = make([]int, 0, len(origin))
    for pow(2, level) < len(origin) {
        level++
    }
    // 叶子节点数
    var leaf = pow(2, level)
    var tree = make([]node, leaf*2-1)

    // 先填充叶子节点的数据
    for i := 0; i < len(origin); i++ {
        tree[leaf+i-1] = node{origin[i], true, i}
    }
    // 每层都比较叶子兄弟大小,选出较大值作为父节点
    for i := 0; i < level; i++ {
        // 当前层节点数
        nodeCount := pow(2, level-i)
        // 每组兄弟间比较
        for j := 0; j < nodeCount/2; j++ {
            compareAndUp(&tree, nodeCount-1+j*2)
        }
    }

    // 这个时候树顶端的就是最小的元素了
    result = append(result, tree[0].value)
    fmt.Println(result)

    // 选出最小的元素后,还剩n-1个需要排序
    for t := 0; t < len(origin) - 1; t++ {
        // 赢球的节点
        winNode := tree[0].rank + leaf - 1
        // 把赢球的叶子节点状态改为失效
        tree[winNode].available = false

        // 从下一轮开始,只需与每次胜出节点的兄弟节点进行比较
        for i := 0; i < level; i ++ {
            leftNode := winNode
            if winNode%2 == 0 {
                leftNode = winNode - 1
            }

            // 比较兄弟节点间大小,并将胜出的节点向上传递
            compareAndUp(&tree, leftNode)
            winNode = (leftNode - 1) / 2
        }

        // 每轮都会吧最小的推到树顶端
        result = append(result, tree[0].value)
        fmt.Println(result)
    }

    return origin
}

func compareAndUp(tree *[]node, leftNode int) {
    rightNode := leftNode + 1

    // 除非左节点无效或者右节点有效并且比左节点大,否则就无脑选左节点
    if !(*tree)[leftNode].available || ((*tree)[rightNode].available && (*tree)[leftNode].value > (*tree)[rightNode].value) {
        (*tree)[(leftNode-1)/2] = (*tree)[rightNode]
    } else {
        (*tree)[(leftNode-1)/2] = (*tree)[leftNode]
    }
}

func pow(x, y int) int {
    return int(math.Pow(float64(x), float64(y)))
}

运行结果
[golang] 数据结构-树形选择排序(锦标赛排序)


有疑问加站长微信联系(非本文作者)

本文来自:51CTO博客

感谢作者:NicoChen

查看原文:[golang] 数据结构-树形选择排序(锦标赛排序)

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

1820 次点击  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传