Post

go - PS

go를 이용해 알고리즘 문제 풀이할 때 유용한 팁 정리

go - PS

fmt

bufio.NewReader && fmt.Fscanf

대량의 입력을 받고자한다면, bufiofmt.Fscan을 사용하는 것이 좋습니다.

1
2
r := bufio.NewReader(os.Stdin)
fmt.Fscan(r, &v)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package main

import (
  "bufio"
  "fmt"
  "os"
)

var br = bufio.NewReader(os.Stdin)

func main() {
  a := make([]int, 5)
  for i = 0; i < len(a); i++ {
    fmt.Fscan(br, &a[i])
  }
}

여러개의 변수를 다음과 같이 Fscan 혹은 Fscanf를 이용해서 읽을 수 있습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package main

import (
  "fmt"
  "bufio"
  "os"
)

var br = bufio.NewReader(os.Stdin)

func main {
  var n int
  var m int
  fmt.Fscan(br, &n, &m)
  // 혹은 다음과 같이 사용할 수 있습니다.
  fmt.Fscanf(br, "%d %d\n", &n, &m)
}

출력의 경우,bufio패키지를 사용하는 것이 출력할 결과의 개행문자가 많은 경우 손해일 수 있기에 fmt.Println으로 충분합니다.

cmp

cmp.Compare && cmp.Or

cmp 패키지를 이용해 값 비교를 통한 정렬을 구현할 수 있습니다. cmp.Compare 함수로 go기본 자료형에 대한 값 비교를 진행할 수 있습니다.

1
2
3
4
5
6
7
8
func Compare[T Ordered](x, y T) int

type Ordered interface {
	~int | ~int8 | ~int16 | ~int32 | ~int64 |
		~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr |
		~float32 | ~float64 |
		~string
}

cmp.Or함수를 이용해 하나 이상의 값비교가 가능합니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
package main

import (
  "fmt"
  "cmp"
  "slices"
)

type Order struct {
  Product string
  Customer string
  Price float64
}

func main() {
  orders := []Order{
		{"foo", "alice", 1.00},
		{"bar", "bob", 3.00},
		{"baz", "carol", 4.00},
		{"foo", "alice", 2.00},
		{"bar", "carol", 1.00},
		{"foo", "bob", 4.00},
	}
  // customer 작은 순, product 작은순, price 큰순으로 정렬됩니다.
  slices.SortFunc(orders, func(a, b Order) int {
    return cmp.Or(
      cmp.Compare(a.Customer, b.Customer),
      cmp.Compare(a.Product, b.Product), 
      cmp.Compare(b.Price, a.Price)
    )
  })
}

slices

slices패키지에는 슬라이스에 사용가능한 다양한 함수들이 정의되어 있습니다.

BinarySearch

1
func BinarySearch[S ~[]E, E cmp.Ordered](x S, target E) (int, bool)

오름차순으로 정렬된 기본 타입 슬라이스에 적용가능한 함수입니다.

만약 찾으려는 값이 슬라이스에 존재한다면, 찾으려는 값의 인덱스가 없다면 삽입될 인덱스의 값과 찾았는지 여부가 리턴됩니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package main

import (
  "fmt"
  "slices"
)

func main() {
  names := []string{"Alice", "Bob", "Vera"}
  n, found := slices.BinarySearch(names, "Vera")
  fmt.Println("Vera: ", n, found)
  n, found = slices.BinarySearch(names, "Bill")
  fmt.Println("Bill: ", n, found)
}

// Vera: 2 true
// Bill: 1 false

BinarySearchFunc 메소드로는 기본 타입 뿐만 아니라, 어떤 타입이든 탐색가능합니다.

1
func BinarySearchFunc[S ~[]E, E, T any](x S, target T, cmp func(E, T) int) (int, bool)

먼저 슬라이스는 cmp 함수를 이용해서 오름차순으로 정렬되어 있어야합니다. 리턴 결과는 BinarySearch와 동일합니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package main

import (
  "fmt"
  "slices"
  "strings"
)

type Person struct {
  Name string
  Age int
}

func main() {
  people := []Person {
    {"Alice", 55},
    {"Bob", 24},
    {"Gopher", 13}
  }
  n, found := slices.BinarySearchFunc(people, Person{"Bob", 0}, func(a, b Person) int {
    return strings.Compare(a.Name, b.Name)
  })
  fmt.Println("Bob: ",n,found)
}
// Bob: 1 true

