質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
VBA

VBAはオブジェクト指向プログラミング言語のひとつで、マクロを作成によりExcelなどのOffice業務を自動化することができます。

アルゴリズム

アルゴリズムとは、定められた目的を達成するために、プログラムの理論的な動作を定義するものです。

Q&A

2回答

6396閲覧

VBAによる木構造への要素の挿入方法

teratailist

総合スコア6

VBA

VBAはオブジェクト指向プログラミング言語のひとつで、マクロを作成によりExcelなどのOffice業務を自動化することができます。

アルゴリズム

アルゴリズムとは、定められた目的を達成するために、プログラムの理論的な動作を定義するものです。

0グッド

0クリップ

投稿2018/05/10 02:42

編集2018/05/18 00:24

ある木構造

図1.

idparent_iddepth
11
21
312
422
533
633
743
854
954
1064
1164

に対して、

depthの変わり目に適当な要素を挿入できるものとします。

挿入する要素のidを通し番号、parent_idを1つ上のparent_idと同じにするというルールの下、

適当な要素を挿入すると、

図2.

idparent_iddepth
11
21
31
41
512
622
722
822
953
1053
1163
1294
1394
14104
15104

のような木構造が得られます。(太字は挿入した要素)

以上のルールの下で、図1の配列から図2の配列を作成する方法がわからなくて困っています。

考え方のヒントで良いので教えていただきたいです。

よろしくお願いいたします。

VBA

1Option Explicit 2 3Sub main() 4 Dim tree() As String ' 図1.の木構造配列格納用変数 5 Dim expand_tree() As String ' 図2.の木構造配列格納用変数 6 7 ' 変数treeの初期化 8 Call Initialize(tree) 9 10 ' 処理を記述 11 12 ' 図2.の配列をexpand_treeに取得 13 14End Sub 15 16Sub Initialize(ByRef tree() As String) 17 Dim tmp() As String 18 Dim i As Integer 19 20 ReDim tree(1 To 11, 1 To 3) 21 ReDim tmp(0 To 2) 22 23 For i = 1 To 3 24 tmp() = Split("1,,1", ",") 25 tree(1, i) = tmp(i - 1) 26 Next i 27 28 For i = 1 To 3 29 tmp() = Split("2,,1", ",") 30 tree(2, i) = tmp(i - 1) 31 Next i 32 33 For i = 1 To 3 34 tmp() = Split("3,1,2", ",") 35 tree(3, i) = tmp(i - 1) 36 Next i 37 38 For i = 1 To 3 39 tmp() = Split("4,2,2", ",") 40 tree(4, i) = tmp(i - 1) 41 Next i 42 43 For i = 1 To 3 44 tmp() = Split("5,3,3", ",") 45 tree(5, i) = tmp(i - 1) 46 Next i 47 48 For i = 1 To 3 49 tmp() = Split("6,3,3", ",") 50 tree(6, i) = tmp(i - 1) 51 Next i 52 53 For i = 1 To 3 54 tmp() = Split("7,4,3", ",") 55 tree(7, i) = tmp(i - 1) 56 Next i 57 58 For i = 1 To 3 59 tmp() = Split("8,5,4", ",") 60 tree(8, i) = tmp(i - 1) 61 Next i 62 63 For i = 1 To 3 64 tmp() = Split("9,5,4", ",") 65 tree(9, i) = tmp(i - 1) 66 Next i 67 68 For i = 1 To 3 69 tmp() = Split("10,6,4", ",") 70 tree(10, i) = tmp(i - 1) 71 Next i 72 73 For i = 1 To 3 74 tmp() = Split("11,6,4", ",") 75 tree(11, i) = tmp(i - 1) 76 Next i 77 78End Sub

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

can110

2018/05/17 10:13

挿入後の末尾4行のparent_idは10,10,11,11ではなく9,9,10,10ではないでしょうか?
teratailist

2018/05/18 00:26

確認したところ、指摘されたとおりでした。修正しておきました。
guest

回答2

0

挿入位置の特定と、既存要素のID、親IDをずらす処理が肝になるかと思います。
Pythonで書いてみました。参考まで。

Python

