Transcript アルゴリズムとデータ構造1
アルゴリズムとデータ構造
1
2006 年 6 月 23 日 酒居敬一 (
)
http://www.info.kochi-tech.ac.jp/k1sakai/Lecture/ALG/2006/index.html
連結リスト
(29ページ)
• •
データをそれぞれの要素に格納 要素どおし、つながってリストを形成 先頭 データ 次 データ 次 データ 次 データを動かすことなく、要素の挿入・削除ができる
// { リストを形成するためのデータ構造 public class Element private Element() { } public Element(Object aData) { this.data = aData; } public Object getData() { return this.data; } } クラス宣言の中における
•
クラスの自己参照 宣言は未完了だが特別な記述は不要
•
インスタンスの自己参照
this
による参照 public Element getNextElement() { return this.next; } public void setNextElement(Element anNextElement) { this.next = anNextElement; } public String toString() { if(null != this.next){ return (this.data.toString() + " → }else{ return this.data.toString(); "); } } private Object data; private Element next; // クラスの自己参照
自己参照
•
宣言の中で自分自身を参照すること
•
一見すると奇妙な点がある – Java 言語では自分の中に自分を含むように見える – 宣言が完了する前に自分自身を参照していること
•
宣言が未完了では、当然、インスタンスは定義できないはず – C 言語で書くと、ポインタを置いている
•
参照ということは、型が未定義でも大きさは計算可能
/* リストを形成するためのデータ構造 in C language */ struct Element { void *data; struct Element *next; /* 構造体型の自己参照 }; */
先頭
連結リスト操作(挿入)
データ 次 データ 次 データ 次 データ 次 図 2.4.1
・ 2.4.2
教科書 41 ・ 42 ページ 単純な連結リスト(線形リスト)データ挿入
public class MyLinkedList { public MyLinkedList() { this.firstElement = new Element(null); } public boolean insert(Object aData) { return this.insert(1, aData); } private Element firstElement; } 連結リストの宣言 リストへの挿入 public boolean insert(int aPosition, Object aData) { if(1 > aPosition){ System.err.println(" その場所には挿入はできません : " + aPosition); return false; } Element previousElement = this.getElement(aPosition - 1); if(null == previousElement){ System.err.println(" その場所には挿入はできません : " + aPosition); return false; } Element element = new Element(aData); element.setNextElement(previousElement.getNextElement()); previousElement.setNextElement(element); return true; }
public Element getElement(int aPosition) { Element currentElement = this.firstElement; for(int count = 0; count < aPosition; count++){ if(null == currentElement){ return null; } currentElement = currentElement.getNextElement(); } return currentElement; } public int search(Object aData) { int count = 1; Element currentElement = this.firstElement.getNextElement(); while(null != currentElement){ if(currentElement.getData().equals(aData)){ return count; } ++count; currentElement = currentElement.getNextElement(); } return -1; }
public Object get(int aPosition) { Element element = this.getElement(aPosition); if(null == element){ System.err.println(" 指定した場所には要素がありません "); return null; } return element.getData(); } public int size() { int length = 0; Element currentElement = this.firstElement.getNextElement(); while(null != currentElement){ ++length; currentElement = currentElement.getNextElement(); } return length; } public void printAll() { Element currentElement = this.firstElement.getNextElement(); while(null != currentElement){ System.out.print(currentElement); currentElement = currentElement.getNextElement(); } System.out.println(); }
連結リスト操作(削除)
先頭 データ 次 データ 次 データ 次 図 2.4.3
教科書 43 ページ 単純な連結リスト(線形リスト)データ削除
public boolean remove(Object aData) { int position = this.search(aData); return this.remove(position); } リストへの挿入・削除を先頭に限定すれば スタック構造が実現できる public boolean remove(int aPosition) { if(1 > aPosition){ System.err.println
(" その場所は削除できません : " + aPosition); return false; } Element targetElement = this.getElement(aPosition); if(null == targetElement){ System.err.println
(" その場所は削除できません : " + aPosition); return false; } Element previousElement = this.getElement(aPosition - 1); previousElement.setNextElement(targetElement.getNextElement()); targetElement.setNextElement(null); return true; }
例:メモリ割り当て
(37ページ)
最も簡単なメモリ割り当てアルゴリズム ・リストで管理 ・データ領域を分断し、あらたなリスト要素として、挿入 ・リストのヘッダには空きか使用中かのフラグがある ・割当てるデータ領域はリストのヘッダとヘッダの間 使用状況を示すフラグ 次のヘッダを指すポインタ ヘッダは左のような構造 要素としては、フラグとポインタしかない 空き 空き使用中 空きの領域 使用中
空き 空き 空き 空き 使用中 メモリの割り付け・開放を繰り返していくとメモリが分断するようになる ・フラグメンテーションといい、大きな領域を割り当てできなくなる ・そこで、ときおり、空き領域にはさまれたヘッダを削除する メモリ割付け技法
•
連続割付け • 単一連続割付け
•
分割割付け
(
ゾーン割り付け
) •
固定区画割付け
•
可変区画割付け
•
非連続割付け
•
ページング
•
セグメンテーション 管理手法
•
線形リスト
•
ハッシュ表
双方向連結リスト
(39ページ)
•
連結リストを双方にリンクしたもの 先頭 最後 データ 次 前 データ 次 前 データ 次 前 両方向から探索できる
public class DoublyElement { public DoublyElement(Object aData) { this.data = aData; } public Object getData() { return this.data; } public DoublyElement getNextElement() { return this.next; } public DoublyElement getPreviousElement() { return this.previous; } リストを形成する要素として
next
と
previous
が定義されて リストを双方向にたどれるようにしている public void setNextElement (DoublyElement anNextElement) { this.next = anNextElement; } public void setPreviousElement (DoublyElement anPreviousElement) { this.previous = anPreviousElement; } private Object data; private DoublyElement next, previous; }
public class DoublyLinkedList { public DoublyLinkedList() { this.firstElement = new DoublyElement(null); this.lastElement = new DoublyElement(null); this.firstElement.setNextElement(this.lastElement); this.lastElement.setPreviousElement(this.firstElement); } private DoublyElement firstElement; private DoublyElement lastElement; } public int search(Object aData) { int count = 1; DoublyElement currentElement = this.firstElement.getNextElement(); while(currentElement != this.lastElement){ if(currentElement.getData().equals(aData)){ return count; } ++count; currentElement = currentElement.getNextElement(); } return -1; }
public int size() { int length = 0; DoublyElement currentElement = this.firstElement.getNextElement(); while(currentElement != this.lastElement){ ++length; currentElement = currentElement.getNextElement(); } return length; } public Object get(int aPosition) { DoublyElement element = this.getElement(aPosition); if(null == element){ System.err.println(" 指定した場所には要素がありません "); return null; } return element.getData(); } public DoublyElement getElement(int aPosition) { DoublyElement currentElement = this.firstElement; for(int count = 0; count < aPosition; count++){ if(currentElement == this.lastElement){ return null; } currentElement = currentElement.getNextElement(); } return currentElement; }
先頭
双方向連結リスト(挿入) キューへのデータの挿入
データ 次 データ 次 データ 次 最後 前 前 前 データ 次 前 双方向連結リスト データ挿入
•
連結リストを双方にリンクしたもの
public boolean insert(Object aData) { return this.insert(1, aData); } public boolean insert(int aPosition, Object aData) { if(1 > aPosition){ System.err.println
(" その場所には挿入はできません : " + aPosition); return false; } DoublyElement previousElement = this.getElement(aPosition - 1); if(null == previousElement){ System.err.println
(" その場所には挿入はできません : " + aPosition); return false; } DoublyElement element = new DoublyElement(aData); element.setNextElement(previousElement.getNextElement()); previousElement.getNextElement().setPreviousElement(element); previousElement.setNextElement(element); element.setPreviousElement(previousElement); return true; }
双方向連結リスト操作(削除) キューからのデータの取り出し
先頭 最後 データ 次 前 データ 次 前 データ 次 前 双方向連結リスト データ削除
public boolean remove(Object aData) { int position = this.search(aData); return this.remove(position); } public boolean remove(int aPosition) { if(1 > aPosition){ System.err.println
(" その場所は削除できません : " + aPosition); return false; } DoublyElement targetElement = this.getElement(aPosition); if(null == targetElement){ System.err.println
(" その場所は削除できません : " + aPosition); return false; } DoublyElement previousElement = this.getElement(aPosition - 1); previousElement.setNextElement(targetElement.getNextElement()); targetElement.getNextElement().setPreviousElement(previousElement); targetElement.setNextElement(null); targetElement.setPreviousElement(null); return true; }