hash
Hash
: κ²μκ³Ό λλΆμ΄ λ°μ΄ν°μ μΆκ°μ μμ λ ν¨μ¨μ μΌλ‘ μνν μ μλ λ°©λ² μμ μ΄λμ νμν μκ° λ³΅μ‘λλ O(n)μ΄λ©°, μμ ν λλ κ°μ λΉμ©μΌλ‘ λ°μ
F(key) -> Hashcode -> index -> value
κ²μνκ³ μ νλ ν€κ°μ λ°μμ ν΄μ¬ν¨μλ‘ λλ €μ λ°νλ°μ hashcodeλ₯Ό μ΄μ©ν΄μ λ°°μ΄μ indexλ₯Ό μ΄μ©ν΄μ λ°μ΄ν°μ μλ£μ μ κ·Όνλ λ°©λ² key κ°μ μ«μ, λ¬Έμμ΄, νμΌ λ±μ ν΄λΉνλ€
ν΄μ¬ ν μ΄λΈμ hashcode % sizeλ‘ λλμ΄ μΈλ±μ€λ‘ μ¬μ©νλ€
λ€μ΄μ¬λλ§λ€ linkedListλ‘ μ μ₯νκ³ μ°Ύλλ€
μΆ©λμ λ°λΌμ λΆλ₯(Collision)
O(1) -> O(N)κΉμ§ 볡μ‘λκ° λμ΄λλ€
체μΈλ² : κ°μ ν΄μ κ°μ κ°λ μμλ₯Ό μ°κ²° 리μ€νΈλ‘ κ΄λ¦¬
μ€ν μ£Όμλ² : λΉ λ²ν·μ μ°Ύμ λκΉμ§ ν΄μλ₯Ό λ°λ³΅
ν΄μμ ν΄μ ν¨μμ λνμ¬
λ§μ½ μΆ©λμ΄ μ ν λ°μνμ§ μλλ€λ©΄ ν΄μ ν¨μλ‘ μΈλ±μ€λ₯Ό μ°Ύλ κ²λ§μΌλ‘ κ²μ, μΆκ°, μμ κ° κ±°μ μλ£ λλ―λ‘ μκ° λ³΅μ‘λλ μ΄λ κ²μ΄λ O(1)μ΄ λλ€ ν΄μ ν μ΄λΈμ ν¬κ² νλ©΄ μΆ©λ λ°μμ μ΅μ ν μ μμ§λ§ λ€λ₯Έ ννΈμΌλ‘ λ©λͺ¨λ¦¬λ₯Ό μΈλ°μμ΄ λ§μ΄ μ°¨μ§νλ€ μ¦, μκ°κ³Ό 곡κ°μ μ μΆ(trade-Off)μ΄λΌλ λ¬Έμ κ° νμ λ°λΌ λ€λλλ€. μΆ©λμ νΌνκΈ° μν΄ ν΄μ ν¨μλ ν΄μ ν μ΄λΈ ν¬κΈ° μ΄νμ μ μλ₯Ό λλλ‘ νμͺ½μΌλ‘ μΉμ°μΉμ§ μκ³ κ³ λ₯΄κ² λ§λ€μ΄μΌ νλ€ κ·Έλμ ν΄μ ν μ΄λΈ ν¬κΈ°λ μμκ° μ’λ€κ³ μλ €μ Έμλ€ ν€ κ°μ΄ μ μκ° μλ κ²½μ° ν΄μ κ°μ ꡬν λλ μ’ λ μ κ²½μ μ¨ λ°©λ²μΌ λͺ¨μν΄μΌ νλ€. μ건λ μ€μ ν€ κ°μ λν΄ λΉνΈ μ°μ°(bitwise operation)μ νλ λ°©λ², λ¬Έμμ΄ ν€ κ°μ λν΄ κ° λ¬Έμ μ½λμ κ³±μ κ³Ό λ§μ μ νλ μλ²μ΄ μλ€
μ€ν μ£Όμλ²μ μν ν΄μ
public class OpenHash<K,V> {
// λ²ν·μ μν
enum Status {OCCUPIED, EMPTY, DELETED}; // {λ°μ΄ν° μ μ₯, λΉμ΄ μμ, μμ λ§μΉ¨}
// λ²ν·
static class Bucket<K,V> {
private K key; // ν€ κ°
private V data; // λ°μ΄ν°
private Status stat; // μν
// μμ±μ
Bucket() {
stat = Status.EMPTY; // λ²ν·μ λΉμ΄ μμ
}
// λͺ¨λ νλμ κ°μ μ€μ ν©λλ€.
void set(K key, V data, Status stat) {
this.key = key; // ν€ κ°
this.data = data; // λ°μ΄ν°
this.stat = stat; // μν
}
// μνλ₯Ό μ€μ ν©λλ€.
void setStat(Status stat) {
this.stat = stat;
}
// ν€ κ°μ λ°νν©λλ€.
K getKey() {
return key;
}
// λ°μ΄ν°λ₯Ό λ°νν©λλ€.
V getValue() {
return data;
}
// ν€μ ν΄μ κ°μ λ°νν©λλ€.
public int hashCode() {
return key.hashCode();
}
}
private int size; // ν΄μ ν
μ΄λΈμ ν¬κΈ°
private Bucket<K,V>[] table; // ν΄μ ν
μ΄λΈ
// μμ±μ
public OpenHash(int size) {
try {
table = new Bucket[size];
for (int i = 0; i < size; i++)
table[i] = new Bucket<K,V>();
this.size = size;
} catch (OutOfMemoryError e) { // ν
μ΄λΈμ μμ±ν μ μμ
this.size = 0;
}
}
// ν΄μ κ°μ ꡬν¨
public int hashValue(Object key) {
return key.hashCode() % size;
}
// μ¬ν΄μκ°μ ꡬν¨
public int rehashValue(int hash) {
return (hash + 1) % size;
}
// ν€ κ° keyλ₯Ό κ°λ λ²ν·μ κ²μ
private Bucket<K,V> searchNode(K key) {
int hash = hashValue(key); // κ²μν λ°μ΄ν°μ ν΄μκ°
Bucket<K,V> p = table[hash]; // μ ν λ²ν·
for (int i = 0; p.stat != Status.EMPTY && i < size; i++) {
if (p.stat == Status.OCCUPIED && p.getKey().equals(key))
return p;
hash = rehashValue(hash); // μ¬ν΄μ
p = table[hash];
}
return null;
}
// ν·κ° keyλ₯Ό κ°λ μμμ κ²μ (λ°μ΄ν°λ₯Ό λ°ν)
public V search(K key) {
Bucket<K,V> p = searchNode(key);
if (p != null)
return p.getValue();
else
return null;
}
// ν€ κ° key, λ°μ΄ν° dataλ₯Ό κ°λ μμμ μΆκ°
public int add(K key, V data) {
if (search(key) != null)
return 1; // μ΄ ν€ κ°μ μ΄λ―Έ λ±λ‘λ¨
int hash = hashValue(key); // μΆκ°ν λ°μ΄ν°μ ν΄μ κ°
Bucket<K,V> p = table[hash]; // μ ν λ²ν·
for (int i = 0; i < size; i++) {
if (p.stat == Status.EMPTY || p.stat == Status.DELETED) {
p.set(key, data, Status.OCCUPIED);
return 0;
}
hash = rehashValue(hash); // μ¬ν΄μ
p = table[hash];
}
return 2; // ν΄μ ν
μ΄λΈμ΄ κ°λ μ°Έ
}
// ν€ κ° keyλ₯Ό κ°λ μμμ μμ
public int remove(K key) {
Bucket<K,V> p = searchNode(key); // μ ν λ²ν·
if (p == null)
return 1; // μ΄ ν€ κ°μ λ±λ‘λμ§ μμ
p.setStat(Status.DELETED);
return 0;
}
// ν΄μ ν
μ΄λΈμ λ€ν
public void dump() {
for (int i = 0; i < size; i++) {
System.out.printf("%02d ", i);
switch (table[i].stat) {
case OCCUPIED :
System.out.printf("%s (%s)\n",
table[i].getKey(), table[i].getValue());
break;
case EMPTY :
System.out.println("-- λ―Έλ±λ‘ --"); break;
case DELETED :
System.out.println("-- μμ λ§μΉ¨ --"); break;
}
}
}
}
public class OpenHashTester {
static Scanner stdIn = new Scanner(System.in);
// λ°μ΄ν° (νμλ²νΈ + μ΄λ¦)
static class Data {
static final int NO = 1; // λ²νΈλ₯Ό μ½μ΄ λ€μΌκΉμ?
static final int NAME = 2; // μ΄λ¦μ μ½μ΄ λ€μΌκΉμ?
private Integer no; // νμλ²νΈ (ν€ κ°)
private String name; // μ΄λ¦
// ν€ κ°
Integer keyCode() {
return no;
}
// λ¬Έμμ΄μ λ°νν©λλ€.
public String toString() {
return name;
}
// λ°μ΄ν°λ₯Ό μ
λ ₯ν©λλ€.
void scanData(String guide, int sw) {
System.out.println(guide + "ν λ°μ΄ν°λ₯Ό μ
λ ₯νμΈμ.");
if ((sw & NO) == NO) {
System.out.print("λ²νΈοΌ");
no = stdIn.nextInt();
}
if ((sw & NAME) == NAME) {
System.out.print("μ΄λ¦οΌ");
name = stdIn.next();
}
}
}
// λ©λ΄ μ΄κ±°ν
enum Menu {
ADD( "μΆκ°"),
REMOVE( "μμ "),
SEARCH( "κ²μ"),
DUMP( "μΆλ ₯"),
TERMINATE("μ’
λ£");
private final String message; // λνλΌ λ¬Έμμ΄
static Menu MenuAt(int idx) { // μμκ° idxμΈ μ΄κ±°λ₯Ό λ°νν©λλ€.
for (Menu m : Menu.values())
if (m.ordinal() == idx)
return m;
return null;
}
Menu(String string) { // μμ±μ
message = string;
}
String getMessage() { // λνλΌ λ¬Έμμ΄μ λ°ν
return message;
}
}
// λ©λ΄ μ ν
static Menu SelectMenu() {
int key;
do {
for (Menu m : Menu.values())
System.out.printf("(%d) %s ", m.ordinal(), m.getMessage());
System.out.print("οΌ");
key = stdIn.nextInt();
} while (key < Menu.ADD.ordinal() || key > Menu.TERMINATE.ordinal());
return Menu.MenuAt(key);
}
public static void main(String[] args) {
Menu menu; // λ©λ΄
Data data; // μΆκ°μ© λ°μ΄ν° μ°Έμ‘°
Data temp = new Data(); // μ
λ ₯μ© λ°μ΄ν°
OpenHash<Integer, Data> hash = new OpenHash<Integer, Data>(13);
do {
switch (menu = SelectMenu()) {
case ADD : // μΆκ°
data = new Data();
data.scanData("μΆκ°", Data.NO | Data.NAME);
int k = hash.add(data.keyCode(), data);
switch (k) {
case 1: System.out.println("κ·Έ ν€ κ°μ μ΄λ―Έ λ±λ‘λμ΄ μμ΅λλ€.");
break;
case 2: System.out.println("ν΄μ ν
μ΄λΈμ΄ κ°λ μ°Όμ΅λλ€.");
break;
}
break;
case REMOVE : // μμ
temp.scanData("μμ ", Data.NO);
hash.remove(temp.keyCode());
break;
case SEARCH : // κ²μ
temp.scanData("κ²μ", Data.NO);
Data t = hash.search(temp.keyCode());
if (t != null)
System.out.println("κ·Έ ν€λ₯Ό κ°λ λ°μ΄ν°λ " + t + "μ
λλ€.");
else
System.out.println("κ·Έ λ°μ΄ν°κ° μμ΅λλ€.");
break;
case DUMP : // μΆλ ₯
hash.dump();
break;
}
} while (menu != Menu.TERMINATE);
}
}
체μΈλ²
public class ChainHash<K,V> {
// ν΄μλ₯Ό ꡬμ±νλ λ
Έλ
class Node<K,V> {
private K key; // ν€ κ°
private V data; // λ°μ΄ν°
private Node<K,V> next; // λ€μ λ
Έλμ λν μ°Έμ‘°
// μμ±μ
Node(K key, V data, Node<K,V> next) {
this.key = key;
this.data = data;
this.next = next;
}
// ν€ κ°μ λ°νν©λλ€.
K getKey() {
return key;
}
// λ°μ΄ν°λ₯Ό λ°νν©λλ€.
V getValue() {
return data;
}
// ν€μ ν΄μκ°μ λ°νν©λλ€.
public int hashCode() {
return key.hashCode();
}
}
private int size; // ν΄μ ν
μ΄λΈμ ν¬κΈ°
private Node<K,V>[] table; // ν΄μ ν
μ΄λΈ
// μμ±μ
public ChainHash(int capacity) {
try {
table = new Node[capacity];
this.size = capacity;
} catch (OutOfMemoryError e) { // ν
μ΄λΈμ μμ±ν μ μμ
this.size = 0;
}
}
// ν΄μκ°μ ꡬν¨
public int hashValue(Object key) {
return key.hashCode() % size;
}
// ν€ κ° keyλ₯Ό κ°λ μμμ κ²μ (λ°μ΄ν°λ₯Ό λ°ν)
public V search(K key) {
int hash = hashValue(key); // κ²μν λ°μ΄ν°μ ν΄μκ°
Node<K,V> p = table[hash]; // μ ν λ
Έλ
while (p != null) {
if (p.getKey().equals(key))
return p.getValue(); // κ²μ μ±κ³΅
p = p.next; // λ€μ λ
Έλμ μ£Όλͺ©
}
return null; // κ²μ μ€ν¨
}
// ν€ κ° key, λ°μ΄ν° dataλ₯Ό κ°λ μμμ μΆκ°
public int add(K key, V data) {
int hash = hashValue(key); // μΆκ°ν λ°μ΄ν°μ ν΄μκ°
Node<K,V> p = table[hash]; // μ ν λ
Έλ
while (p != null) {
if (p.getKey().equals(key)) // μ΄ ν€ κ°μ μ΄λ―Έ λ±λ‘λ¨
return 1;
p = p.next; // λ€μ λ
Έλμ μ£Όλͺ©
}
Node<K,V> temp = new Node<K,V>(key, data, table[hash]);
table[hash] = temp; // λ
Έλλ₯Ό μ½μ
return 0;
}
// ν€ κ° keyλ₯Ό κ°λ μμμ μμ
public int remove(K key) {
int hash = hashValue(key); // μμ ν λ°μ΄ν°μ ν΄μ κ°
Node<K,V> p = table[hash]; // μ ν λ
Έλ
Node<K,V> pp = null; // λ°λ‘ μμ μ ν λ
Έλ
while (p != null) {
if (p.getKey().equals(key)) { // μ°ΎμΌλ©΄
if (pp == null)
table[hash] = p.next;
else
pp.next = p.next;
return 0;
}
pp = p;
p = p.next; // λ€μ λ
Έλλ₯Ό κ°λ¦¬ν΄
}
return 1; // κ·Έ ν€ κ°μ μμ΅λλ€.
}
// ν΄μ ν
μ΄λΈμ λ€ν
public void dump() {
for (int i = 0; i < size; i++) {
Node<K,V> p = table[i];
System.out.printf("%02d ", i);
while (p != null) {
System.out.printf("β %s (%s) ", p.getKey(), p.getValue());
p = p.next;
}
System.out.println();
}
}
}
class ChainHashTester {
static Scanner stdIn = new Scanner(System.in);
// λ°μ΄ν° (νμλ²νΈ + μ΄λ¦)
static class Data {
static final int NO = 1; // λ²νΈλ₯Ό μ½μ΄ λ€μΌκΉμ?
static final int NAME = 2; // μ΄λ¦μ μ½μ΄ λ€μΌκΉμ?
private Integer no; // νμλ²νΈ (ν€ κ°)
private String name; // μ΄λ¦
// ν€ κ°
Integer keyCode() {
return no;
}
// λ¬Έμμ΄ ννμ λ°νν©λλ€.
public String toString() {
return name;
}
// λ°μ΄ν°λ₯Ό μ
λ ₯ν©λλ€.
void scanData(String guide, int sw) {
System.out.println(guide + "ν λ°μ΄ν°λ₯Ό μ
λ ₯νμΈμ.");
if ((sw & NO) == NO) {
System.out.print("λ²νΈοΌ");
no = stdIn.nextInt();
}
if ((sw & NAME) == NAME) {
System.out.print("μ΄λ¦οΌ");
name = stdIn.next();
}
}
}
// λ©λ΄ μ΄κ±°ν
enum Menu {
ADD( "μΆκ°"),
REMOVE( "μμ "),
SEARCH( "κ²μ"),
DUMP( "μΆλ ₯"),
TERMINATE("μ’
λ£");
private final String message; // λνλΌ λ¬Έμμ΄
static Menu MenuAt(int idx) { // μμκ° idxμΈ μ΄κ±°λ₯Ό λ°ν
for (Menu m : Menu.values())
if (m.ordinal() == idx)
return m;
return null;
}
Menu(String string) { // μμ±μ
message = string;
}
String getMessage() { // λνλΌ λ¬Έμμ΄μ λ°ν
return message;
}
}
// λ©λ΄ μ ν
static Menu SelectMenu() {
int key;
do {
for (Menu m : Menu.values())
System.out.printf("(%d) %s ", m.ordinal(), m.getMessage());
System.out.print("οΌ");
key = stdIn.nextInt();
} while (key < Menu.ADD.ordinal() || key > Menu.TERMINATE.ordinal());
return Menu.MenuAt(key);
}
public static void main(String[] args) {
Menu menu; // λ©λ΄
Data data; // μΆκ°μ© λ°μ΄ν° μ°Έμ‘°
Data temp = new Data(); // μ
λ ₯μ© λ°μ΄ν°
ChainHash<Integer, Data> hash = new ChainHash<Integer, Data>(13);
do {
switch (menu = SelectMenu()) {
case ADD : // μΆκ°
data = new Data();
data.scanData("μΆκ°", Data.NO | Data.NAME);
hash.add(data.keyCode(), data);
break;
case REMOVE : // μμ
temp.scanData("μμ ", Data.NO);
hash.remove(temp.keyCode());
break;
case SEARCH : // κ²μ
temp.scanData("κ²μ", Data.NO);
Data t = hash.search(temp.keyCode());
if (t != null)
System.out.println("κ·Έ ν€λ₯Ό κ°λ λ°μ΄ν°λ " + t + "μ
λλ€.");
else
System.out.println("κ·Έ λ°μ΄ν°κ° μμ΅λλ€.");
break;
case DUMP : // μΆλ ₯
hash.dump();
break;
}
} while (menu != Menu.TERMINATE);
}
}
Last updated
Was this helpful?