解説 5月10日B

今回の学習項目です。


木構造

教科書の2.2節の19ページ目下から5行目から、21ページ目まで読んでください。

教科書の2.2節の内容としては以下が重要です。

木構造は、おそらく離散数学などで学んだ概念だと思います。 この木構造は、アルゴリズムの設計でとても重要な役割を果たしています。 ここでは木構造のことを「根つき木」と呼んでいますが、単に「木」と呼んでもよいものです。

Fig 2.8

(根つき)木とは、図2.8に示されるような、特殊なグラフです。 ここで、グラフとは、頂点(「ノード」ともいう)と、頂点と頂点を結ぶ辺(「枝」ともいう)とから構成されているものです。 ただ、辺は頂点をその端に持たなければならない(ただし、同じ頂点であってもよい)という制約があります。なお、頂点にはそういう制約はありません。辺によって結ばれていない頂点があっても構いません。そのようなものがグラフです。

木は「根」と呼ばれる特別な頂点を持ち、すべての頂点が辺で結ばれるという特殊なグラフです。さらに、根からはいくつかの辺を経由してどの頂点にもたどっていけることと、さらにそのような根から頂点へのたどり方は必ず一通りに限ること(つまり、たどる方法が複数通りはないこと)という制限があります。

こういうとややこしそうですが、図2.8を上下逆さにしてみてください。 こうすれば「根」が図の下になります。そして本当の木のように(もしくは草のように)見えませんか?根から上へ上へと枝が伸びているようにみえませんか? これが「木」と呼ばれる理由です。このイメージをもっていれば、どんなグラフでも木か、そうでないかは容易に判定できます。

Terms of Tree structure

図2.8をもとに説明します。この図では根が一番上にあります。 そして他の頂点はその根の下にあります。これを家系図にたとえてみます。 根がご先祖様です。根の下にあり、根から辺一本で結ばれている頂点は、いわば根の「子」です。逆に言えば、これらの頂点からみると根は「親」にあたります。

図2.8において根の「子」に名前をつけてみます。左から順にv1, v2, v3とします。 (ここでvという記号をつかったのは、頂点の英語名から来ています。さて、頂点は英語では何と呼ばれているでしょう?) すると、v1, v2, v3は同じ親をもつ子ですから、これらは「兄弟」(「姉妹」ともいう)という関係にあります。

なお、v1にもv2にもv3にも子の頂点がいます。例えばv1の子からみると根は「祖父(grandparent)」にあたります。逆に根から見るとv1の子は「孫(grandson)」にあたります。すぐわかるように、根以外のどの頂点にも「親」がただ一つ存在します。また「祖父」があるとすればこれもただ一つに限ります。

ここでの用語の最後に、「葉」(leaf)を紹介します。木は根から幹や枝が伸び、花や葉となります。木構造では「花」はありませんが、「葉」はあります。葉とは、子を持たない頂点のことをいいます。たとえ根であっても、子がない場合は「葉」と呼ばれます。根であって葉でもある、というのは不思議ですね。でも理論としてはこういうことも可能なのです。

課題1-1

このページの「木の用語」と書かれている図を見て、以下の問に答えなさい。

  1. 根の孫頂点はどれでしょうか。すべて答えなさい。
  2. v7の兄弟頂点はどれでしょうか。すべて答えなさい。
  3. v7の親頂点はどれでしょうか。答えなさい。
  4. v7の祖父頂点はどれでしょうか。答えなさい。
  5. この木の葉はどれでしょうか。すべて答えなさい。
  6. v0の親頂点はどれでしょうか。答えなさい。
[「兄弟」について]
頂点vの「兄弟」頂点とは、同じ親をもつ頂点のこととしました。 すると、vはvの(つまり自分の)兄弟でしょうか? この定義にそえばそのとおり(自分は自分の兄弟)ですが、感覚的には変な感じです。 教科書20ページには、「子が複数あるとき、それらは互いに兄弟と呼ばれる」 (10行目)と書いてあります。つまり、子が一つしかない場合には、その子頂点には兄弟はいないことになります。 そこで本講義では教科書にならい、「自分は自分の兄弟ではない」とします。

[課題1-1(6)のヒント]
もちろん、根の親頂点は存在しません。また、頂点によっては、子や兄弟の頂点がないものもあります。 その場合には、「ありません」と答えてください。

課題1-2

木の頂点が教科書21ページ目の図2.9のように表されているとします。 つまり、頂点は「リスト構造」の一つのセルとして表され、 そのセルには「親」「次の兄弟」「第1子」それぞれの頂点(セル)へのポインタが書かれているのです。 ただし、「親」など該当する頂点がない場合は nilが値となっているとします。

