2 5 13
This presentation is the property of its rightful owner.
Sponsored Links
1 / 43

アルゴリズムとデータ構造 第 2 章 リスト構造 5 月 13 日分 PowerPoint PPT Presentation


  • 129 Views
  • Uploaded on
  • Presentation posted in: General

アルゴリズムとデータ構造 第 2 章 リスト構造 5 月 13 日分. 情報知能学科 白井英俊. 復習:クラスとインスタンス. Ruby のオブジェクトには  数、文字列、 配列 、 … などがあった これは、プログラミング言語としてはかなり豊か これらを使いこなせば、いろいろなことができる。でも …. 問題に適したデータ構造を考えないといけないことがある。 そのために、 クラス定義 と インスタンス生成 の方法は知っておかないといけない (本講義では これから多用します). 復習: Ruby におけるクラス定義. オブジェクト指向

Download Presentation

アルゴリズムとデータ構造 第 2 章 リスト構造 5 月 13 日分

An Image/Link below is provided (as is) to download presentation

Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author.While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server.


- - - - - - - - - - - - - - - - - - - - - - - - - - E N D - - - - - - - - - - - - - - - - - - - - - - - - - -

Presentation Transcript


2 5 13

アルゴリズムとデータ構造第2章 リスト構造5月13日分

情報知能学科

白井英俊


2 5 13

復習:クラスとインスタンス

Rubyのオブジェクトには

 数、文字列、配列、…などがあった

これは、プログラミング言語としてはかなり豊か

これらを使いこなせば、いろいろなことができる。でも…

問題に適したデータ構造を考えないといけないことがある。

そのために、クラス定義とインスタンス生成の方法は知っておかないといけない

(本講義では これから多用します)


2 5 13

復習:Rubyにおけるクラス定義

オブジェクト指向

プログラムが扱うデータ(オブジェクト)と、それをどのように扱うかという手続き(関数)とを「組」として表す

クラスーオブジェクトの「種類」

クラス定義

1) どのようなパーツ(部品)があるかを宣言:インスタンス変数

2) 手続き(関数)の宣言:インスタンスメソッド


2 5 13

復習:クラス定義の例(続)

Item

大文字から始める

Initialize は重要なインスタンスメソッド。「インスタンス」作成の時に呼び出される

@つきの変数が「インスタンス変数」

これが「パーツ(部品)名」になる

  • 右に図示した名簿の項目(Item)に対応した「クラス」:

    class Item

    def initialize(n,a,b,s)

    @name = n

    @address = a

    @birth_date = b

    @sex = s

    @age = Time.now.year - b

    end

    end


2 5 13

復習:クラス定義の例(続)

Item

  • attr_accessor につづけて

  • インスタンス変数名の前にコロン( : )

ただ、このままだと、部品にアクセス(取り出し、参照)しにくいので…

class Item

def initialize(n,a,b,s)

@name = n

@address = a

@birth_date = b

@sex = s

@age = Time.now.year - b

end

attr_accessor :name, :address, :birth_date, :sex, :age

end


2 5 13

復習:インスタンス生成

これだけでは、鯛焼きは食べられない…

鯛焼きを食べるには、「材料を用意して、鯛焼きを作る」必要がある。

それをするのが、「インスタンス生成」

「Taro Yamada」の名簿項目を作る:

Item.new(“Taro Yamada”, “Toyota”, 1990, “male”)

注:ここでは、生年月日ではなく「生まれた年(西暦)」としよう

  • 鯛焼きに例えて言えば、

    クラス定義は、鯛焼きを焼く金型(鉄板)


2 5 13

復習:演習1:クラスとインスタンス

演習1.1.Itemクラス定義を用いて、以下の例を参考に、自分に当てはめて名簿項目(Itemインスタンス)を生成せよ(注意:女性の場合、性別は”female”)

mydata = Item.new(“Taro Yamada”, “Toyota”, 1990, “male”)

