go语言标准库
1、io中最重要的两个接口:Reader和Writer
    1.1 Reader 接口
        Reader 接口定义如下:

         

type Reader interface {
                 Read(p []byte) (n int, err error)
             }

        Read 将数据读取到 p 中,返回读取的字节数n,即使 Read 返回的 n < len(p),它也会在调用过程中占用 len(p) 个字节作为暂存空间。
        若可读取的数据不到 len(p) 个字节,Read 会返回可用数据,而不是等待更多数据,
        当 Read 在成功读取 n > 0 个字节后遇到一个错误或 EOF (end-of-file)。

     

func ReadFrom(reader io.Reader, num int) ([]byte, error) {
             p := make([]byte, num)
             n, err := reader.Read(p)
             if n > 0 {
                 return p[:n], nil
             }
             return p, err
         }

        ReadFrom 函数将 io.Reader 作为参数,也就是说,ReadFrom 可以从任意的地方读取数据,只要来源实现了io.Reader 接口。
        比如,我们可以从标准输入、文件、字符串等读取数据,示例代码如下:
 

// 从标准输入读取
         data, err = ReadFrom(os.Stdin, 11)        // 从普通文件读取,其中 file 是 os.File 的实例
           data, err = ReadFrom(file, 9)        // 从字符串读取
         data, err = ReadFrom(strings.NewReader("from string"), 12)
   
     1.2 Writer 接口
         Writer 接口的定义如下:        type Writer interface {
             Write(p []byte) (n int, err error)
         }

        Write 将 len(p) 个字节从 p 中写入到基本数据流中,返回从 p 中被写入的字节数 n ,以及任何遇到的引起写入提前停止的错误。
        若 Write 返回的 n < len(p),它就必须返回一个 非nil 的错误。
  
        fmt.Println 函数的源码如下:

 

func Println(a ...interface{}) (n int, err error) {
           return Fprintln(os.Stdout, a...)
         }        fmt.Println会将内容输出到标准输出中

   

1.3 实现了io.Reader或者io.Writer接口的类型
        os.File 同时实现了这两个接口:

   

Stdin  = NewFile(uintptr(syscall.Stdin), "/dev/stdin")
         Stdout = NewFile(uintptr(syscall.Stdout), "/dev/stdout")
         Stderr = NewFile(uintptr(syscall.Stderr), "/dev/stderr")

        Stdin/Stdout/Stderr 只是三个特殊的文件类型的标识(即都是os.File的实例),自然也实现了 io.Reader 和 io.Writer。

        对io.Reader或者io.Writer所有的实现类型:
            os.File 同时实现了 io.Reader 和 io.Writer
            strings.Reader 实现了 io.Reader
            bufio.Reader/Writer 分别实现了 io.Reader 和 io.Writer
            bytes.Buffer 同时实现了 io.Reader 和 io.Writer
            bytes.Reader 实现了 io.Reader
            compress/gzip.Reader/Writer 分别实现了 io.Reader 和 io.Writer
            crypto/cipher.StreamReader/StreamWriter 分别实现了 io.Reader 和 io.Writer
            crypto/tls.Conn 同时实现了 io.Reader 和 io.Writer
            encoding/csv.Reader/Writer 分别实现了 io.Reader 和 io.Writer
            mime/multipart.Part 实现了 io.Reader
            net/conn 分别实现了 io.Reader 和 io.Writer(Conn接口定义了Read/Write)
        除此之外,io 包本身也有这两个接口的实现类型。如:
            实现了 Reader 的类型:LimitedReader、PipeReader、SectionReader
            实现了 Writer 的类型:PipeWriter

    1.4 ReaderAt和WriterAt 接口
        ReaderAt 接口的定义如下:

        type ReaderAt interface {
            ReadAt(p []byte, off int64) (n int, err error)
        }

        ReadAt 从基本输入源的偏移量 off 处开始,将 len(p) 个字节读取到 p 中。它返回读取的字节数 n 以及任何遇到的错误。
        当 ReadAt 返回的 n < len(p) 时,它就会返回一个 非nil 的错误来解释 为什么没有返回更多的字节。在这一点上,ReadAt 比 Read 更严格。
        即使 ReadAt 返回的 n < len(p),它也会在调用过程中使用 p 的全部作为暂存空间。若可读取的数据不到 len(p) 字节,ReadAt 就会阻塞,直到所有数据都可用或一个错误发生。 在这一点上 ReadAt 不同于 Read。
        若 n = len(p) 个字节从输入源的结尾处由 ReadAt 返回,Read可能返回 err == EOF 或者 err == nil。
        若 ReadAt 携带一个偏移量从输入源读取,ReadAt 应当既不影响偏移量也不被它所影响。
        可对相同的输入源并行执行 ReadAt 调用。

        示例代码如下:

  

