Go语言标准库堆(heap)封装及堆排序实现

Go语言的OOP,接口,接口的组合,基础库的函数及接口如何抽象设计,

这些东西在Go的Heap源码及演示例子处理中,都有很好的展示.

在"container/heap"中,它的接口是如下定义的:

type Interface interface {
    sort.Interface
    Push(x interface{}) // add x as element Len()
    Pop() interface{}   // remove and return element Len() - 1.
}  

我不太明白为啥是这样的设计,只好通过下面的方法来尝试了解.

1. 通过测试例子,了解使用方式.

2. 试图还原原始场景,即利用源码整合实现了一个原始的堆排序

然后通过这两者的对比,来慢慢体会.

container/heap的测试例子:

package main

import (
	"container/heap"
	"fmt"
	"sort"
)

// An IntHeap is a min-heap of ints.
type IntHeap []int

func (h IntHeap) Len() int           { return len(h) }
func (h IntHeap) Less(i, j int) bool { return h[i] < h[j] }
func (h IntHeap) Swap(i, j int)      { h[i], h[j] = h[j], h[i] }

func (h *IntHeap) Push(x interface{}) {
	// Push and Pop use pointer receivers because they modify the slice's length,
	// not just its contents.
	*h = append(*h, x.(int))
}

func (h *IntHeap) Pop() interface{} {
	old := *h
	n := len(old)
	x := old[n-1]
	*h = old[0 : n-1]
	return x
}

// This example inserts several ints into an IntHeap, checks the minimum,
// and removes them in order of priority.
func main() {
	h := &IntHeap{100,16,4,8,70,2,36,22,5,12}

	fmt.Println("\nHeap:")
	heap.Init(h)

	fmt.Printf("最小值: %d\n", (*h)[0])

	//for(Pop)依次输出最小值,则相当于执行了HeapSort
	fmt.Println("\nHeap sort:")
	for h.Len() > 0 {
		fmt.Printf("%d ", heap.Pop(h))
	}

	//增加一个新值,然后输出看看
	fmt.Println("\nPush(h, 3),然后输出堆看看:")
	heap.Push(h, 3)
	for h.Len() > 0 {
		fmt.Printf("%d ", heap.Pop(h))
	}

	fmt.Println("\n使用sort.Sort排序:")
    h2 := IntHeap{100,16,4,8,70,2,36,22,5,12}
    sort.Sort(h2)
    for _,v := range h2 {
		fmt.Printf("%d ",v)
	}
}

/*
输出结果:
-----------------------------------
Heap:
最小值: 2

Heap sort:
2 4 5 8 12 16 22 36 70 100
Push(h, 3),然后输出堆看看:
3
使用sort.Sort排序:
2 4 5 8 12 16 22 36 70 100
*/

自定义的类,实现相关接口后,交由heap.Init()去构建堆.

从堆中Pop()后,数据就被从heap中移除了.

升降序由Less()来决定.

自定义类也可以直接用Sort来排序,因为实现了相关接口.

再看下我手工整合实现的堆排序代码:

package main

//Heap
//author:Xiong Chuan Liang
//date:2015-2-4

import (
	"fmt"
)

var(
  heap = []int{100,16,4,8,70,2,36,22,5,12}
)

func main(){
	fmt.Println("\n数组:")
	Print(heap)

    MakeHeap()
    fmt.Println("\n构建树后:")
	Print(heap)

	fmt.Println("\n增加 90,30,1 :")
	Push(90)
	Push(30)
	Push(1)
	Print(heap)	

	n := Pop()
	fmt.Println("\nPop出最小值(",n,")后:")
	Print(heap)	

	fmt.Println("\nRemove()掉idx为3即值",heap[3-1],"后:")
	Remove(3)
	Print(heap)	

	fmt.Println("\nHeapSort()后:")
	HeapSort()
	Print(heap)	

}

func Print(arr []int){
	for _,v := range arr {
		fmt.Printf("%d ",v)
	}
}

//构建堆
func MakeHeap(){
	n := len(heap)
	for i := n/2-1 ;i >= 0;i--{
		down(i, n)
	}
}

