[golang] 数据结构-堆排序

接上文 树形选择排序
上篇也说了,树形选择排序相较简单选择排序,虽然减少了时间复杂度,但是使用了较多空间去储存每轮比较的结果,并且每次还要再和胜出节点比较。而堆排序就是为了优化这个问题而在1964年被两位大佬发明。

原理
首先有几个关于树的定义:

如果一棵树每个节点的值都大于(小于)或等于其字节点的话,那么这棵树就是大(小)根树
如果一棵大(小)根树正好又是完全二叉树,则被称为大根堆(小根堆)

堆排序就是利用大根堆(小根堆)的特性进行排序的。
从小到大排序一般用大根堆,从大到小一般用小根堆。

流程

  • 先把待排序的数组8、4、12、7、35、9、22、41、2用完全二叉树表示
  • 按大根堆的特性把这个完全二叉树从最后一个非叶子节点开始比较,把较大值交换到当前位置。遇到上层节点顺序影响下层节点不满足大根堆特性时,再对下层节点进行排序。最终得到初始状态的大根堆。

  • 然后将根节点与最后一个叶子节点进行交换
  • 交换后,忽略最后一个叶子节点,再对这棵树的节点进行比较与交换,再次得到符合大根堆要求的树。然后继续将根节点与最后的叶子节点进行交换
  • 以此类推,最终可以得到一个有序的数组

复杂度
平均o(n*logn)
由于初次构建大根堆时有较多次的排序,所以不适合对少量元素进行排序。由于相同数值的节点在比较过程中不能保证顺序,所以是种不稳定的排序方法。

代码

package main

import (
    "fmt"
    "math/rand"
)

func main() {
    var length = 20
    var tree []int

    for i := 0; i < length; i++ {
        tree = append(tree, int(rand.Intn(1000)))
    }
    fmt.Println(tree)

    // 此时的切片o可以理解为初始状态二叉树的数(qie)组(pian)表示,然后需要将这个乱序的树调整为大根堆的状态
    // 由于是从树的右下角第一个非叶子节点开始从右向左从下往上进行比较,所以可以知道是从n/2-1这个位置的节点开始算
    for i := length/2 - 1; i >= 0; i-- {
        nodeSort(tree, i, length-1)
    }

    // 次数tree已经是个大根堆了。只需每次交换根节点和最后一个节点,并减少一个比较范围。再进行一轮比较
    for i := length - 1; i > 0; i-- {
        // 如果只剩根节点和左孩子节点,就可以提前结束了
        if i == 1 && tree[0] <= tree[i] {
            break
        }
        // 交换根节点和比较范围内最后一个节点的数值
        tree[0], tree[i] = tree[i], tree[0]
        // 这里递归的把较大值一层层提上来
        nodeSort(tree, 0, i -1)
        fmt.Println(tree)
    }
}

func nodeSort(tree []int, startNode, latestNode int) {
    var largerChild int
    leftChild := startNode*2 + 1
    rightChild := leftChild + 1

    // 子节点超过比较范围就跳出递归
    if leftChild >= latestNode {
        return
    }

    // 左右孩子节点中找到较大的,右孩子不能超出比较的范围
    if rightChild <= latestNode && tree[rightChild] > tree[leftChild] {
        largerChild = rightChild
    } else {
        largerChild = leftChild
    }

    // 此时startNode节点数值已经最大了,就不用再比下去了
    if tree[largerChild] <= tree[startNode] {
        return
    }

    // 到这里发现孩子节点数值比父节点大,所以交换位置,并继续比较子孙节点,直到把大鱼捞上来
    tree[startNode], tree[largerChild] = tree[largerChild], tree[startNode]
    nodeSort(tree, largerChild, latestNode)
}

注:代码里用递归并不是最优解

运行结果

原文地址:http://blog.51cto.com/13022101/2147404

时间: 2024-10-01 11:19:08

[golang] 数据结构-堆排序的相关文章

数据结构 - 堆排序(heap sort) 详解 及 代码(C++)

堆排序(heap sort) 详解 及 代码(C++) 本文地址: http://blog.csdn.net/caroline_wendy 堆排序包含两个步骤: 第一步: 是建立大顶堆(从大到小排序)或小顶堆(从小到大排序), 从下往上建立; 如建堆时, s是从大到小; 第二步: 是依次交换堆顶和堆底, 并把交换后的堆底输出, 只排列剩余的堆, 从上往下建立; 如构造时, s始终是1; 代码: /* * main.cpp * * Created on: 2014.6.12 * Author: S