reader := strings.NewReader("Go语言中文网")
         p := make([]byte, 6)
         n, err := reader.ReadAt(p, 2)
         if err != nil {
             panic(err)
         }
         fmt.Printf("%s, %d\n", p, n)        输出: 语言,6
        WriterAt 接口的定义如下:
        type WriterAt interface {
             WriteAt(p []byte, off int64) (n int, err error)
         }

        WriteAt 从 p 中将 len(p) 个字节写入到偏移量 off 处的基本数据流中。它返回从 p 中被写入的字节数 n 以及任何遇到的引起写入提前停止的错误。若 WriteAt 返回的 n < len(p),它就必须返回一个 非nil 的错误。
        若 WriteAt 携带一个偏移量写入到目标中,WriteAt 应当既不影响偏移量也不被它所影响。
        若被写区域没有重叠,可对相同的目标并行执行 WriteAt 调用。

        可以通过该接口将数据写入到数据流的特定偏移量之后。

        示例代码如下(os.File 实现了 WriterAt 接口):
  

file, err := os.Create("writeAt.txt")
         if err != nil {
             panic(err)
         }
         defer file.Close()
         file.WriteString("Golang中文社区——这里是多余")
         n, err := file.WriteAt([]byte("Go语言中文网"), 24)
         if err != nil {
                panic(err)
         }
         fmt.Println(n)

        打开文件 WriteAt.txt,内容是:Golang中文社区——Go语言中文网

        分析:
            file.WriteString("Golang中文社区——这里是多余") 往文件中写入 Golang中文社区——这里是多余,之后 file.WriteAt([]byte("Go语言中文网"), 24) 在文件流的 offset=24 处写入 Go语言中文网(会覆盖该位置的内容)

    1.5 ReaderFrom 和 WriterTo 接口
        ReaderFrom 的定义如下:

type ReaderFrom interface {
             ReadFrom(r Reader) (n int64, err error)
         }

        ReadFrom 从 r 中读取数据,直到 EOF 或发生错误。其返回值 n 为读取的字节数。除 io.EOF 之外,在读取过程中遇到的任何错误也将被返回。
        如果 ReaderFrom 可用,Copy 函数就会使用它。

        注意:ReadFrom 方法不会返回 err == EOF。

        示例代码如下:
     

file, err := os.Open("writeAt.txt")
         if err != nil {
             panic(err)
         }
         defer file.Close()
         writer := bufio.NewWriter(os.Stdout)
         writer.ReadFrom(file)
         writer.Flush()

        当然,我们可以通过 ioutil 包的 ReadFile 函数获取文件全部内容。其实,跟踪一下 ioutil.ReadFile 的源码,会发现其实也是通过 ReadFrom 方法实现(用的是 bytes.Buffer,它实现了 ReaderFrom 接口)。

        如果不通过 ReadFrom 接口来做这件事,而是使用 io.Reader 接口,我们有两种思路:
        (1)先获取文件的大小(File 的 Stat 方法),之后定义一个该大小的 []byte,通过 Read 一次性读取
        (2)定义一个小的 []byte,不断的调用 Read 方法直到遇到 EOF,将所有读取到的 []byte 连接到一起

        注意:bufio.Writer 或 strings.Buffer 类型的 ReadFrom 方法实现,会发现,其实它们的实现和上面说的第 (2) 种思路类似。

        WriterTo的定义如下:
     

type WriterTo interface {
             WriteTo(w Writer) (n int64, err error)
         }

        WriteTo 将数据写入 w 中,直到没有数据可写或发生错误。其返回值 n 为写入的字节数。 在写入过程中遇到的任何错误也将被返回。
        如果 WriterTo 可用,Copy 函数就会使用它。

        注意:ReaderFrom 和 WriterTo 接口的方法接收的参数是 io.Reader 和 io.Writer 类型。

        示例代码:
     