Max && Min

최대 최소를 구하는 함수도 유사하게, Max, Min이 있고, 비교함수를 추가할 수 있는 MaxFunc, MinFunc가 있습니다.

Sort

기본 타입 슬라이스인 경우, Sort를 이용해서 정렬 가능합니다. 그렇지 않은 경우 SortFunc를 이용해서 원하는 순서로 정렬할 수 있습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package main

import (
	"cmp"
	"fmt"
	"slices"
	"strings"
)

func main() {
	type Person struct {
		Name string
		Age  int
	}
	people := []Person{
		{"Gopher", 13},
		{"Alice", 55},
		{"Bob", 24},
		{"Alice", 20},
	}
	slices.SortFunc(people, func(a, b Person) int {
		if n := strings.Compare(a.Name, b.Name); n != 0 {
			return n
		}
		// If names are equal, order by age
		return cmp.Compare(a.Age, b.Age)
	})
	fmt.Println(people)
}

container

heap

container/heap패키지를 사용해서 우선순위 큐를 사용할 수 있습니다. 우선순위 큐를 구현하기 위해서는 다음과 같이 구현할 수 있습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
package main

import (
	"container/heap"
	"fmt"
)

type Item struct {
	value    string
	priority int
	index    int
}

// heap.Interface를 구현하는 PriorityQueue 타입을 선언합니다.
type PriorityQueue []*Item

func (pq PriorityQueue) Len() int { return len(pq) }

func (pq PriorityQueue) Less(i, j int) bool {
	// 우선 순위가 높을 수록 상단에 위치합니다.
	return pq[i].priority > pq[j].priority
}

func (pq PriorityQueue) Swap(i, j int) {
	pq[i], pq[j] = pq[j], pq[i]
	pq[i].index = i
	pq[j].index = j
}

func (pq *PriorityQueue) Push(x any) {
	n := len(*pq)
	item := x.(*Item)
	item.index = n
	*pq = append(*pq, item)
}

func (pq *PriorityQueue) Pop() any {
	old := *pq
	n := len(old)
	item := old[n-1]
	old[n-1] = nil
	item.index = -1
	*pq = old[0 : n-1]
	return item
}

func main() {
	items := map[string]int{
		"banana": 3, "apple": 2, "pear": 4,
	}

	pq := make(PriorityQueue, len(items))
	i := 0
	for value, priority := range items {
		pq[i] = &Item{
			value:    value,
			priority: priority,
			index:    i,
		}
		i++
	}
	heap.Init(&pq)

	item := &Item{
		value:    "orange",
		priority: 1,
	}
	heap.Push(&pq, item)
	for pq.Len() > 0 {
		item := heap.Pop(&pq).(*Item)
		fmt.Printf("%.2d:%s\n", item.priority, item.value)
	}
}
/*
04:pear
03:banana
02:apple
01:orange
*/

같은 패키지 안에서 선언한 타입에 대해서만 리시버 함수를 구현할 수 있기에, PriorityQueue타입을 만들었고, Push, Pop 기능을 구현했습니다. 추가로 내부에서 우선순위 큐 동작을 위한 Less, Len, Swap함수를 정의했습니다.

list

container/list 패키지에서 제공하는 인터페이스를 이용해서 스택과 큐를 구현할 수 있습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
package main

import (
	"container/list"
	"fmt"
)

type Item struct {
	value    string
	priority int
}

func (e Item) String() string {
	return fmt.Sprintf("%s %d", e.value, e.priority)
}

func main() {
	l := list.New()
	l.PushBack(Item{"hello", 1})
	l.PushBack(Item{"world", 2})

	for e := l.Front(); e != nil; e = e.Next() {
		fmt.Println(e.Value)
	}

	l.PushBack(Item{"hello", 1})
	l.PushBack(Item{"world", 2})
	for e := l.Back(); e != nil; e = e.Prev() {
		fmt.Println(e.Value)
	}
}

etc

2차원 배열 초기화

1
2
3
4
5
connected := make([][] bool, n)
for i := 0; i < n; i++ {
  connected[i] = make([]bool, n)
}

실행 환경 버전 확인

1
2
3
4
5
6
7
8
import (
  "fmt"
  "runtime"
)

func main() {
  fmt.Printf("The application was built with the Go version: %s\n", runtime.Version())
}
This post is licensed under CC BY 4.0 by the author.