//由父节点至子节点依次建堆
//parent      : i
//left child  : 2*i+1
//right child : 2*i+2
func down(i,n int) {
	//构建最小堆,父小于两子节点值
	for {	

		j1 := 2*i + 1
		if j1 >= n || j1 < 0 { // j1 < 0 after int overflow
			break
		}

		//找出两个节点中最小的(less: a<b)
		j := j1 // left child
		if j2 := j1 + 1; j2 < n && !Less(j1, j2) {
			j = j2 // = 2*i + 2  // right child
		}

		//然后与父节点i比较,如果父节点小于这个子节点最小值,则break,否则swap
		if !Less(j, i) {
			break
		}
		Swap(i, j)
		i = j
	}
}

//由子节点至父节点依次重建堆
func up(j int)  {

	for {
		i := (j - 1) / 2 // parent      

		if i == j || !Less(j, i) {
			//less(子,父) !Less(9,5) == true
			//父节点小于子节点,符合最小堆条件,break
			break
		}
		//子节点比父节点小,互换
		Swap(i, j)
		j = i
	}
}

func Push(x interface{}){
	heap = append(heap, x.(int))
	up(len(heap)-1)
	return
}

func Pop() interface{} {
	n := len(heap) - 1
	Swap(0, n)
	down(0, n)

	old :=heap
	n = len(old)
	x := old[n-1]
	heap = old[0 : n-1]
	return x
}

func Remove(i int) interface{} {
	n := len(heap) - 1
	if n != i {
		Swap(i, n)
		down(i, n)
		up(i)
	}
	return Pop()
}