演習1.2.上を実行後 mydata.age の値を表示させよ。

演習1.3. さらに mydata.age = 30 を実行させた後の mydata.birth_date と mydata.age の値を表示させよ。

演習の狙い:クラス定義に従って、パーツ(部品)をもったインスタンスが生成されることを確認。また、インスタンス変数にアクセスしたり、その値を書き換えることができることも確認。

mydata.age という「アクセス方法」が作れるのは、attr_accessor宣言のおかげ。


2 5 13

復習:演習2:クラスとインスタンス

演習2:

(1) 複素数を表すクラスを定義せよ。そのクラスの名前を Complexとし、パーツは二つ、real (reと略す、実部) と imaginary (imと略す、虚部)。

(2) 補助資料の例にならって、複素数同士の引き算と割り算をするメソッドを付け加えよ。

(3) インスタンスを2つ以上つくり、計算がちゃんと行われることを確かめよ。


2 5 13

複素数について

  • 2つの複素数を (a+bi), (c+di)とすると(a,b,c,dは実数, iは虚数)

    足し算: (a+bi) + (c+di) = (a+c) + (b+d)i

    引き算: (a+bi) - (c+di) = (a-c) + (b-d)i

    掛け算: (a+bi) * (c+di) = (a*c - b*d)+(b*c + a*d)i

    割り算: (a+bi) / (c+di) = (a*c + b*d)+(b*c - a*d)i

    c2 + d2 c2 + d2

    参考:

    (a+bi) (a+bi) (c - di) (a*c + b*d) (b*c -a*d)i

    (c+di) (c+di) (c - di) c2+ d2c2+ d2

(c –di)は(c+di)の共役複素数

*

+


2 5 13

復習:演習2:複素数

class Complex

def initialize(x,y)

end # initialize

attr_accessor :re, :im

end # class

  • 複素数を表すクラスを定義せよ。

    そのクラスの名前を Complexとし、パーツは二つ、real (reと略す、実部) と imaginary (imと略す、虚部)

    (2) 補助資料の例にならって、複素数同士の引き算と割り算をするメソッドを付け加えよ。

@re = x

@im = y

インスタンス変数の前に@をつける!

class Complex

def tasu(z) # 複素数同士の足し算

return Complex.new(@re + z.re, @im + z.im)

end

def hiku(z) # 複素数同士の引き算

end

end

return Complex.new(@re - z.re, @im - z.im)


2 5 13

復習:演習2:複素数

(3) インスタンスを2つ以上つくり、計算がちゃんと行われることを確かめよ

a = Complex.new(1,-1)

b = Complex.new(3,2)

#

print a.show, " + ", b.show, " = “; print (a.tasu(b)).show,"\n“

=> 4.0 + 1.0 i

print a.show, " - ", b.show, " = “; print (a.hiku(b)).show,"\n"

=> -2.0 - 3.0 i

print a.show, " * ", b.show, " = “; print (a.kakeru(b)).show,"\n“

=> 5.0 – 1.0 i

print a.show, " / ", b.show, " = “; print (a.waru(b)).show,"\n“

=> 0.077 + 0.385 i


2 5 13

クラス定義とインスタンス生成(まとめ)

クラス名は大文字

class Complex # 複素数のクラス

def initialize(x,y) # インスタンス生成メソッド

@re = x

@im = y

end # initialize

attr_accessor :re, :im# インスタンス変数へのアクセス

#

def tasu(z) # 複素数同士の足し算

return Complex.new(@re + z.re, @im + z.im)

end

def hiku(z) # 複素数同士の引き算

return Complex.new(@re - z.re, @im - z.im)

end#

end # class

インスタンスを作る時のメソッド

生成の例: Complex.new(1,2)

インスタンス変数は@つき

attr_accessor につづけて

インスタンス変数の前にコロン( : )

インスタンス固有のメソッドをクラス定義の中に書く。インスタンス変数を参照・書き換え可能