1# ツリーに要素を挿入 2def add_tree( tree, depth, parent_id = 0, name = ''): 3 4 # 挿入位置=指定レベルの末尾 を末尾から探す 5 TREE_LEN = len(tree) 6 ins_pos = TREE_LEN 7 for i in range(TREE_LEN): 8 idx = TREE_LEN - i - 1 9 node = tree[idx] 10 if node[2] == depth: 11 ins_pos = idx + 1 12 13 # 親IDの指定がなければこの要素と同じ親にする 14 if parent_id <= 0: 15 parent_id = node[1] 16 break 17 18 ins_id = ins_pos+1 19 20 # 挿入要素以降の自身IDと親IDを1つずらす 21 for i in range( ins_pos, TREE_LEN): 22 tree[i][0] += 1 # 自身ID 23 24 # 追加ID以上の親IDも 25 if tree[i][1] >= ins_id: 26 tree[i][1] += 1 27 28 # 要素を挿入 29 tree.insert(ins_pos, [ ins_id, parent_id, depth, name]) 30 31 32# 指定IDの要素を返す 33def get_node( tree, id): 34 for node in tree: 35 if node[0] == id: 36 return node 37 return None 38 39 40# ツリー内容を表示 41def print_tree( tree): 42 print('自ID\t親ID\t深さ\t名前(親の名前)') 43 for node in tree: 44 parent_name = '' 45 parent = get_node( tree, node[1]) 46 if parent: 47 parent_name = parent[3] 48 print( '{}\t{}\t{}\t{}({})'.format( node[0], node[1], node[2], node[3], parent_name)) 49 50 51 52tree = [] 53 54# 初期化 55add_tree( tree, 1, 0, 'a') 56add_tree( tree, 1, 0, 'b') 57add_tree( tree, 2, 1, 'c') 58add_tree( tree, 2, 2, 'd') 59add_tree( tree, 3, 3, 'e') 60add_tree( tree, 3, 3, 'f') 61add_tree( tree, 3, 4, 'g') 62add_tree( tree, 4, 5, 'h') 63add_tree( tree, 4, 5, 'i') 64add_tree( tree, 4, 6, 'j') 65add_tree( tree, 4, 6, 'k') 66 67print('挿入前') 68print_tree( tree) 69 70add_tree( tree, 1, 0, 'add_a') 71add_tree( tree, 1, 0, 'add_b') 72add_tree( tree, 2, 0, 'add_c') 73add_tree( tree, 2, 0, 'add_d') 74 75print('挿入後') 76print_tree( tree)

結果

PlainText

1挿入前 2自ID 親ID 深さ 名前(親の名前) 31 0 1 a() 42 0 1 b() 53 1 2 c(a) 64 2 2 d(b) 75 3 3 e(c) 86 3 3 f(c) 97 4 3 g(d) 108 5 4 h(e) 119 5 4 i(e) 1210 6 4 j(f) 1311 6 4 k(f) 14挿入後 15自ID 親ID 深さ 名前(親の名前) 161 0 1 a() 172 0 1 b() 183 0 1 add_a() 194 0 1 add_b() 205 1 2 c(a) 216 2 2 d(b) 227 2 2 add_c(b) 238 2 2 add_d(b) 249 5 3 e(c) 2510 5 3 f(c) 2611 6 3 g(d) 2712 9 4 h(e) 2813 9 4 i(e) 2914 10 4 j(f) 3015 10 4 k(f)

Python Tutorによる動作の様子(1000ステップまで)

投稿2018/05/17 10:09

編集2018/05/22 01:38
can110

総合スコア38266

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

teratailist

2018/05/18 07:02

ありがとうございます。 まさに、欲しい結果です。 コードを解読してVBAに変換してみましたが、まだエラーだらけです。 エラーなくなり次第、ご提示します。
teratailist

2018/05/22 01:18

お世話になっております。 pythonコードをVBAに変換する作業で苦戦しているのですが、 add_tree関数内、tree.insertの直前で tree[i][0]、tree[i][1] にはそれぞれどのような値が格納されているのでしょうか。 教えていただきたいです。 よろしくお願いいたします。
can110