reader := bytes.NewReader([]byte("Go语言中文网"))
         reader.WriteTo(os.Stdout)

    1.6 Seeker 接口
        接口定义如下:
      

type Seeker interface {
             Seek(offset int64, whence int) (ret int64, err error)
         }

        Seek 设置下一次 Read 或 Write 的偏移量为 offset,它的解释取决于 whence: 0 表示相对于文件的起始处,1 表示相对于当前的偏移,而 2 表示相对于其结尾处。 Seek 返回新的偏移量和一个错误,如果有的话。

        Seek 方法是用于设置偏移量的,这样可以从某个特定位置开始操作数据流。听起来和 ReaderAt/WriteAt 接口有些类似,不过 Seeker 接口更灵活,可以更好的控制读写数据流的位置。

        示例代码:获取倒数第二个字符(需要考虑 UTF-8 编码)

    

reader := strings.NewReader("Go语言中文网")
         reader.Seek(-6, io.SeekEnd)
         r, _, _ := reader.ReadRune()
         fmt.Printf("%c\n", r)

    

1.7 Closer 接口
        接口定义如下:

       

type Closer interface {
                 Close() error
             }

        该接口比较简单,只有一个 Close() 方法,用于关闭数据流。
        文件 (os.File)、归档(压缩包)、数据库连接、Socket 等需要手动关闭的资源都实现了 Closer 接口。
        实际编程中,经常将 Close 方法的调用放在 defer 语句中。

        源码如下:

          

func (f *File) Close() error {
                 if f == nil {
                     return ErrInvalid
                 }
                 return f.file.close()
             }

        注意:在关闭文件之前要先检查错误,因为如果文件为空会panic    

    1.8 ByteReader 和 ByteWriter
        接口定义如下:

        

type ByteReader interface {
                 ReadByte() (c byte, err error)
             }            type ByteWriter interface {
                 WriteByte(c byte) error
             }

        在标准库中,有如下类型实现了 io.ByteReader 或 io.ByteWriter:

            bufio.Reader/Writer 分别实现了io.ByteReader 和 io.ByteWriter
            bytes.Buffer 同时实现了 io.ByteReader 和 io.ByteWriter
            bytes.Reader 实现了 io.ByteReader
            strings.Reader 实现了 io.ByteReader

        接下来的示例中,我们通过 bytes.Buffer 来一次读取或写入一个字节(主要代码):

        

var ch byte
             fmt.Scanf("%c\n", &ch)            buffer := new(bytes.Buffer)
             err := buffer.WriteByte(ch)
             if err == nil {
                 fmt.Println("写入一个字节成功!准备读取该字节……")
                 newCh, _ := buffer.ReadByte()
                 fmt.Printf("读取的字节:%c\n", newCh)
             } else {
                 fmt.Println("写入错误")
             }

    1.9 ByteScanner、RuneReader 和 RuneScanner
        ByteScanner 接口的定义如下:

          

type ByteScanner interface {
                 ByteReader
                 UnreadByte() error
             }

        可见,它内嵌了 ByteReader 接口(可以理解为继承了 ByteReader 接口),
        UnreadByte 方法的意思是:将上一次 ReadByte 的字节还原,使得再次调用 ReadByte 返回的结果和上一次调用相同,也就是说,UnreadByte 是重置上一次的 ReadByte。
        注意,UnreadByte 调用之前必须调用了 ReadByte,且不能连续调用 UnreadByte。

        这里需要补充

    1.10 SectionReader 
        接口定义如下:

          

type SectionReader struct {
                 r     ReaderAt    // 该类型最终的 Read/ReadAt 最终都是通过 r 的 ReadAt 实现
                 base  int64        // NewSectionReader 会将 base 设置为 off
                 off   int64        // 从 r 中的 off 偏移处开始读取数据
                 limit int64        // limit - off = SectionReader 流的长度
             }            func NewSectionReader(r ReaderAt, off int64, n int64) *SectionReader

        NewSectionReader 返回一个 SectionReader,它从 r 中的偏移量 off 处读取 n 个字节后以 EOF 停止。
        也就是说,SectionReader 只是内部(内嵌)ReaderAt 表示的数据流的一部分:从 off 开始后的 n 个字节。
        这个类型的作用是:方便重复操作某一段 (section) 数据流;或者同时需要 ReadAt 和 Seek 的功能。
    
    1.11 LimitedReader 
        接口定义如下:            

            