Break

ちょっとbreak

  • さて、これでクラス定義とインスタンス変数の復習ができた。

  • これから本格的にデータ構造を考えていく。

  • 最初は「リスト構造」。これはCell(セル) という、「ポインタ」をパーツにもつ最小のデータ構造をつなぎあわせて作る。

  • ここで、クラスが活躍する

  • ここまで質問は?


2 5 13

リスト構造の必要性とポインタ

名簿のような「表」を記憶することを考える

  普通の方法: 1行分記憶するのに必要な記憶の大きさをkバイトとして、コンピュータ上に固定した記憶場所を確保して使う

0

1行k バイト

k

2k


2 5 13

表方式の特徴:検索が容易

n件目のデータは、n*k~(n+1)*k-1の範囲に入っている

表方式の欠点:データの削除や挿入の手間が大きい

例:1番目のデータを削除したら、その後のデータをすべて一行ずつ前に移動しなければならない

リスト構造の必要性とポインタ(続)


2 5 13

リスト構造の必要性とポインタ(続)

リスト(線形リスト)構造:表方式の欠点を解消

データの挿入、削除の手間が軽減

ただし、記憶場所が多く必要、データの検索(アクセス)の手間がかかる、という欠点あり

  • リスト(線形リスト)構造の基本単位:セル(Cell)

data

データ部

next

ポインタ

データ部: データの記憶用

ポインタ部:別なセルのアドレス


2 5 13

セル(Cell)

線形リスト構造を表現する基本単位 

例えて言えば、連結器つきの車両

つながったセル(Cell)の図


2 5 13

リスト構造(教科書p.16)

  • リスト構造

    ポインタを持つデータ構造

以下のような Cell (セル)クラスを考え、線形リスト構造を作る:

クラス (Rubyのクラス)

概念図

Cell

data

データ部

next

ポインタ

class Cell

definitialize(x,y)

@data = x

@next = y

end

end


2 5 13

線形リストの例

data

next

data

next

青木

石川

ここには、隣のセルへのポインタ(アドレス)が入っている

この印は、このセルに続くセルがないことを意味

b = Cell.new(石川, nil )

a = Cell.new(青木, b)

nilは、このセルに続くセルがないことを意味

これは次のように書いてよい:

a = Cell.new(青木, Cell.new(石川, nil ))


2 5 13

Cell について

x = Cell.new(“a”, Cell.new(“b”, Cell.new(“c”,nil)))

により作成された連結リストは:

x

data

next

data

next

data

next

“a”

“b”

“c”


2 5 13

クラスCellにメソッドを追加する

  • このままだと単にCellの構造(どんなパーツがあるか)を定義しただけ

  • Cellをつないだり、後続のCellにアクセスしたり、書き替えたりできるよう、メソッドを追加する

クラス (Rubyのクラス)

概念図

Cell

data

データ部

next

ポインタ

class Cell

definitialize(x,y)

@data = x

@next = y

end

attr_accessor :data, :next

end


2 5 13

クラスCellにメソッドを追加する(続)

Cellに3つのインスタンスメソッドを付け加える:

  • get(n) : 先頭のセルから数えてn番目のセルを返す(get)

  • insert(n, content) : 先頭のセルから数えて n番目のセルの前に、contentをデータ部にもつセルを挿入(insert)する

  • delete(n) : 先頭のセルから数えてn番目のセルを削除する(delete)


Cell get

クラスCellにgetメソッドを追加

  • get(n) : 先頭のセルから数えてn番目のセルを返す(get)

    例: x.get(1) =>

data

next

要するに x.nextと同じ

“b”

data

next

data

next

data

next

x

“a”

“b”

“c”

x.get(0) は x を返す。それでは x.get(2) は?


Cell get1

クラスCellにgetメソッドを追加

class Cell

definitialize(x,y)

@data = x

@next = y

end # def

