본문 바로가기

Project & Module

Btree 구현



reference : http://algs4.cs.princeton.edu/code/edu/princeton/cs/algs4/BTree.java.html 




1. Java Source 전체적 설명


* BTree를 Key, Value 로 Generic 선언해줌. 이렇게 선언해주면 Key, Value 라는 것이 T, E 이런걸 의미함.

  즉, 어떤 타입이든 받을 수 있고 그걸로 명시되어서 컴파일시 에러를 잡아내는 것이다.

* public class BTree<Key extends Comparable<Key>, Value> 이렇게 선언함으로써 Key는 Comparable을 상속받는 것이며,

  Comparable을 통해 Compare를 사용할 수 있다. 쉽게 비교가 가능한거지.

* 전체적인 구조를 보면 Node, Entry가 있으며, Node는 Entry를 관리하는 개념. Entry는 Key, Value를 가지고 있는 것이다.

* 삽입의 전체적인 과정은 M (자식 갯수) 만큼 넣다가 자식 갯수를 넘어서면 분할함. 분할하면 높이가 커짐. 

* 검색은 맨 밑에 노드부터 검색해서 위로 올라감. less, j+1 = x.m 를 통해 찾아야할 것을 찾아나감.  





2. Insert 과정

1. www.cs.princeton.edu 가 root 밑에 들어감. m = 1 / children[0] = "www.cs.princeton.edu", "128.112.136.12"

2. www.cs.princeton.edu 가 root 밑에 들어감 m  = 2 / children[1] = "www.cs.princeton.edu", "128.112.136.11"

3. www.princeton.edu 가 root 밑에 들어감 m = 3 / children[2] = "www.princeton.edu", "128.112.128.15"

4. www.yale.edu 가 root 밑에 들어감 m = 4  / children[3] = "www.yale.edu", "130.132.143.21"

   이렇게 들어가는데 우리가 정해둔 M = 4 를 넘어서가지고 분할이 일어난다. root의 자식 노드수는 4/2 = 2가 되고,

   새로 만든 노드 t[0],[1]에  h.children[2], [3]이 들어간다. 그리고 return 하고 root에 이 2개를 자식으로 삼음.

   

5. www.simpsons.com 가 들어오면 층을 잘 비교해서 insert


...

...

...




3. Search 과정


맨 밑에 노드부터 검색하는데 j+1 = x.m || less(key, children[j+1].key)) 인지에 따라 찾는 곳을 계속해서 찾아감.





4. Source


package algorithm.btree;


public class BTree<Key extends Comparable<Key>, Value>

