バックトラッキングの実装

バックトラッキングアルゴリズムを実装で学ぶ。制約満たし探索、重複なし組み合わせ、再帰処理、木構造による考え方でGoの実装例から理解を深める実践ガイドです。

Read in: en
バックトラッキングの実装

バックトラッキングとは

指定された制約を満たすような組み合わせを探索するアルゴリズム。 

重複しない組み合わせ(nCr)を全て探索するようなときに使える。

実装

ソースコードは以下。

backtrack

与えられた配列からN個の重複しないサブシーケンスを取得する処理。 以下は4C3の例。

package main

import "fmt"

func backtrack(rslt *[][]int, tmp []int, items []int, start int, k int) {
	if k == 0 {
		combination := make([]int, len(tmp))
		copy(combination, tmp)
		*rslt = append(*rslt, combination)
		return
	}

	for i := start; i < len(items); i++ {
		tmp = append(tmp, items[i])
		backtrack(rslt, tmp, items, i+1, k-1)
		tmp = tmp[:len(tmp)-1]
	}
}

func get(items []int, k int) [][]int {
	rslt := [][]int{}
	tmp := []int{}
	backtrack(&rslt, tmp, items, 0, k)
	return rslt
}

func main() {
	items := []int{1, 2, 3, 4}
	k := 3
	combination := get(items, k)
	fmt.Println(combination)
}
// 出力
[[1 2 3] [1 2 4] [1 3 4] [2 3 4]]

再帰処理のところで脳内メモリがパンクしそうになるが、データを木構造にして考えると分かりやすい。

1, 2, 3, 4からなるデータが渡されるとき、kが0~4でそれぞれどういう結果が得られるか考えてみる。

k = 0
N/A

k = 1
1  2  3  4

k = 2

	   1      2    3
	 / | \   / \   |
	2  3  4 3   4  4

k = 3

	    1     2
	   / \    |
	  2   3   3
	 / \  |   |
	3  4  4   4

k = 4
1
2
3
4

要するに、kは木構造の深さで、4C3の3は深さであるといえる。

組み合わせをバックトラッキングを使わずに愚直に解こうとすると、for文の多重ループになってしまう。

それを再帰処理で上手く書くアプローチの一つがバックトラッキングだったりする。

leetcode.com - subsetsはいくつか解法がある問題だが、バックトラッキングで解くことができる。

参考

Tags: バックトラック
Share: 𝕏 Post Facebook Hatena
✏️ View source / Discuss on GitHub
☕ サポート

このブログを応援していただける方は、以下からサポートをお願いします。いただいたサポートはブログ運営・技術研鑽に活用します。


関連記事