type LimitedReader struct {
                 R Reader // underlying reader,最终的读取操作通过 R.Read 完成
                 N int64  // max bytes remaining
             }

        从 R 读取但将返回的数据量限制为 N 字节。每调用一次 Read 都将更新 N 来反应新的剩余数量。
        也就是说每次最多返回 N 字节数据。

        LimitedReader 只实现了 Read 方法(Reader 接口)。示例代码如下:

            

content := "This Is LimitReader Example"
             reader := strings.NewReader(content)
             limitReader := &io.LimitedReader{R: reader, N: 8}
             for limitReader.N > 0 {
                 tmp := make([]byte, 2)
                 limitReader.Read(tmp)
                 fmt.Printf("%s", tmp)
             }

        输出: This Is

        可见,通过该类型可以达到 只允许读取一定长度数据 的目的。
        在 io 包中,LimitReader 函数的实现其实就是调用 LimitedReader:

            func LimitReader(r Reader, n int64) Reader { return &LimitedReader{r, n} }

    1.12 PipeReader 和 PipeWriter
        PipeReader 是管道的读取端。它实现了 io.Reader 和 io.Closer 接口。结构定义如下:

     

type PipeReader struct {
                 p *pipe
             }

        关于 PipeReader.Read 方法的说明:从管道中读取数据。该方法会堵塞,直到管道写入端开始写入数据或写入端被关闭。
        如果写入端关闭时带有 error(即调用 CloseWithError 关闭),该Read返回的 err 就是写入端传递的error;否则 err 为 EOF。

        PipeWriter 是管道的写入端。它实现了 io.Writer 和 io.Closer 接口。结构定义如下:

        

type PipeWriter struct {
                 p *pipe
             }

        关于 PipeWriter.Write 方法的说明:写数据到管道中。该方法会堵塞,直到管道读取端读完所有数据或读取端被关闭。
        如果读取端关闭时带有 error(即调用 CloseWithError 关闭),该Write返回的 err 就是读取端传递的error;否则 err 为 ErrClosedPipe。

        示例代码如下:

      

func main() {
                 pipeReader, pipeWriter := io.Pipe()
                 go PipeWrite(pipeWriter)
                 go PipeRead(pipeReader)
                 time.Sleep(30 * time.Second)
             }            func PipeWrite(writer *io.PipeWriter){
                 data := []byte("Go语言中文网")
                 for i := 0; i < 3; i++{
                     n, err := writer.Write(data)
                     if err != nil{
                         fmt.Println(err)
                         return
                     }
                     fmt.Printf("写入字节 %d\n",n)
                 }
                 writer.CloseWithError(errors.New("写入段已关闭"))
             }            func PipeRead(reader *io.PipeReader){
                 buf := make([]byte, 128)
                 for{
                     fmt.Println("接口端开始阻塞5秒钟...")
                     time.Sleep(5 * time.Second)
                     fmt.Println("接收端开始接受")
                     n, err := reader.Read(buf)
                     if err != nil{
                         fmt.Println(err)
                         return
                     }
                     fmt.Printf("收到字节: %d\n buf内容: %s\n",n,buf)
                 }
             }

        问题:为什么一个中文字是3个字节?

        io.Pipe() 用于创建一个同步的内存管道 (synchronous in-memory pipe),函数签名:

            func Pipe() (*PipeReader, *PipeWriter)

        它将 io.Reader 连接到 io.Writer。一端的读取匹配另一端的写入,直接在这两端之间复制数据;它没有内部缓存。
        它对于并行调用 Read 和 Write 以及其它函数或 Close 来说都是安全的。一旦等待的 I/O 结束,Close 就会完成。
        并行调用 Read 或并行调用 Write 也同样安全:同种类的调用将按顺序进行控制。
        正因为是同步的,因此不能在一个 goroutine 中进行读和写。
        另外,对于管道的 close 方法(非 CloseWithError 时),err 会被置为 EOF。

    1.13 Copy 和 CopyN函数

