专注收集记录技术开发学习笔记、技术难点、解决方案
网站信息搜索 >> 请输入关键词:
您当前的位置: 首页 > Go

Go语言标准库堆(heap)打包及堆排序实现

发布时间:2011-06-29 18:23:04 文章来源:www.iduyao.cn 采编人员:星星草
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: xcl_168@aliyun.com

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






友情提示:
信息收集于互联网,如果您发现错误或造成侵权,请及时通知本站更正或删除,具体联系方式见页面底部联系我们,谢谢。

其他相似内容:

  • ModernUI课程:定义一个Logo

    ModernUI教程:定义一个Logo ModernWindow的标题栏包含了一块区域用来显示自定义的窗体Logo: 这个窗体logo通过ModernWindow.LogoD...

  • Django忘记管理员账号和密码的解决方法

    Django忘记管理员账号和密码的解决办法 看着Django的教程学习搭建网站,结果忘记第一次创建的账号和密码了。结果搭建成功以后,一直...

  • GO语言小结(1)——基本知识

    GO语言总结(1)——基本知识 1、注释(与C++一样)   行注释://  块注释:/*   ...  */ 2、标识符   可以这么说,除了数字开头...

  • golang 惯用的文件读取方式

    golang 常用的文件读取方式 Golang 的文件读取方法很多,刚上手时不知道怎么选择,所以贴在此处便后速查。 一次性读取 小文件推荐一...

  • 查询深圳市通相关信息

    查询深圳通相关信息 用 HTTP.GET 从开放 API 中查询深圳通信息,然后将 JSON 数据存入结构体中,再格式化输出。 注意:获取的并不是实...

  • Go语言设计模式实践:结合(Composite)

    Go语言设计模式实践:组合(Composite) 关于本系列 这个系列首先是关于Go语言实践的。在项目中实际使用Go语言也有段时间了,一个体会就...

  • 列出索引和遍历目录

    列出目录和遍历目录 获取目录列表用 ioutil.ReadDir(),遍历目录用 filepath.Walk(),使用方法请参考文章示例。 示例代码: package ma...

  • io 包的惯用接口速记

    io 包的常用接口速记 我没有 C/C++ 基础,没有接口的概念,且从 Python 投奔而来,Python 的极简主义(一个结果往往只提供一个方法),让我在...

  • 代理服务扩充

    代理服务扩展 之前自己实现了一个代理服务,当时考虑的是只要支持SOCKS5就好了,因为我经常用CHROME,配合着SwitchySharp,体验还是很棒...

  • 文件的创造与打开

    文件的创建与打开 文件操作是个很重要的话题,使用也非常频繁,熟悉如何操作文件是必不可少的。Golang 对文件的支持是在 os package ...

热门推荐: