[Golang] ring 자료구조

Ring 자료구조는 단어가 의미하듯이 Ring 자료를 구성하는 요소들을 순서대로 얻다가 마지막 요소에서 다시 처음 요소로 순회하면서 무한으로 계속 요소 데이터를 얻을 수 있는 자료구조입니다. Go에서 이러한 Ring 자료 구조에 대한 예제 코드는 아래와 같습니다.

package main

import (
    "container/ring"
    "fmt"
    "time"
)

func main() {
    dt := []string{"ONE", "TWO", "THREE", "FOUR", "FIVE"}

    r := ring.New(len(dt))
    for i := 0; i < r.Len(); i++ {
        r.Value = dt[i]
        r = r.Next()
    }

    r.Do(func(x interface{}) {
        fmt.Println(x)
    })

    fmt.Println("------")

    for _ = range time.Tick(time.Second * 1) {
        fmt.Println(r.Value)
        r = r.Next()
    }
}

링의 자료구조 객체는 12번 코드처럼 ring.New 함수를 통해 생성할 수 있으며 이 함수의 인자는 링을 구성하는 데이터 개수입니다. 10번 코드에서 링의 데이터 요소로 구성할 슬라이스 객체를 준비해 두었고 이 슬라이스의 갯수를 인자로 ring.New 함수를 호출하고 있습니다. 13번의 for 문을 통해 슬라이스의 데이터를 하나 하나 링에 추가합니다. 18번 코드에서 링의 Do 함수는 각 요소에 대해 실행할 함수를 지정해 두면 링을 구성하는 모든 데이터 각각에 대해 실행할 함수를 호출해 줍니다. 여기서는 단순히 화면 출력에 대한 코드를 함수로 지정하고 있습니다. 24번 코드가 바로 1초 간격으로 무한 반복으로 링을 구성하는 요소를 얻는 for 문입니다. 위의 코드를 실행하면 다음과 같은 결과를 얻을 수 있습니다.

ONE
TWO
THREE
FOUR
FIVE
------
ONE
TWO
THREE
FOUR
FIVE
ONE
TWO
THREE
FOUR
FIVE
ONE

.
.
.

[Golang] Heap 자료구조

go에서 패키지 라이브러리로 제공하는 힙 자료구조가 있습니다. 제공되는 힙 자료구조는 부모 노드의 값은 자식 노드의 값보다 반드시 작거나 같다라는 조건을 충족해야 합니다. 이러한 조건 충족을 비교 매서드와 힙 자료구조에 요소를 추가(Push)하고 가져오는(Pop) 매서드 등을 제공하는 heap.Interface 인터페이스를 직접 구현한 Custom Type 소스 코드를 작성해야 합니다.

만약 정수형 값을 요소로 하는 힙 자료구조를 만든다고 할때 heap.Interface 인터페이스를 구현하는 Custom Type은 다음과 같습니다.

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(elem interface{}) {
    *h = append(*h, elem.(int))
}

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

    return elem
}

heap.Interface에서 구현해야 하는 매서드는 아래와 같습니다.

매서드 설명
func (h IntHeap) Less(i, j int) bool i와 j 번째 요소의 값을 비교하여 i번째 요소가 j번째 요소보다 작으면 true을 반환
func (h IntHeap) Len() int 요소의 개수를 반환
func (h IntHeap) Swap(i, j int) i번째와 j번째 요소의 값을 서로 바꿈
func (h *IntHeap) Push(elem interface{}) 요소의 값을 추가
func (h *IntHeap) Pop() interface{} 요소의 값을 가져옴(작은 값 순서로 값이 얻어짐)

이제 위의 IntHeap을 사용하는 소스 코드를 살펴 보면 아래와 같습니다.

// main
package main

import (
    "container/heap"
    "fmt"
)

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(elem interface{}) { 
    *h = append(*h, elem.(int)) 
} 

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

    return elem 
} 

func main() { 
    h := &IntHeap{9, 6, 4, 1} 
    heap.Init(h) 

    fmt.Println(*h) 
    fmt.Println("---------------") 

    heap.Push(h, 7) 
    heap.Push(h, 9) 
    heap.Push(h, 11) 

    for h.Len() > 0 {
        m := heap.Pop(h)
        fmt.Print(m, " ")
    }
}

앞서 설명한 코드에 대한 설명은 하지 않고 main 함수를 살펴보면.. 먼저 37번 코드에서 힙 데이터로 구성할 요소들에 대한 슬라이스 객체를 생성합니다. 그리고 38번에서 이 슬라이스 객체를 통해 heap 객체로 초기화합니다. 그리고 43번~45번 코드에서 다시 몇개의 데이터를 더 추가하고 47번의 for 문을 통해 힙의 요소를 순서대로 출력해보면 작은 값에서 점점 큰 값이 얻어지는 것을 볼 수 있습니다. 위의 코드에 대한 실행 결과는 아래와 같습니다.

[1 6 4 9]
---------------
1 4 6 7 9 9 11

이 글은 예제로 배우는 GO 프로그래밍에서 학습한 코드를 제가 이해한 내용을 덧붙여 정리한 글입니다.