func Less(a,b int)bool{
	return heap[a] < heap[b]

func Swap(a,b int){
	heap[a],heap[b] = heap[b],heap[a]
}

func HeapSort(){
	//升序 Less(heap[a] > heap[b])	//最大堆
	//降序 Less(heap[a] < heap[b])	//最小堆
	for i := len(heap)-1 ;i > 0;i--{
		//移除顶部元素到数组末尾,然后剩下的重建堆,依次循环
		Swap(0, i)
		down(0, i)
	}
}

/*
输出结果:
-----------------------------------
数组:
100 16 4 8 70 2 36 22 5 12
构建树后:
2 5 4 8 12 100 36 22 16 70
增加 90,30,1 :
1 5 2 8 12 4 36 22 16 70 90 100 30
Pop出最小值( 1 )后:
2 5 4 8 12 30 36 22 16 70 90 100
Remove()掉idx为3即值 4 后:
4 5 8 16 12 30 36 22 100 70 90
HeapSort()后:
100 90 70 36 30 22 16 12 8 5 4

*/

两相对比之后发现,container/heap 竞然,真的,仅且只是封装了一个堆而已.

把那些不确定的,各种需要定制的东西,都交给了用户去实现.

它仅仅只负责最核心的堆这部份的东西.

这样基础库清爽了,使用堆时也不会再感到缚手缚脚了.

MAIL: [email protected]aliyun.com

BLOG:http://blog.csdn.ent/xcl168

时间: 2024-07-30 22:46:36

Go语言标准库堆(heap)封装及堆排序实现的相关文章

(转)堆heap和栈stack

一 英文名称 堆和栈是C/C++编程中经常遇到的两个基本概念.先看一下它们的英文表示: 堆――heap 栈――stack 二 从数据结构和系统两个层次理解 在具体的C/C++编程框架中,这两个概念并不是并行的.深入到汇编级进行研究就会发现,栈是机器系统提供的数据结构,而堆是由C/C++函数库提供的.这两个概念可以从数据结构和系统两个层次去理解: 1.从数据结构层次理解,栈是一种先进后出的线性表,只要符合先进后出的原则的线性表都是栈.至于采用的存储方式(实现方式)是顺序存储(顺序栈)还是链式存储(

【数据结构】堆heap

本节研究堆heap的相关操作实现: 堆 说明几点 (1)堆分为大根堆和小根堆:大根堆的根为最大值,每一个节点的值都不小于其孩子的值: (2)可以利用大根堆实现升序排序:主要是利用大根堆的头和需要排序的最后一个数字交换的思想: (3)使用大根堆实现最大优先级队列,类似stl中queue的操作,只是对于元素在队列中的元素优先级是不一样的,在最大优先级中,队列头为值最大元素:可利用大根堆来维护一个最大优先级队列:向优先级队列中push元素,类似像堆的末尾添加一个元素,然后使用_makeHeapUp寻找

堆(heap)和栈(stack)几点认识

堆(heap)和栈(stack)主要的区别由以下几点:1.管理方式不同:2.空间大小不同:3.产生碎片不同:4.生长方向不同:5.分配归属不同:6.分配效率不同:7.存取效率不同:管理方式:对于栈来讲,释放是由程序自动管理,无需在程序中手工控制:对于堆来说,释放工作由程序员控制,容易产生memory leak.产生碎片:对于堆来讲,频繁的new/delete,malloc/free势必会造成内存空间的不连续,从而造成大量的碎片,使程序效率降低.对于栈来讲,则不会存在这个问题,因为栈是先进后出的队

iOS数据存储类型 及 堆(heap)和栈(stack)

iOS数据存储类型 及 堆(heap)和栈(stack) 一般认为在c中分为这几个存储区: 1栈 --  由编译器自动分配释放. 2堆 --  一般由程序员分配释放,若程序员不释放,程序结束时可能由OS回收. 3全局区(静态存储区)-- 全局变量和静态变量的存储是放在一块区域 ,程序退出后自动释放 .全局区又分为全局初始化区和全局未初始化区.初始化的全局变量和静态变量存放在全局初始化区,未初始化的全局变量和未初始化的静态变量存放在相邻的另一块区域. 4常量区-- 专门放数字/字符常量的地方, 程

《github一天,一个算术题》:堆算法接口(堆排序、堆插入和堆垛机最大的价值,并删除)

阅览.认为.编写代码! /********************************************* * [email protected] * blog: http://blog.csdn.net/hustyangju * 题目:堆排序实现,另外实现接口:取堆最大值并删除.堆插入 * 思路:堆是在顺序数组原址上实现的.利用全然二叉树的性质.更具最大堆和最小堆的定义实现的. * 经典应用场景:内存中堆数据管理 * 空间复杂度:堆排序是在原址上实现的,为0 * 时间复杂度:堆排序为

堆heap和栈Stack(百科)

堆heap和栈Stack 在计算机领域,堆栈是一个不容忽视的概念,堆栈是两种数据结构.堆栈都是一种数据项按序排列的数据结构,只能在一端(称为栈顶(top))对数据项进行插入和删除.在单片机应用中,堆栈是个特殊的存储区,主要功能是暂时存放数据和地址,通常用来保护断点和现场.要点:堆,队列优先,先进先出(FIFO—first in first out)[1]  .栈,先进后出(FILO—First-In/Last-Out). 目录 1 简介 2 对比分析 ? 堆栈空间分配 ? 堆栈缓存方式 ? 堆栈

每日一问2:堆(heap)和栈(stack)的区别

因为这里没有明确指出堆是指数据结构还是存储方式,所以两个尝试都回答一下. 一.堆和栈作为数据结构 1.堆(heap),也叫做优先队列(priority queue),队列中允许的操作是先进先出(FIFO),在队尾插入元素,在队头取出元素.而堆也是一样,在堆底插入元素,在堆顶取出元素,但是堆中元素的排列不是按照到来的先后顺序,而是按照一定的优先顺序排列的.这个优先顺序可以是元素的大小或者其他规则. 2.栈(stack),是一种运算受限的线性表,栈中允许的操作时先进后出(FILO),在栈顶插入元素,

JVM 内存初学 (堆(heap)、栈(stack)和方法区(method) )

这两天看了一下深入浅出JVM这本书,推荐给高级的java程序员去看,对你了解JAVA的底层和运行机制有比较大的帮助.废话不想讲了.入主题:先了解具体的概念:JAVA的JVM的内存可分为3个区:堆(heap).栈(stack)和方法区(method) 堆区:1.存储的全部是对象,每个对象都包含一个与之对应的class的信息.(class的目的是得到操作指令)2.jvm只有一个堆区(heap)被所有线程共享,堆中不存放基本类型和对象引用,只存放对象本身栈区:1.每个线程包含一个栈区,栈中只保存基础数

堆排序:什么是堆?什么是最大堆?二叉堆是什么?堆排序算法是怎么样的?PHP如何实现堆排序?

本文标签:  堆排序 php php算法 堆排序算法 二叉堆 数据结构 REST   服务器 什么是堆 这里的堆(二叉堆),指得不是堆栈的那个堆,而是一种数据结构. 堆可以视为一棵完全的二叉树,完全二叉树的一个"优秀"的性质是,除了最底层之外,每一层都是满的,这使得堆可以利用数组来表示,每一个结点对应数组中的一个元素. 数组与堆之间的关系 二叉堆一般分为两种:最大堆和最小堆. 什么是最大堆 堆中每个父节点的元素值都大于等于其孩子结点(如果存在),这样的堆就是一个最大堆 因此,最大堆中的