attr_accessor :data, :next

def get(n)

return self if (n <= 0)

if (@next != nil)

return @next.get(n-1)

else

return nil

end # if

end #def

end # class

既に定義済み

self は「自分自身」を指すポインタ


Cell get2

クラスCellのgetメソッドの検証

c = Cell.new(“c”, nil); b = Cell.new("b", c) ; x = Cell.new("a", b)

# getの検証

p x.get(0)

p x.get(1)

p x.get(2)

p x.get(3)

class Cell

def get(n)

return self if (n <= 0)

if (@next != nil)

return @next.get(n-1)

else

return nil

end # if

end #def

end

n = 0

x (= self)

next

n = 1

b (= b.get(0))

= b.get(1)

= c.get(0) =c

data

next

data

next

data

next

“a”

“b”

“c”

x

c

b


Cell insert

クラスCellにinsertメソッドを追加

  • insert(n, content) : 先頭のセルから数えて n番目のセルの前に、contentをデータ部にもつセルを挿入(insert)する

data

next

data

next

data

next

“a”

“b”

“c”

x

この時、x.insert(2,”d”)を実行すると

data

next

“d”

となるようにできればよい


Cell insert1

クラスCellにinsertメソッドを追加(続)

class Cell

definitialize(x,y)

@data = x

@next = y

end # def

attr_accessor :data, :next

def get(n) …. end

def insert(n, cont)

if (n == 1)

@next = Cell.new(cont, @next)

else # n > 1 と仮定

@next.insert(n - 1, cont)

end # if

end #def

end # class

既に定義済み

ここにはちょっと問題がある

値のチェックをすることが必要。


Cell insert2

クラスCellのinsertメソッドの検証

c = Cell.new(“c”, nil); b = Cell.new("b", c) ; x = Cell.new("a", b)

x.insert(2, “d”)

def insert(n, cont)

if (n == 1)

@next = Cell.new(cont, @next)

else # n > 1 と仮定

@next.insert(n - 1, cont)

end # if

end #def

n =2

“d”

next

next

n =1

b.insert(1,”d”)

“d”

next

data

next

data

next

data

next

data

next

“a”

“b”

“c”

x


Insert

insertメソッドの動き(続)

先の結果を書き直すと

x.insert(2,”d”)の結果は:

x

“a”

“d”

“c”

“b”

x から見て2番目のセルの後ろに、新しいセルが挿入され、元のリストの残りの要素がそれに繋がっている


Insert1

insertメソッドの問題

  • このままだと先頭のセルの「前に」 contentをデータ部にもつセルを挿入(insert)することができない --- 何とかならないのだろうか?

def insert(n, cont)

if (n == 0) # n = 0 の場合、セルの先頭に挿入

y = Cell.new(@data,@next)

@data , @next = cont, y

elsif (n == 1)

@next = Cell.new(cont, @next)

else # n > 1 と仮定

@next.insert(n - 1, cont)

end # if

end #def

if (n == 1)


Insert2

リストの先頭に insert

data

next

data

next

data

next

a

“a”

“a”

“h”

“b”

“c”

このとき、insert(1,”h”) をすると、どうなる?

data

next

該当するプログラム:

y = Cell.new(@data,@next)

@data, @next = cont, y

y

cont


Break1

ちょっとbreak…

  • Cell、get メソッドとinsertメソッドについて、質問はありますか?


Cell delete

クラスCellにdeleteメソッドを追加

  • delete(n) : 先頭のセルから数えてn番目のセルを削除する(delete)

data

data

data

data

next

next

next

next

変数aの値が以下のような連結リストへのポインタとする:

x

“a”

“b”

“d”

“c”

x.delete(2) を実行すると

となるようにできればよい


2 5 13

deleteメソッドの動き

先の結果を書き直すと x.delete(2)の結果は:

x

“a”

“b”

“c”

“d”

元のリストの3番目が2番目のセルになる。