2018/05/22 01:39

0から、自ID、親ID、深さ、名前です。 動作の様子が見れるリンクを回答に追加しましたので 詳細動作についてはリンク先を確認、参照ください。
teratailist

2018/05/22 05:14

詳細動作についてもご提示いただきありがとうございます。 お陰様で、tree.insertの直前までは欲しい結果を得られるようになりました。 tree.insertの処理を現在書いておりますが、 VBAで用意されているプロシージャでは、これに相当するプロシージャはありませんよね?
guest

0

1件追加するサブ関数を書いてみました。
木構造だとかは全く意識してません。
ルールに従って配列にデータを挿入しているだけです。
質問にあるように、数件追加してから、expand_treeを得たいというのとは動作が異なります。
(数件追加する場合の処理手順がわからなかったので省きました)
参考程度にしてください。

VBA

1Sub main() 2 Dim tree() As String ' 図1.の木構造配列格納用変数 3 Dim expand_tree() As String ' 図2.の木構造配列格納用変数 4 5 ' 変数treeの初期化 6 Call Initialize(tree) 7 8 ' 1件追加 9 Call Add(tree, expand_tree, "2") 10 11End Sub 12 13Sub Add(ByRef tree() As String, ByRef extree() As String, depth As String) 14 ReDim extree(1 To UBound(tree, 1) + 1, 1 To UBound(tree, 2)) 15 Dim ti As Integer 16 Dim eti As Integer 17 eti = 1 18 For ti = 1 To UBound(tree, 1) 19 If depth < tree(ti, 3) And ti = eti Then 20 extree(eti, 1) = CStr(eti) 21 extree(eti, 2) = tree(ti - 1, 2) 22 extree(eti, 3) = depth 23 eti = eti + 1 24 End If 25 extree(eti, 1) = CStr(eti) 26 extree(eti, 2) = tree(ti, 2) 27 extree(eti, 3) = tree(ti, 3) 28 eti = eti + 1 29 Next 30End Sub 31

最終行の処理が足りていませんでしたので修正してみました。
処理済みかどうかの判定をフラグに変更しました。
なんか無駄が多いですが、基本的な作りとしてはこんな感じかと思います。
配列に代入する3行を関数化したりすれば多少は見やすくなりますかね。
そこらへんはご自分で調整願います。

VBA

1Sub Add(ByRef tree() As String, ByRef extree() As String, depth As String) 2 ReDim extree(1 To UBound(tree, 1) + 1, 1 To UBound(tree, 2)) 3 Dim ti As Integer 4 Dim eti As Integer 5 Dim f As Boolean 6 eti = 1 7 f = False 8 For ti = 1 To UBound(tree, 1) 9 If depth < tree(ti, 3) And f = False Then 10 extree(eti, 1) = CStr(eti) 11 extree(eti, 2) = tree(ti - 1, 2) 12 extree(eti, 3) = depth 13 eti = eti + 1 14 f = True 15 End If 16 extree(eti, 1) = CStr(eti) 17 extree(eti, 2) = tree(ti, 2) 18 extree(eti, 3) = tree(ti, 3) 19 eti = eti + 1 20 Next 21 If f = False Then 22 extree(eti, 1) = CStr(eti) 23 extree(eti, 2) = tree(ti - 1, 2) 24 extree(eti, 3) = depth 25 End If 26End Sub

投稿2018/05/15 07:30

編集2018/05/17 01:46
ttyp03

総合スコア16998

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

teratailist

2018/05/17 01:33

ご回答いただきありがとうございます。 ご提示いただいたプログラムを参考に自分のコードに組み込んでおりました。 そこで、問題が生じました。 こちらから示した例が不足していたのですが、 depthが最終番号のとき、要素がうまく追加されず考え中です。 今回の例だと、 main関数で Call Add(tree, expand_tree, "4") を使用したとき、 expand_tree(12)の要素が全て""になってしまいます。 私の方でも考え中ではありますが、 原因が分かり次第、ご助言いただければ助かります。
ttyp03

2018/05/17 01:46

最終行の処理が足りてませんでした。考慮不足です。 修正版を追記してみましたのでご確認ください。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問