3、数据结构与算法

    数据结构是数据组织和存储的逻辑形式,以达到方便访问和修改数据的目的。
    算法是根据实际输入输出的需求设计的一系列计算过程,被认为是程序的灵魂。
    本章内容涵盖了 Go 标准库中的 3 个包:
        sort 包包含基本的排序方法,支持切片数据排序以及用户自定义数据集合排序,sort的实现不依赖与其他任何包
        index/suffixary 包实现了后缀数组相关算法以支持许多常见的字符串操作
        container 包提供了对 heap、list 和 ring 这 3 种数据结构的底层支持。

    3.1 sort--排序算法
        该包实现了四种基本排序算法:插入排序、归并排序、堆排序和快速排序。 但是这四种排序方法是不公开的,它们只被用于 sort 包内部使用。
        sort.Interface接口的定义如下:

      

type Interface interface {
                 // 获取数据集合元素个数
                 Len() int
                 // 如果 i 索引的数据小于 j 索引的数据,返回 true,且不会调用下面的 Swap(),即数据升序排序。
                 Less(i, j int) bool
                 // 交换 i 和 j 索引的两个元素的位置
                 Swap(i, j int)
             }


        
        对数据集合排序时不必考虑应当选择哪一种排序方法,只要实现了 sort.Interface 定义的三个方法就可以顺利对数据集合进行排序,sort 包会根据实际数据自动选择高效的排序算法。 
        除此之外,为了方便对常用数据类型的操作,sort 包提供了对[]int 切片、[]float64 切片和[]string 切片完整支持,主要包括:

            (1)对基本数据类型切片的排序支持
            (2)基本数据元素查找
            (3)判断基本数据类型切片是否已经排好序
            (4)对排好序的数据集合逆序

        3.1.1 数据集合排序
            数据集合实现了上述三个方法后,即可调用该包的 Sort() 方法进行排序。 
            Sort() 方法定义如下:

                func Sort(data Interface)

            Sort() 方法惟一的参数就是待排序的数据集合。
            该包还提供了一个方法可以判断数据集合是否已经排好顺序,该方法的内部实现依赖于我们自己实现的 Len() 和 Less() 方法:

          

func IsSorted(data Interface) bool {
                     n := data.Len()
                     for i := n - 1; i > 0; i-- {
                         if data.Less(i, i-1) {
                             return false
                         }
                     }
                     return true
                 }            下面是一个使用 sort 包对学生成绩排序的示例:
                package main
                import (
                     "fmt"
                     "sort"
                 )                // 学生成绩结构体
                 type StuScore struct {
                     name  string    // 姓名
                     score int   // 成绩
                 }                type StuScores []StuScore
                //Len()
                 func (s StuScores) Len() int {
                     return len(s)
                 }                //Less(): 成绩将有低到高排序
                 func (s StuScores) Less(i, j int) bool {
                     return s[i].score < s[j].score
                 }                //Swap()
                 func (s StuScores) Swap(i, j int) {
                     s[i], s[j] = s[j], s[i]
                 }                func main() {
                     stus := StuScores{
                                 {"alan", 95},
                                 {"hikerell", 91},
                                 {"acmfly", 96},
                                 {"leao", 90},
                                 }                    // 打印未排序的 stus 数据
                     fmt.Println("Default:\n\t",stus)
                     //StuScores 已经实现了 sort.Interface 接口 , 所以可以调用 Sort 函数进行排序
                     sort.Sort(stus)
                     // 判断是否已经排好顺序,将会打印 true
                     fmt.Println("IS Sorted?\n\t", sort.IsSorted(stus))
                     // 打印排序后的 stus 数据
                     fmt.Println("Sorted:\n\t",stus)
                     //这个可以逆序排序,上面是升序,这个是逆序
                     sort.Sort(sort.Reverse(stus))
                     fmt.Println(stus)
                 }

            该示例程序的自定义类型 StuScores 实现了 sort.Interface 接口,所以可以将其对象作为 sort.Sort() 和 sort.IsSorted() 的参数传入。
            运行结果:

     

Default:
                      [{alan 95} {hikerell 91} {acmfly 96} {leao 90}]
                 IS Sorted?
                      true
                 Sorted:
                      [{leao 90} {hikerell 91} {alan 95} {acmfly 96}]

            此外,sort包提供了 Reverse() 方法,可以允许将数据按 Less() 定义的排序方式逆序排序,而不必修改 Less() 代码。方法定义如下:

                func Reverse(data Interface) Interface

            我们可以看到 Reverse() 返回的一个 sort.Interface 接口类型,整个 Reverse() 的内部实现比较有趣:

             