元の2番目の要素は消えたわけではない。

が、リストの先頭からはアクセス(接近)不能=削除!


Cell delete1

クラスCellにdeleteメソッドを追加

class Cell

definitialize(x,y)

@data = x

@next = y

end # def

attr_accessor :data, :next

def get(n) …. end

def insert(n,cont) … end

def delete(n)

if (n == 1)

@next = @next.next

else # n > 1 と仮定

@next.delete(n - 1)

end # if

end #def

end # class

既に定義済み

ここにはちょっと問題がある

値のチェックをすることが必要。


Cell delete2

クラスCellのdeleteメソッドの検証

c = Cell.new(“c”, nil); d = Cell.new(“d”, c)

b = Cell.new("b", c) ; x = Cell.new("a", b)

data

data

next

next

x.delete(2)

def delete(n)

if (n == 1)

@next = @next.next

else # n > 1 と仮定

@next.delete(n - 1)

end # if

end #def

n =2

n =1

b.delete(1)

data

next

data

next

x

“a”

“b”

“d”

“c”

d

c


Delete

deleteメソッドの問題

  • このままだと先頭のセル削除(delete)することができない --- 何とかならないのだろうか?

def delete(n)

if (n == 0) # n = 0 の場合、セルの先頭に挿入

@data = @next.data

@next = @next.next

elsif (n == 1)

@next = @next.next

else # n > 1 と仮定

@next.delete(n - 1)

end # if

end #def

if (n == 1)


Delete1

先頭の要素をdelete

data

data

next

next

@data

@next

x

“a”

“b”

“b”

“c”

該当するプログラム:

@data = @next.data

@next = @next.next

x.delete(0) を実行すると

リストの2番目の要素を1番目のセルにコピー

元の2番目の要素はリストの先頭からはaccess不能

つまり、『1番目のセルではなく2番目のセルを削除』


2 5 13

プログラミング演習

クラスCellに次のようなインスタンスメソッドを付け加える:

  • find(data) : 先頭のセルから順にたどり、dataをデータ部に持つセルを返す

  • remove(data) : 先頭のセルから順にたどっていき、最初にdataをデータ部に持つセルを削除する(remove)


2 5 13

リストの実現のための別な方法

  • 今見てきた方法だと、1番目の要素の削除や、1番目に要素を挿入するのは、かなり技巧的

  • 以下の方法は、その点、分かりやすい方法

     ⇒ 先頭にダミー(データ記憶には使わない)セルを置く

今までのリスト:

x

“a”

“b”

新方法のリスト:

x

“a”

“b”


2 5 13

リストの実現のための別な方法(続)

  • ダミーセルの next の値を書き換えることで、

    次を実現するプログラムが容易に書ける

    (1) 1番目のセルとして新たな要素を挿入

    dummy.next =

    Cell.new(新たな要素,dummy.next)

    (2) 1番目のセルを削除

    dummy.next = dummy.next.next


2 5 13

リストの実現のための別な方法(続)

  • この方式による新たなリスト構造のためのデータ構造(クラス)をNCellとする

    class NCell

    def initialize(data)

    @contents = Cell.new(nil, data)

    end

    attr_accessor :contents

    end

     使用例:

c = Cell.new(“c”, nil);b = Cell.new("b", c)

a = Cell.new("a", b) ; x = NCell.new(a)


2 5 13

発展問題

このように定義したNCellクラスに、3つのインスタンスメソッドを付け加える:

  • get(n) : 先頭のセルから数えてn番目のセルを返す。ただし、n =0の時は先頭のセルを返す。

  • insert(n, content) : 先頭のセルから数えて n番目のセルの前に、contentをデータ部にもつセルを挿入する。n=0の時は先頭のセルの「前に」挿入する。

  • delete(n) : 先頭のセルから数えてn番目のセルを削除する。n=0の時は、先頭のセルを削除する。


  • Login