문제 : https://school.programmers.co.kr/learn/courses/30/lessons/42576
수많은 마라톤 선수들이 마라톤에 참여하였습니다. 단 한 명의 선수를 제외하고는 모든 선수가 마라톤을 완주하였습니다.
마라톤에 참여한 선수들의 이름이 담긴 배열 participant와 완주한 선수들의 이름이 담긴 배열 completion이 주어질 때, 완주하지 못한 선수의 이름을 return 하도록 solution 함수를 작성해주세요.
Hash : 임의의 크기를 가진 데이터를 고정된 크기의 데이터로 변화시켜 저장하는것이다.
이처럼 Hash에 저장할 때는 정해진 크기 안에서 인덱스가 생성이 되고 key값이 들어오면 hash function에서 식을 사용하여 고유한 숫자를 만들어 내는데 이것이 index 번호이다. 그 후 맵핑을하여 바구니에 저장이 된다.
Hash 식 구조
Bucket은 8개가 있다고 가정할때 Hash function은 학과의 알파벳들을 숫자로 변환하여 합한 술르 8로 나눈 나머지를 반환한다.
이 때 hash function이 8로 나눈 나머지를 반환하는 이유는 bucket이 8개이므로 숫자를 0~7 내로 압축하기 위해서이다.
hash function을 h(key)라 하면, h("Music") = (13+21+19+9+3) mod 8 = 1 이고, h("History") = (8+9+19+20+15+18+25) mod 8 = 2 이다.
이와 같이 모든 데이터를 hash function을 통해 bucket을 결정하여 삽입하면 아래와 같이 된다.
Hash Table : Hash함수를 이용해 array에 접근하는 자료구조로 값을 넣을때도 hash(key)를 쓰고 값을 뺄 때도 hash(key)를 쓴다. 그래서 접근 속도가 O(1)이 나와서 빠른 자료구조 이다.
Hash 방식
public class HashFunction {
public int hash(String key) {
int asciiSum = 0;
for(int i=0; i<key.length(); i++) {
asciiSum += key.charAt(i);
}
return asciiSum % 90;
}
public static void main(String[] args) {
HashFunction hf = new HashFunction();
hf.hash("yeong");
}
}
Hash 실습
public class HashTableSolution {
private int size = 10000;
private int[] table = new int[size];
public HashTableSolution() {
}
public HashTableSolution(int size) {
this.size = size;
this.table = new int[size];
}
public int hash(String key) {
int asciiSum = 0;
for (int i = 0; i < key.length(); i++) {
asciiSum += key.charAt(i);
}
return asciiSum % size;
}
public void insert(String key, Integer value) {
int hashCode = hash(key);
this.table[hashCode] = value;
System.out.println(key + " " + hashCode + "방에 저장이 완료되었습니다.");
}
public int search(String key) {
return this.table[hash(key)];
}
public static void main(String[] args) {
String[] names = new String[]{"aaa","bbb","ccc"};
HashTableSolution ht = new HashTableSolution(200);
for (int i = 0; i < names.length; i++) {
ht.insert(names[i] , ht.hash(names[i]));
}
System.out.println(ht.search("aaa"));
System.out.println(ht.search("ccc"));
}
}
해시의 고유식을 통해 임의의 값을 지정하여 그곳에 데이터를 저장하는 방식을 하게되면 많은 데이터를 처리할때 값이 중복되어 같은 자리에 2개 이상의 데이터가 입력되는 경우가 발생한다.
따라서, 이를 방지하기 위한 방법을 제시한다.
1개 입력시 {test:1} 표현법
148 | |||
---|---|---|---|
[{test:1}] |
2개 입력시 {test2:2} 표현법
148 | |||
---|---|---|---|
[{test:1}] | |||
[{test2:2}] |
// 해시 충돌 했을경우 해결법
public class HashSolution {
// 멤버변수
private int size;
private List<Node>[] table; // 2차원 배열 리스트
// ex) 2차원 배열 리스트에 Node형식으로 저장함
// 1 2 3
// ["aa",1] ["aaa",2]
// ["bb",2]
// 내부 클래스 선언
class Node { // Node DTO [(String)key,(Integer)value] 형식
private String key;
private Integer value;
public Node(String key, Integer value) {
this.key = key;
this.value = value;
}
public String getKey() {
return key;
}
public Integer getValue() {
return value;
}
}
// 디폴트 생성자
public HashSolution() { // 생성자
this.size = 10000;
this.table = new ArrayList[size];
}
// 생성자 오버로딩
public HashSolution(int size) { // 생성자
this.size = size;
this.table = new ArrayList[size]; // 2차원 배열 사이즈 선언
}
public int hash(String str) { // hash를 통해 임의의 숫자 구함
int ascii = 0;
for (int i = 0; i < str.length(); i++) {
ascii += str.charAt(i);
}
return ascii % size; // 1000개 0~999까지의 값 출력
}
public Integer get(String key) { // 해시 값 찾기
List<Node> nodes = this.table[hash(key)]; // 2차원 배열 리스트중 hash(key)에 해당하는 배열 가져옴
// ex) 2차원 배열 리스트의 해당 값을 가져옴 만약 1번일때
// 1 2 3
// ["aa",1] ["aaa",2]
// ["bb",2]
// nodes => ["aa",1],["bb',2] 가져옴
for (Node node : nodes) { // 2번 반복
if (key.equals(node.getKey())) { // 입력한 키와 찾아온 키를 비교함
// ex) 입력된 키가 "bb"일때
// 1번째 순서일때는 못찾고 2번째 순서일떄 찾는것으로
// 2번째 node = {"bb",2} , node.getkey() = "bb"
// bb.equals(bb) true
return node.value; // 2번쨰 노드의 객체의 값인 2를 가져옴
}
}
return null;
}
public void insert(String key, Integer value) {
int hashCode = hash(key); // 입력한 문자를 가지고 해시를 통해 임의의 key값 출력
if (this.table[hashCode] == null) { // 만약 2차원 배열 리스트에 없는 값이라면
this.table[hashCode] = new ArrayList<>(); // 2차원 배열 리스트 생성
}
this.table[hashCode].add(new Node(key,value)); // 해당 해쉬 자리에 key,value 객체 저장
}
public static void main(String[] args) {
String[] names = new String[]{"DongyeonKang",
"SubinKang", "KwanwunKo", "HyunseokKo", "KyoungdukKoo", "YeonjiGu", "SoyeonKown", "OhsukKwon", "GunwooKim", "KiheonKim", "NayeongKim", "DohyeonKim", "MinkyoungKim", "MinjiKim", "SanghoKim", "SolbaeKim", "YejinKim", "EungjunKim", "JaegeunKim", "JeonghyeonKim", "JunhoKim", "JisuKim", "kimjinah", "HaneulKim", "HeejungKim", "KimoonPark", "EunbinPark", "JeongHoonPark", "JeminPark", "TaegeunPark", "JiwonBae", "SeunggeunBaek", "JihwanByeon", "HeungseopByeon", "JeongHeeSeo", "TaegeonSeo", "SeeYunSeok", "SuyeonSeong", "SeyoelSon", "MinjiSong", "JinwooSong", "hyunboSim", "SominAhn", "JiyoungAhn", "ChangbumAn", "SoonminEom",
"HyeongsangOh", "SuinWoo", "JuwanWoo", "InkyuYoon", "GahyunLee", "DaonLee", "DohyunLee", "SanghunLee", "SujinLee", "AjinLee", "YeonJae", "HyeonjuLee", "HakjunYim", "SeoyunJang", "SeohyeonJang", "JinseonJang", "SujinJeon", "SeunghwanJeon", "DaehwanJung", "JaeHyunJeung", "HeejunJeong", "GukhyeonCho", "MunjuJo", "YejiJo", "ChanminJu", "MinjunChoi", "SujeongChoi", "SeunghoChoi", "AyeongChoi", "GeonjooHan", "JinhyuckHeo", "MinwooHwang", "SieunHwang",
"JunhaHwang"};
HashSolution hashSolution = new HashSolution(200);
hashSolution.insert("Yoonseo", 1);
hashSolution.insert("Seoyoon", 2);
int result = hashSolution.get("Yoonseo");
if (result == 1) {
System.out.println("테스트 성공");
} else {
System.out.printf("테스트 실패 value:%d", result);
}
result = hashSolution.get("Seoyoon");
if (result == 2) {
System.out.println("테스트 성공");
} else {
System.out.printf("테스트 실패 value:%d", result);
}
}
}
※ 참고자료
https://ejyoo.tistory.com/72
https://ju-hy.tistory.com/107