// 定义了一个 reverse 结构类型,嵌入 Interface 接口
                 type reverse struct {
                     Interface
                 }                //reverse 结构类型的 Less() 方法拥有嵌入的 Less() 方法相反的行为
                 //Len() 和 Swap() 方法则会保持嵌入类型的方法行为
                 func (r reverse) Less(i, j int) bool {
                     return r.Interface.Less(j, i)
                 }                // 返回新的实现 Interface 接口的数据类型
                 func Reverse(data Interface) Interface {
                     return &reverse{data}
                 }


            
            Search()定义如下:

                func Search(n int, f func(int) bool) int

            该方法会使用“二分查找”算法来找出能使 f(x)(0<=x<n) 返回 ture 的最小值 i。 前提条件 : f(x)(0<=x<i) 均返回 false, f(x)(i<=x<n) 均返回 ture。 如果不存在 i 可以使 f(i) 返回 ture, 则返回 n。

            Search() 函数一个常用的使用方式是搜索元素 x 是否在已经升序排好的切片 s 中:

          

x := 11
                 s := []int{3, 6, 8, 11, 45} // 注意已经升序排序
                 pos := sort.Search(len(s), func(i int) bool { return s[i] >= x })
                 if pos < len(s) && s[pos] == x {
                     fmt.Println(x, " 在 s 中的位置为:", pos)
                 } else {
                     fmt.Println("s 不包含元素 ", x)
                 }

            官方文档还给出了一个猜数字的小程序:

          

func GuessingGame() {
                     var s string
                     fmt.Printf("Pick an integer from 0 to 100.\n")
                     answer := sort.Search(100, func(i int) bool {
                         fmt.Printf("Is your number <= %d? ", i)
                         fmt.Scanf("%s", &s)
                         return s != "" && s[0] == 'y'
                     })
                     fmt.Printf("Your number is %d.\n", answer)
                 }

            问题:Search()函数需要学习

        3.1.2 sort.Search
            需要学习

    3.3 container--容器数据类型:heap、list 和 ring
        3.3.1 堆
            这里的堆使用的数据结构是最小二叉树,即根节点比左边子树和右边子树的所有值都小。 go 的堆包只是实现了一个接口,我们看下它的定义:

      

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


            可以看出,这个堆结构继承自 sort.Interface, 回顾下 sort.Interface,它需要实现三个方法

            Len() int
            Less(i, j int) bool
            Swap(i, j int)
            加上堆接口定义的两个方法

            Push(x interface{})
            Pop() interface{}
            就是说你定义了一个堆,就要实现五个方法,直接拿 package doc 中的 example 做例子:

         

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{}) {
                 *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
             }


            那么 IntHeap 就实现了这个堆结构,我们就可以使用堆的方法来对它进行操作:

            h := &IntHeap{2, 1, 5}
            heap.Init(h)
            heap.Push(h, 3)
            heap.Pop(h)
            具体说下内部实现,是使用最小堆,索引排序从根节点开始,然后左子树,右子树的顺序方式。索引布局如下:

                              0
                        1            2
                     3    4       5      6
                    7 8  9 10   11   
            假设 (heap[1]== 小明 ) 它的左子树 (heap[3]== 小黑 ) 和右子树 (heap[4]== 大黄 ) 且 小明 > 小黑 > 大黄 ;

            堆内部实现了 down 和 up 函数 : down 函数用于将索引 i 处存储的值 ( 设 i=1, 即小明 ) 与它的左子树 ( 小黑 ) 和右子树 ( 大黄 ) 相比 , 将三者最小的值大黄与小明的位置交换,交换后小明继续与交换后的子树 (heap[9]和 heap[10]) 相比,重复以上步骤,直到小明位置不变。

            up 函数用于将索引 i 处的值 ( 设 i=3, 即小黑 ) 与他的父节点 ( 小明 ) 比较,将两者较小的值放到父节点上,本例中即交换小黑和小明的位置,之后小黑继续与其父节点比较,重复以上步骤,直到小黑位置不变。
            假设 heap[11]== 阿花 当从堆中 Pop 一个元素的时候,先把元素和最后一个节点的值 ( 阿花 ) 交换,然后弹出,然后对阿花调用 down,向下保证最小堆。

            当往堆中 Push 一个元素的时候,这个元素插入到最后一个节点,本例中为 heap[12],即作为 heap[5]的右子树,然后调用 up 函数向上比较。