堆数据结构+堆排序+最大优先队列的堆的实现

对于堆排序,首先要先知道什么是堆数据结构,堆数据结构就是一个完全二叉树,但是它有自己的性质. 例如最大堆的性质为:A[PARENT[i]]>=A[i]:即每一个结点的值大于等于其左右孩子的值,小于等于其父节点的值.我们在这里只讨论最大堆的情况.我们知道一颗完全二叉树对应一个最大堆的形式,我们要做的就是将二叉树转化为最大堆,这就是所谓的最大堆的维护,我们定义函数MaxheapFY(A,i)来进行操作. 代码: /** *MaxheapFY(A,i):维护位置i最大堆性质,此时假设left(i)和r

数据结构——堆排序

堆排序算法就是通过维护一个小根堆或者大根堆的数据结构.小/大根堆本质上是一个完全二叉树.利用了完全二叉树的性质,即完全二叉树节点x的子节点编号为2x和2x+1. 利用这个性质,我们可以让一个一维数组来模拟这个二叉树,数组下标从1开始建立,下标为2*x和2*x+1的就是x的左子树和右子树. #include<bits/stdc++.h> using namespace std; const int N = 100010; //h[n]是堆数组(一维存储) int h[N]; //s表示的是堆的大

数据结构 堆排序原理及其实现

堆:堆是具有特殊性质的二叉树 每个结点都大于其左右儿子的的二叉树叫大顶堆 每个结点都小于其左右儿子的二叉树叫做小顶堆 堆排序图解: 给定一个整形数组a[]={16,7,3,20,17,8},对其进行堆排序. 首先根据该数组元素构建一个完全二叉树,得到 然后需要构造初始堆,则从最后一个非叶节点开始调整,调整过程如下: 20和16交换后导致16不满足堆的性质,因此需重新调整 这样就得到了初始堆. 即每次调整都是从父节点.左孩子节点.右孩子节点三者中选择最大者跟父节点进行交换(交换之后可能造成被交换的

数据结构--堆排序

#include <stdio.h> #include <stdlib.h> void MaxHeapify(int a[],int length,int i); void BuildMaxHeapify(int a[],int length); void HeapSort(int a[],int length); void main(void) { int i; int a[10]={1,3,4,2,1,3,2,19,4,0}; printf("Heapsort alg

数据结构 - 堆排序(heap sort) 具体解释 及 代码(C++)

堆排序(heap sort) 具体解释 及 代码(C++) 本文地址: http://blog.csdn.net/caroline_wendy 堆排序包括两个步骤: 第一步: 是建立大顶堆(从大到小排序)或小顶堆(从小到大排序), 从下往上建立; 如建堆时, s是从大到小; 第二步: 是依次交换堆顶和堆底, 并把交换后的堆底输出, 仅仅排列剩余的堆, 从上往下建立; 如构造时, s始终是1; 堆排序(Heap Sort)的时间复杂度是O(nlogn), 最坏情况下也是如此. 而高速排序(Quic

数据结构堆排序

#include <iostream> #include<stdlib.h> // 创建一个结构体 typedef struct node{ int data; struct node *left,*right; }BiTreeNode; int rear=0,front=0;//全局变量 //创建完全二叉树 BiTreeNode *CreateBiTree(int a[],BiTreeNode **Q,int n){ BiTreeNode *root,*p,*t=NULL; in

[golang] 数据结构-简单选择排序

原理简单选择排序应该是最直观最容易理解的排序方法了.直接从头开始一个一个去比,找出最小的放到最左边.再依次完成其他位的排序. 时间复杂度比较次数固定为O(n^2),数据交换次数是0~n-1次因为会交换不同位置相同数值的数据,所以选择排序并不稳定 代码实现 package main import ( "fmt" ) func main() { var length = 10 var mm = make(map[int]int, length) var o []int // 先准备一个顺序

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

接上文 简单选择排序简单选择排序很容易理解,代码也很容易实现.但毕竟比较次数太多.树形选择排序则对这个问题进行了改进. 原理简单来说,树形选择排序(Tree selection sort)就是在选择完一轮找出最小值后,直接在与最小值比较中稍大的元素里筛选出最小的.这样避免了简单选择查询那种,抛弃了之前比较过的结果,每次都全部重新比较的情况. 流程举例 先列出所有待排序的元素如:8.4.12.7.35.9.22,并用他们组成满二叉树的叶子元素,不足的位置以∞作为补充.将元素两两相比较,分别得到较小