教科書によれば、頂点vの子をすべて列挙するには次の手続きをとればよいとあります。

  x <- FirstChild[v]
  while x != nil do
      report x and x <- NextBrother[x]
これにならって、頂点vの祖先をすべて列挙する手続きを書きなさい。 [教科書に書かれた手続きの解説]

  x <- FirstChild[v]
は、頂点vの「第1子」頂点を取り出し、変数xの値とすることを意味しています。 ここで、頂点vの「第1子」頂点がない場合は、xの値はnilが入ります。
次のwhile文でxの値が「nilでない限り」くり返しが行われます。
  while x != nil do
つまり、「第1子頂点がない」場合は、繰り返しが行われずに終了です。 繰り返される手続きは以下です:
      report x and x <- NextBrother[x]
report xは変数x (つまり頂点が代入されている)が何かをreport(報告)するという命令です。 ここでは頂点のラベルが印刷表示 (ここではprintとしておきます) されると思ってください。 次のandは、その次の x <- NextBrother[x] 命令が続けて実行されることを意味しています。これによって変数xには頂点xの「次の兄弟」頂点が値となります (またもや、そういう頂点がなければnilが代入されます。

Rubyで書くとすれば以下のようにかけるでしょう (ここで、頂点を表すクラスが用意されてあり、vはそのインスタンスが代入されているとしています。 が、このままではもちろん動きません。あくまで感触をつかむためのもの、と思ってください)

  x = v.firstChild
  while x != nil
      p x.label
      x = x.nextBrother
  end # while

[ヒント]
例題では「第1子」頂点から始めて「次の兄弟」を次々変数xにいれていくことで「子」頂点を列挙していました。 この問題では「子」ではなく「親」をあてはめて考えればできます。 つまり、「親」から始めて、その頂点の「親」を次々変数xにいれていくとどうなるでしょう?

課題1-3

課題1-2にならって、頂点vの孫をすべて列挙する手続きを書きなさい。 [ヒント]
課題1-2の例題では「第1子」頂点から始めて「次の兄弟」を次々変数xにいれていくことで「子」頂点を列挙していました。
  x <- FirstChild[v]
  while x != nil do
      report x and x <- NextBrother[x]
ここでは「孫」の列挙が必要です。孫とは「子」の「子」を意味します。 3行目の「くり返し」されている命令を考えましょう。この時点ではxは元の頂点の「子」でしかありません。 さらにその「子」を取り出さなければなりません。そこで3行目を次のように変えてみましょう。
       y <- FirstChild[x]
       while y != nil do
           report y and y <- NextBrother[y]
すると、何がreportされることになるでしょうか?
プログラムを答えるだけではなく、その理由(根拠)も答えるようにしましょう。

課題1-4

木の頂点を表すためのクラスVertexをRubyで次のように表すとします:
   class Vertex   # クラスの定義の始まり
        def initialize(l, p = nil, b = nil, f = nil)  # インスタンスを作るためのメソッド
            @label = l      # label(頂点のラベル)を表す「要素」を定義
            @parent = p        # parent(親)を表す「要素」を定義
            @nextBrother = b   # nextBrother(次の兄弟)を表す「要素」を定義
            @firstChild = f    # firstChild(第1子)を表す「要素」を定義
        end  # of def
        attr_accessor :label,:parent, :firstChild, :nextBrother   # インスタンス変数へのアクセスを可能にする
   end  # of Class
  1. クラスVertexのインスタンスを作り、変数rの値とするコードを書きなさい。ただし、ラベルの値は"v0"とし、 親も第1子も、次の兄弟も存在しない(値がnil)ものとします。
  2. 上のコードの実行後に、次のコードを実行するものとします。
        z = Vertex.new("v3",r)
        y = Vertex.new("v2",r,z)
        x = Vertex.new("v1",r,y)
        r.firstChild = x
    
    これらで表される木はどのようなものでしょうか、答えなさい。
  3. 以上のコードの実行後に、次を実行したとします。その結果はどのようになるか、まず予測し、 そしてプログラムを実行して、その結果と予測と比較しなさい。
      a = r.firstChild
      while a != nil
          print a.label," "
          a = a.nextBrother
      end # while
    
  4. クラスVertexを用いて、 このページの「木の用語」の図に表されている木構造をRubyで表すためのコードを書きなさい。 ここで、変数v0には、根に対応するVertexクラスのインスタンスが代入されているようにしなさい。
  5. 課題1-3で考えた手続きをRubyのプログラムとして実現し、 上の課題によって表現された根v0の孫の頂点がすべて次々と表示されることを確かめなさい。

「アルゴリズムとデータ構造」のホームに戻る