{

// max children per B-tree node = M-1

// (must be even and greater than 2)

private static final int M = 4;


private Node root; // root of the B-tree

private int height; // height of the B-tree

private int n; // number of key-value pairs in the B-tree


// helper B-tree node data type

private static final class Node

{

private int m; // number of children

private Entry [] children = new Entry [M]; // the array of children


// create a node with k children

private Node(int k)

{

m = k;

}

}


// internal nodes: only use key and next

// external nodes: only use key and value

private static class Entry

{

private Comparable key;

private final Object val;

private Node next; // helper field to iterate over array entries


public Entry(Comparable key, Object val, Node next)

{

this.key = key;

this.val = val;

this.next = next;

}

}


/**

* Initializes an empty B-tree.

*/

public BTree()

{

root = new Node(0);

}


/**

* Returns true if this symbol table is empty.

* @return {@code true} if this symbol table is empty; {@code false}

*         otherwise

*/

public boolean isEmpty()

{

return size() == 0;

}


/**

* Returns the number of key-value pairs in this symbol table.

* @return the number of key-value pairs in this symbol table

*/

public int size()

{

return n;

}


/**

* Returns the height of this B-tree (for debugging).

*

* @return the height of this B-tree

*/

public int height()

{

return height;

}


/**

* Returns the value associated with the given key.

*

* @param key

*            the key

* @return the value associated with the given key if the key is in the

*         symbol table and {@code null} if the key is not in the symbol

*         table

* @throws IllegalArgumentException

*             if {@code key} is {@code null}

*/

public Value get(Key key)

{

if (key == null)

throw new IllegalArgumentException("argument to get() is null");

return search(root, key, height);

}


private Value search(Node x, Key key, int ht)

{

Entry [] children = x.children;


// external node

if (ht == 0)

{

for (int j = 0; j < x.m; j++)

{

if (eq(key, children[j].key))

return (Value) children[j].val;

}

}


// internal node

else

{

for (int j = 0; j < x.m; j++)

{

if (j + 1 == x.m || less(key, children[j + 1].key))

return search(children[j].next, key, ht - 1);

}

}

return null;

}


/**

* Inserts the key-value pair into the symbol table, overwriting the old

* value with the new value if the key is already in the symbol table. If

* the value is {@code null}, this effectively deletes the key from the

* symbol table.

*

* @param key

*            the key

* @param val

*            the value

* @throws IllegalArgumentException

*             if {@code key} is {@code null}

*/

public void put(Key key, Value val)

{

if (key == null)

throw new IllegalArgumentException("argument key to put() is null");

Node u = insert(root, key, val, height);

n++;

if (u == null)

return;


// need to split root

/*

*                      Root

*                  | |    

*  root.children[0].key - u.children[0].key

*                  |               |

*        기존 루트 주소                                      새로 만든 것 

*  (children[0], children[1])  (children[2], children[3])

*  

*  

*  이렇게 쪼갠 뒤 height++ 시킴

*/

Node t = new Node(2);

t.children[0] = new Entry(root.children[0].key, null, root);

t.children[1] = new Entry(u.children[0].key, null, u);

root = t;

height++;

}


private Node insert(Node h, Key key, Value val, int ht)

{

int j;

Entry t = new Entry(key, val, null);


// external node

// 처음 들어가면 여기를 타고 들어감.

if (ht == 0)

{

// h.m : 해당 노드의 자식 갯수

for (j = 0; j < h.m; j++)

{

if (less(key, h.children[j].key))

break;

}

}


// internal node

else

{

for (j = 0; j < h.m; j++)

{

// j+1 = h.m 은 해당 레벨의 마지막까지 비교를 하러 들어왔다는 것. 즉, 여기선 princeton.edu랑 비교를 해야하는데

// simpsons.com이 더 크니 안에 들어가서 확인해보겠다는 것.

// h.cdildren[j++].next 를 준건 그 노드의 부모를 root로 해서 확인하겠다는 것

if (( j + 1 == h.m ) || less(key, h.children[j + 1].key))

{

Node u = insert(h.children[j++].next, key, val, ht - 1);

if (u == null)

return null;

t.key = u.children[0].key;

t.next = u;

break;

}

}

}


for (int i = h.m; i > j; i--)

h.children[i] = h.children[i - 1];

h.children[j] = t;

h.m++;

// 자식 갯수가 정해진 것보다 작으면 그대로 진행

// 자식 갯수가 M을 넘어서면 분할

if (h.m < M)

return null;

else

return split(h);

}


// split node in half

private Node split(Node h)

{

Node t = new Node(M / 2);

h.m = M / 2;

for (int j = 0; j < M / 2; j++)

t.children[j] = h.children[M / 2 + j];

return t;

}


/**

* Returns a string representation of this B-tree (for debugging).

*

* @return a string representation of this B-tree.

*/

public String toString()

{

return toString(root, height, "") + "\n";

}


private String toString(Node h, int ht, String indent)

{

StringBuilder s = new StringBuilder();

Entry [] children = h.children;


if (ht == 0)

{

for (int j = 0; j < h.m; j++)

{

s.append(indent + children[j].key + " " + children[j].val + "\n");

}

}

else

{

for (int j = 0; j < h.m; j++)

{

if (j > 0)

s.append(indent + "(" + children[j].key + ")\n");

s.append(toString(children[j].next, ht - 1, indent + "     "));

}

}

return s.toString();

}


// comparison functions - make Comparable instead of Key to avoid casts

private boolean less(Comparable k1, Comparable k2)

{

System.out.println("k1.compareTo(k2) : " + k1.compareTo(k2));

return k1.compareTo(k2) < 0;

}


private boolean eq(Comparable k1, Comparable k2)

{

return k1.compareTo(k2) == 0;

}


/**

* Unit tests the {@code BTree} data type.

*

* @param args

*            the command-line arguments

*/

public static void main(String [] args)

{

BTree<String, String> st = new BTree<String, String>();


st.put("www.cs.princeton.edu", "128.112.136.12");

st.put("www.cs.princeton.edu", "128.112.136.11");

st.put("www.princeton.edu", "128.112.128.15");

st.put("www.yale.edu", "130.132.143.21");

st.put("www.simpsons.com", "209.052.165.60");

st.put("www.apple.com", "17.112.152.32");

st.put("www.amazon.com", "207.171.182.16");

st.put("www.ebay.com", "66.135.192.87");

st.put("www.cnn.com", "64.236.16.20");

st.put("www.google.com", "216.239.41.99");

st.put("www.nytimes.com", "199.239.136.200");

st.put("www.microsoft.com", "207.126.99.140");

st.put("www.dell.com", "143.166.224.230");

st.put("www.slashdot.org", "66.35.250.151");

st.put("www.espn.com", "199.181.135.201");

st.put("www.weather.com", "63.111.66.11");

st.put("www.yahoo.com", "216.109.118.65");


System.out.println(st);

/*StdOut.println("cs.princeton.edu:  " + st.get("www.cs.princeton.edu"));

StdOut.println("hardvardsucks.com: " + st.get("www.harvardsucks.com"));

StdOut.println("simpsons.com:      " + st.get("www.simpsons.com"));

StdOut.println("apple.com:         " + st.get("www.apple.com"));

StdOut.println("ebay.com:          " + st.get("www.ebay.com"));

StdOut.println("dell.com:          " + st.get("www.dell.com"));

StdOut.println();


StdOut.println("size:    " + st.size());

StdOut.println("height:  " + st.height());

StdOut.println(st);

StdOut.println();*/

}


}