https://www.acmicpc.net/problem/2775

 

2775번: 부녀회장이 될테야

첫 번째 줄에 Test case의 수 T가 주어진다. 그리고 각각의 케이스마다 입력으로 첫 번째 줄에 정수 k, 두 번째 줄에 정수 n이 주어진다

www.acmicpc.net

 

그림을 그려 점화식을 찾아 풀이하였다.

이차원배열에 값을 저장하고, 해당 층의 값을 출력해주었다.

그림으로 그리면 아래와 같이 설명할 수 있다.

점화식을 그림으로 유추

- 코드

import java.util.*;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int T = sc.nextInt();

        int[][] aparts = new int[15][15];
        int[][] info = new int[T][2];

        for(int i=0;i<aparts.length;i++){
            aparts[i][1]=1;
            aparts[0][i]=i;
        }

        for(int i=1;i<15;i++){
            for(int j=2;j<15;j++){
                aparts[i][j] = aparts[i-1][j]+aparts[i][j-1];
            }
        }

        for(int i=0;i<T;i++){
            int floor = sc.nextInt();
            int ho = sc.nextInt();
            info[i][0] = floor;
            info[i][1] = ho; 
        }

        for(int i=0;i<T;i++){
            System.out.println(aparts[info[i][0]][info[i][1]]);
        }
    }
}

 

 

백준 2252번: 줄 세우기 문제를 풀다가 위상 정렬 알고리즘에 대해 알게 되었다.

위상정렬 알고리즘의 개념에 대해서 포스팅을 해보았다.

 

1. 위상 정렬 알고리즘이란?

순서가 정해져 있는 작업을 차례대로 사용해야 할 때 사용하는 알고리즘.

 

- 사용조건

1) 단방향 그래프여야함.

2) 그래프끼리의 Cycle이 생기지 않아야함.

 

2. 알고리즘 간략 설명

1) 단방향그래프로 노드 저장

2) 노드를 저장할때 edgeCount값도 같이 저장해준다.(여기서 edgeCount는 노드를 가리키고 있는 간선의 수를 뜻한다.)

3) edgeCount값이 0인 것들을 먼저 큐에 넣고 노드를 방문하면서 edgeCount를 방문할때마다 -1을 해준다.

4) edgeCount값이 0이 되면 큐에 넣는다.

5) 큐에 값이 없을때까지 순회한다.

 

ex) 예시

[4,2],[4,2],[2,1]의 데이터가 들어올 경우 아래처럼 그래프가 그려진다.

위상정렬 그림으로 설명

 

3. 위상정렬을 이용한 대표적인 문제

https://www.acmicpc.net/problem/2252

 

2252번: 줄 세우기

첫째 줄에 N(1 ≤ N ≤ 32,000), M(1 ≤ M ≤ 100,000)이 주어진다. M은 키를 비교한 회수이다. 다음 M개의 줄에는 키를 비교한 두 학생의 번호 A, B가 주어진다. 이는 학생 A가 학생 B의 앞에 서야 한다는 의

www.acmicpc.net

위의 문제는 단방향 그래프에, 키를 비교한 값이기 때문에 서로 사이클이 생기지 않으므로

위상정렬의 알고리즘 조건에 부합한다.

아래는 나의 코드이다.

 

- 내 풀이

import java.util.*;

public class Main {
    static ArrayList<Integer> list = new ArrayList<>();
    static ArrayList<ArrayList<Integer>> graph = new ArrayList<>();
    static int[] edgeCount;

    public static void main(String[] args){
        Scanner sc = new Scanner(System.in);
        int N = sc.nextInt();//노드 수
        int M = sc.nextInt();//줄 수
        Queue<Integer> queue =new LinkedList<>();

        int[][] arr= new int[M][2];
        for(int i=0;i<M;i++){
            arr[i][0]= sc.nextInt();
            arr[i][1]= sc.nextInt();
        }
        edgeCount =new int[N + 1];

        //행마다 arraylist 생성
        for(int i=0;i<N+1;i++)  graph.add(new ArrayList<>());

        for(int i=0;i<arr.length;i++){
            putGraph(arr[i][0],arr[i][1]);
        }

        for(int i=1;i<N+1;i++){
            if(edgeCount[i]==0){//count값이 0인값 먼저 큐에 넣어준다.
                queue.add(i);
            }
        }

        while (!queue.isEmpty()){
            int idx = queue.remove();
            System.out.print(idx+" ");
            ArrayList<Integer> node = graph.get(idx);
            for(int i=0;i<node.size();i++){
                int next = node.get(i);
                edgeCount[next]--;
                if(edgeCount[next]==0){
                    queue.add(next);
                }
            }
        }
    }

    public static void putGraph(int x,int y){
        //포함하고 있지 않다면
        if(!graph.get(x).contains(y)) {
            graph.get(x).add(y);//단방향 그래프에 추가
            edgeCount[y]++;//간선수 카운트 +1
        }
    }
}

'알고리즘' 카테고리의 다른 글

[알고리즘] 조합(Combination)  (0) 2022.12.02
[알고리즘] 슬라이딩 윈도우  (0) 2022.09.15
[알고리즘] 정렬1(버블,선택,삽입)  (0) 2022.07.29
[알고리즘] 힙(Heap)  (0) 2022.07.21
[알고리즘] 해시(Hash)  (0) 2022.07.15

1. 조합이란?

조합이란 n개의 숫자에서 r개의 수를 순서 없이 뽑는 경우의 수를 말한다.

예를 들어 1,2,3 에서 2개의 수를 순서없이 뽑는다면 

[1,2], [1,3], [2,3] 3가지의 경우가 나온다.

 

변수 설명
arr 조합을 뽑아낼 배열
visited 조합에 뽑혔는지 체크하는 배열
n 배열의 길이
r 조합의 길이

 

2. 조합 구현

백트래킹과 재귀로 구현할 수 있는데 백트래킹으로 구현해 보았다.

static void comb1(int[] arr, boolean[] visited, int start, int n, int r) {
	if(r == 0) {
		print(arr, visited, n);
		return;
	} else {
		for(int i = start; i < n; i++) {
			visited[i] = true;
			comb1(arr, visited, i + 1, n, r - 1);
			visited[i] = false;
		}
	}
}

 

'알고리즘' 카테고리의 다른 글

[알고리즘] 위상 정렬(Topology Sort)  (0) 2022.12.05
[알고리즘] 슬라이딩 윈도우  (0) 2022.09.15
[알고리즘] 정렬1(버블,선택,삽입)  (0) 2022.07.29
[알고리즘] 힙(Heap)  (0) 2022.07.21
[알고리즘] 해시(Hash)  (0) 2022.07.15

https://www.acmicpc.net/problem/2309

 

2309번: 일곱 난쟁이

아홉 개의 줄에 걸쳐 난쟁이들의 키가 주어진다. 주어지는 키는 100을 넘지 않는 자연수이며, 아홉 난쟁이의 키는 모두 다르며, 가능한 정답이 여러 가지인 경우에는 아무거나 출력한다.

www.acmicpc.net

 

9명의 난쟁이 중에서 키의 합이 100이 되는 진짜 일곱난쟁이를 찾는 문제이다.

나는 9명의 키의 다 더해서 총합을 구하고 arraylist의 값 중 2개의 값을 삭제하는 방식으로 문제를 풀었다. 

 

- 코드

import java.util.*;
import java.io.*;
import java.util.stream.IntStream;

public class Main {
    public static void main(String[] args) throws IOException {
        BufferedReader bf = new BufferedReader(new InputStreamReader(System.in));
        int[] arr = new int[9];

        for(int i=0;i<9;i++){
            arr[i] = Integer.parseInt(bf.readLine());
        }

        ArrayList<Integer> list = new ArrayList<>();
        int tot = IntStream.of(arr).sum();//난쟁이의 키의 합을 더한다.
        for(int a : arr){
            list.add(a);
        }

        int target= tot-100;//100을 빼서 타겟 넘버를 저장

        for(int i=0;i<arr.length;i++){
          int now = arr[i];
          int want= target-now;
          list.remove(Integer.valueOf(now));
          if(list.contains(want)) {//리스트에 찾는 값이 있는지 검사
              list.remove(Integer.valueOf(want));//있으면 삭제하고
              break;//반복문 탈출
          }
          list.add(now);
        }

        Collections.sort(list);//오름차순으로 정렬
        for(int a: list){
            System.out.println(a);
        }
    }
}

 

- 설명

1) IntStream.of(arr).sum() 

arr배열의 값을 모두 더함.

2) list.remove(Integer.valueOf(now))

list.remove(20); 이라고 쓸 경우, 20이 값인 게 아니라 리스트 인덱스 20인 값이 삭제되게 된다.

따라서 valueOf로 String값으로 바꿔주고 값으로 찾을 수 있도록 하였다.

1. 슬라이딩 윈도우 알고리즘

창문을 왼쪽부터 오른쪽으로 밀어오면서 창문안에 있는 값들을 부분배열로 조건에 맞는 경우를 찾는 알고리즘.

문자열에서 어떤 조건을 찾을때 완전탐색기법을 이용하면 이중포문을 사용하면 되는데 최악의 경우 O(n2)의 시간복잡도를 가지는데, 슬라이딩 윈도우 알고리즘을 사용할 경우 O(n)만큼 시간을 단축할 수 있다.

 

슬라이딩 윈도우는 고정된 일정한 사이즈로 앞부분과 뒷부분만 변경해주는 기법이다. 이는 구간이 일정하기 때문에 시작점을 알면 끝점을 알수 있어서 투포인터와 달리 굳이 2개가 필요하지 않다. 예를 들어 길이가 4라고 고정되었을 때 다음과 같이 구간을 탐색하게 된다.

  • start = 0, 🟦🟦🟦🟦⬜️⬜️⬜️
  • start = 1, ⬜️🟦🟦🟦🟦⬜️⬜️
  • start = 2, ⬜️⬜️🟦🟦🟦🟦⬜️
  • ...

예제 문제)

백준 1593번: 문자 해독

package com.company;

import java.util.Arrays;
import java.util.Scanner;

public class CharDecode {
    public static void main(String[] args) {
        int wlen,slen;
        String w, s;
        int[] wArr = new int[52];
        int[] sArr = new int[52];

        Scanner scan = new Scanner(System.in);
        System.out.print("문자열 길이?");
        wlen = Integer.parseInt(scan.next());
        slen = Integer.parseInt(scan.next());

        //버퍼 초기화
        scan.nextLine();
        System.out.print("W문자?");
        w = scan.nextLine();

        System.out.print("S문자?");
        s = scan.nextLine();

        //wArr 배열 셋팅
        for(char c: w.toCharArray()){
            putWord(c, wArr, 1);
        }

        int start=0;
        int cnt=0;
        int size=0;

        for(int i=0;i<s.length();i++){
            char sc = s.charAt(i);
            putWord(sc,sArr,1);
            size++;

            if(size==w.length()){
                if(Arrays.equals(wArr, sArr)){
                    cnt++;
                }
                putWord(s.charAt(start), sArr, -1);
                start++;
                size--;
            }
        }
        System.out.println(cnt);
    }

    static void putWord(char word, int[] arr, int dif){
        //'A'는 아스키코드값 65, 'a'는 97
        if('a'<= word && word<='z'){
            arr[word-'a']+=dif;
        }else{
            arr[word-'A'+26]+=dif;
        }
    }
}

 

'알고리즘' 카테고리의 다른 글

[알고리즘] 위상 정렬(Topology Sort)  (0) 2022.12.05
[알고리즘] 조합(Combination)  (0) 2022.12.02
[알고리즘] 정렬1(버블,선택,삽입)  (0) 2022.07.29
[알고리즘] 힙(Heap)  (0) 2022.07.21
[알고리즘] 해시(Hash)  (0) 2022.07.15

O(n^2)의 시간복잡도를 갖는 알고리즘(버블정렬, 선택정렬, 삽입정렬) 들과

그보다 좀 더 빠르고 효율적인 O(n logn)의복잡도를 갖는 알고리즘(병합정렬, 퀵정렬, 트리정렬)이 있다.

 

1. Bubble Sort(버블 정렬)

바로 그 다음 노드와 비교해가면서 가장 큰 값을 하나씩 뒤로 보내면서 뒤쪽부터 정렬한다.

버블 정렬은 정렬 방식이 마치 물 속에서 올라오는 물방울과 같다고 하여 버블 정렬이라는 이름이 붙여졌다.

버블 정렬

- 장점

구현이 간단하다.

데이터를 하나씩 비교하기 때문에 정밀한 비교가 가능하다.

 

- 단점

데이터를 하나씩 비교하기 때문에 비교 횟수가 많아지므로 시간이 오래 걸린다.

 

- 소스코드

public class BubbleSort {
    public int[] sol(int[] arr){
        int k= arr.length;//배열의 길이

        for(int i=0;i<k-1;i++){
            for(int j=0;j<k-1-i;j++){
                int temp;
                if(arr[j]>arr[j+1]){
                    temp=arr[j];
                    arr[j]=arr[j+1];
                    arr[j+1]=temp;
                }
            }
        }
        return arr;
    }
}

 

2. 선택 정렬

선택정렬은 앞쪽부터 정렬하는 방식이다.

주어진 배열에서 가장 작은 최소값을 찾고 배열의 맨 앞의 값과 위치를 바꾸면서 정렬한다.

배열에서 최소값을 찾아야 하기 때문에 비교 횟수는 많지만 실제로 값을 바꾸는 교환횟수가 적다는 것이 특징이다.

선택 정렬

- 장점

구현이 간단하다

비교하는 횟수에 비해 교환하는 횟수가 적다.

 

-단점

데이터를 하나씩 비교하기때문에 시간이 오래 걸린다.

정렬된 상태에서 새로운 데이터가 추가되면 정렬 효율이 좋지 않다.

 

- 소스코드

public class SelectionSort {
    public int[] sol(int[] arr){

        int k= arr.length;//배열의 길이

        for(int i=0;i<k-1;i++){
            int temp, minIdx = i;
            for(int j=i+1;j<k;j++){
                if(arr[minIdx]>arr[j]){
                    minIdx = j;
                }
            }
            temp=arr[i];
            arr[i]=arr[minIdx];
            arr[minIdx]=temp;
        }
        return arr;
    }
}

 

3. 삽입 정렬

삽입 정렬은 버블 정렬의 비효율성을 개선하기 위해 등장한 방법이다.

두번째 인덱스부터 시작해서 자신의 앞에 있는 값들과 비교하여 적절한 위치를 찾아 넣어주는 형식으로 진행된다.

정렬을 위해 비교하는 범위 자체가 훨씩 적고, 이미정렬되어 있는 경우 앞에 노드보다 큰지 한번씩 밖에 비교를 하지 않아

시간 복잡도는 O(n)까지 줄어든다.

삽입 정렬

- 장점

입력으로 들어오는 배열의 원소가 정렬되어 있을수록 속도가 빠르다.

정렬된 값은 교환이 일어나지 않는다.

 

- 단점

삽입을 구현해야하므로 속도가 자료구조의 영향을 많이 받는다.

입력으로 들어오는 배열이 역순으로 정렬된 경우 성능이 굉장히 좋지 않다.

public class InsertSort {
    public int[] sol(int[] arr){
        int k=arr.length;
        for(int i=1;i<k;i++){
            int temp;
            for(int j=i;j>0;j--){
                if(arr[j-1]>arr[j]){
                    temp=arr[j-1];
                    arr[j-1]=arr[j];
                    arr[j]=temp;
                }
            }
        }
        return arr;
    }
}

 

*위의 세 정렬에 대한 성능표

반복문을 도는 횟수는 Bubble=Selection>=Insert

정렬 알고리즘 최적의 경우(오메가) 평균적인 경우(델타) 최악의 경우(빅오)
Bubble sort n^2 n^2 n^2
Selection Sort n^2 n^2 n^2
Insertion Sort n n^2 n^2 

- 정리

버블정렬과 선택정렬은 단순하고 직관적이며, 구현이 쉽지만, 비효율적이므로 잘 사용하지 않는다.

삽입정렬은 정렬이 어느정도 되어있는 경우에 최소한의 비교와 연산이 이루어지므로, 성능이 좋아 사용된다.

 

- 프로그래머스 정렬 문제 [ H-Index ]

https://school.programmers.co.kr/learn/courses/30/lessons/42747

 

프로그래머스

코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.

programmers.co.kr

 

'알고리즘' 카테고리의 다른 글

[알고리즘] 조합(Combination)  (0) 2022.12.02
[알고리즘] 슬라이딩 윈도우  (0) 2022.09.15
[알고리즘] 힙(Heap)  (0) 2022.07.21
[알고리즘] 해시(Hash)  (0) 2022.07.15
[알고리즘] 우선순위큐(Priority Queue)  (0) 2019.05.27

[힙(Heap)]

1. Heap이란?

  • 완전 이진 트리의 일종으로 우선순위 큐를 위하여 만들어진 자료구조이다.
  • 여러 값 중, 최대값과 최소값을 빠르게 찾아내도록 만들어진 자료구조로 반정렬 상태(느슨한 정렬 상태)이다.
  • 힙 트리는 중복된 값을 허용한다. (이진 탐색 트리는 중복값 허용X)

 

※ 완전 이진 트리의 특징

완전이진트리

1) 완전 이진 트리는 마지막 레벨을 제외하고 모든 레벨이 완전히 채워져 있다. 

2) 마지막 레벨은 꽉 차 있지 않아도 되지만, 노드가 왼쪽에서 오른쪽으로 채워져야 한다.

3) 완전 이진 트리는 배열을 사용해 효율적으로 표현 가능하다.

 

※ 반정렬 상태

트리 구조를 배열에 저장한 것이므로 배열로만 보았을 때는 완전히 정렬된 상태로 보이지는 않는다는 것을 의미.

 

2. 힙의 종류

1) 최대 힙(max heap)

부모 노드의 키 값이 자식 노드의 키 값보다 크거나 같은 완전 이진 트리

 

2) 최소 힙(min heap)

부모 노드의 키 값이 자식 노드의 키 값보다 작거나 같은 완전 이진 트리

 

최대힙과 최소힙

3. 힙의 구현

  • 힙을 저장하는 표준적인 자료구조는 배열이다.
  • 구현을 쉽게 하기 위하여 배열의 첫번째 인덱스인 0은 사용되지않는다.
  • 특정 위치의 노드번호는 새로운 노드가 추가되어도 변하지 않는다.
    예를 들어 루트 노드의 오른쪽 노드의 번호는 항상 3이다.
    • 왼쪽 자식의 인덱스 = (부모의 인덱스)*2
    • 오른쪽 자식의 인덱스 = (부모의 인덱스)*2+1
    • 부모의 인덱스 = (자식의 인덱스)/2
    • 힙에서의 부모 노드와 자식 노드의 관계

 

4. 힙의 삽입

  • 힙에 새로운 요소가 들어오면, 새로운 노드를 힙의 마지막 노드에 이이서 삽입한다.
  • 새로운 노드를 부모 노드들과 교환해서 힙의 성질을 만족시킨다.

예시)

※ 같은 레벨의 자식노드들 끼리의 크기는 중요하지 않다.

 

5. 힙의 삭제

1) 최대 힙에서 최대값은 루트 노드이므로 루트 노드가 삭제된다.(최소 힙에서는 최소값인 루트노드가 삭제

2) 삭제된 루트 노드에는 마지막 노드를 가져온다.

3) 힙을 재구성한다. 

예시)

 

6. JAVA Priority Queue 사용법

1) Priority Queue 선언

import java.util.PriorityQueue; //import

//int형 priorityQueue 선언 (우선순위가 낮은 숫자 순)
PriorityQueue<Integer> priorityQueue = new PriorityQueue<>();
//int형 priorityQueue 선언 (우선순위가 높은 숫자 순)
PriorityQueue<Integer> priorityQueue = new PriorityQueue<>(Collections.reverseOrder());

//String형 priorityQueue 선언 (우선순위가 낮은 숫자 순)
PriorityQueue<String> priorityQueue = new PriorityQueue<>(); 
//String형 priorityQueue 선언 (우선순위가 높은 숫자 순)
PriorityQueue<String> priorityQueue = new PriorityQueue<>(Collections.reverseOrder());

 

2) Priority Queue 값 추가

priorityQueue.add(1);     // priorityQueue 값 1 추가
priorityQueue.add(2);     // priorityQueue 값 2 추가
priorityQueue.offer(3);   // priorityQueue 값 3 추가

자바의 우선순위 큐에 값을 추가하고 싶다면 add(value) 또는 offer(value)라는 메서드를 활용하면 된다.

  • add() : 삽입에 성공하면 true를 반환, 큐가 꽉 찬 경우 IllegalStateException을 발생
  • offer() : 값 추가 성공 시에 true를 반환, 값 추가 실패 시 false를 반환

 

3) Priority Queue 값 삭제

priorityQueue.poll();       // priorityQueue에 첫번째 값을 반환하고 제거 비어있다면 null
priorityQueue.remove();     // priorityQueue에 첫번째 값 제거
priorityQueue.clear();      // priorityQueue에 초기화
  • poll() : 큐가 비어 있을 경우 null 반환
  • remove() : 큐가 비어 있는 경우 NoSuchElementException 에러 발생
  • clear() : 큐 비우기

4) Priority Queue에서 우선순위가 가장 높은 값 출력

PriorityQueue<Integer> priorityQueue = new PriorityQueue<>();//int형 priorityQueue 선언
priorityQueue.offer(2);     // priorityQueue에 값 2 추가
priorityQueue.offer(1);     // priorityQueue에 값 1 추가
priorityQueue.offer(3);     // priorityQueue에 값 3 추가
priorityQueue.peek();       // priorityQueue에 첫번째 값 참조 = 1

 

- 프로그래머스 Heap 관련 문제 ( Level2 / 더 맵게 ) 

 

프로그래머스

코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.

programmers.co.kr

 

- 참고 지식

※ 자료구조 heap과 메모리영역의 heap의 관련성?

과거에 일부 개발자들이 가용 메모리를 heap이라 부르기 시작한 것 같고, 특별한 이유는 없는 것으로 보임.

결국 stack 메모리는 stack 자료구조로 된 것이 맞지만,

heap 메모리는 heap(우선순위큐) 자료구조와 아무런 연관성이 없다고 볼 수 있다.

 

- 출처

[해시(Hash)]

1. 해시의 등장배경

배열은 내부 인덱스를 이용하여 자료의 검색이 한번에 이루어지기 때문에 빠른 검색 속도를 보이는 반면 

데이터의 삽입, 삭제 시 많은 데이터가 밀리거나 빈자리를 채우기 위해 이동해야하기 때문에 많은 시간이 소요된다.
반면에 연결리스트는 삽입, 삭제 시 인근 노드들의 참조값만 수정해줌으로써 빠른 처리가 가능했었다.
단, 처음 노드 마지막 노드 이외의 위치에서 데이터를 삽입, 삭제할 경우나 데이터를 검색할 경우에는 해당 노드를
찾기 위하여 처음부터 순회 검색을 해야하기 때문에 데이터의 수가 많아질수록 효율이 떨어질 수 밖에 없는 구조다.
이러한 한계를 극복하기 위하여 제시된 방법이 바로 해시(Hash)이다.

 


2. 해시란?

해시는 내부적으로 배열을 사용하여 데이터를 저장하기 때문에 빠른 검색 속도를 가진다.
그리고 데이터의 삽입과 삭제 시 기존 데이터를 밀어내거나 채우는 작업이 필요없도록 특별한 알고리즘을 이용하여 

데이터와 연관된 고유한 숫자를 만들어 낸 뒤 이를 인덱스로 사용한다.


특정 데이터가 저장되는 인덱스는 그 데이터만의 고유한 위치이기때문에 삽입 시 다른 데이터의 사이에 끼어들거나
삭제 시 다른 데이터로 채울 필요가 없으므로 삽입과 삭제 시 데이터의 이동이 없도록 만들어진 구조이다.

* 해시의 특징

  • 해시는 검색과 저장에서 아주 우수한 성능을 보인다
  • 해시의 핵심은 KEY, VALUE를 함께 저장하는 자료구조이다.

 

3. 해시의 충돌

key를 해시함수를 통해서 해시코드로 변환시키고 이 해시코드를 인덱스로 사용하여 value를 저장하는데,

이때 충돌(Collision)이 발생할 수 있다. 다음의 예시를 보자.

해시 충돌

John Smith와 Sandra Dee라는 key가 해시함수를 통해 해시코드로 변환되었는데 우연히 같은 코드로 변환된 것이다.
해시충돌은 완벽히 피할수는 없지만 개선방법은 2가지 존재한다.


1) 충돌해결 - Separate Chaining 기법

Chaining 기법

John Smith가 들어가 있는데 그 공간에 또 Sandra Dee가 들어갈 때 Collision이 발생한다. 이 때 Sandra의 value를 기존 John의 뒤에 연결리스트를 이용해 체인처럼 이어 붙혀준다. 152번지에 John과 Sandra의 정보가 함께 존재하도록 한것이다.

* 장점

  • 한정된 저장공간을 효율적으로 사용할 수 있다.
  • 해시함수에 대한 의존성이 상대적으로 적다.
  • 메모리 공간을 미리 잡아 놓을 필요가 없다.(그때그때 이어붙이기 때문)

* 단점

  • 한 Hash에만 자료가 계속들어간다면(쏠린다면) 검색 효율이 떨어진다.(최악의 경우 O(n))
  • 외부 저장공간을 사용한다. 

 

2) 충돌해결 - Open Addressing(개방주소법)
개방주소법은 데이터의 해시(hash)가 변경되지 않았던 Chaining과는 달리 비어있는 해시를 찾아 데이터를 저장하는 기법이다. 즉, 해시 충돌이 일어나면 다른 버킷에 데이터를 삽입한다.

따라서 개방주소법에서의 해시테이블은 1개의 해시와 1개의 값이 매칭되어 있는 형태로 유지된다.

Open Addressing 기법 중 선형 탐색

* 장점

  • 추가 저장공간이 필요없다.
  • 저장할 데이터가 적을 때 더 유리하다.

* 단점

  • 해시 함수의 성능에 전체 해시테이블의 성능이 좌지우지 된다.
  • 데이터의 길이가 늘어나면 그에 해당하는 저장소를 마련해 두어야한다.

* 개방주소법 3가지 방법
① 선형 탐색(Linear Probing) : 해시충돌 시 다음 버켓, 혹은 몇 개를 건너뛰어 데이터를 삽입한다.
② 제곱 탐색(Quadratic Probing) : 해시충돌 시 제곱만큼 건너뛴 버켓에 데이터를 삽입(1,4, 9.16...)
③ 이중 해시(Double Hashing) : 해시충돌 시 다른 해시함수를 한 번 더 적용한 결과를 이용함.

 

4. HashMap vs HashTable(파이썬에서는 Dictionary)

1) HashMap

  • null을 key, value로 사용할 수 있다.
  • 동기화를 지원하지 않는다.(멀티스레드가 동시에 사용할 때 문제가 발생할 수 있다) => 대신 실행속도가 빠르다.

2) HashTalbe

  • key가 null이 될 수 없다.
  • value가 null이 될 수 없다.
  • 동기화를 지원

=>HashMap은 HashTalbe의 신버전

 

 

5. HashMap vs HashSet 

1) 정의
HashMap은 Map 인터페이스의 구현체로, HashTable과 유사한 자료구조로 데이터를 저장한다.
HashSet은 Set 인터페이스의 구현체로, 내부적으로 HashMap을 사용하기 때문에 HashTable과 유사한 자료구조로 데이터를 저장한다.

2) 데이터 저장 형태
HashMap은 Key-Value 쌍 형태로 데이터를 저장하며, Key와 Value의 mapping을 유지하고 있다.
HashSet은 객체 그 자체를 저장한다. 위에서 HashMap을 내부적으로 사용한다고 했는데,
Key 값으로는 삽입되는 객체 그 자체를, Value 값으로는 HashSet 내부 구현 코드에서 미리 선언해둔 dummy 객체를 사용한다.

3) 중복 허용 여부
HashMap은 중복 key 값을 허용하지 않지만, Value값은 허용한다.
HashSet은 객체 자체를 데이터로 저장하기 때문에 중복을 허용하지 않는다.

 

4) 기본적인 사용법

- HashMap

HashMap<Integer,String> map = new HashMap<>();//new에서 타입 파라미터 생략가능
map.put(1,"사과"); //값 추가
map.put(2,"바나나");
map.remove(1); //key값 1 제거
map.clear(); //모든 값 제거

//반복문을 이용한 출력
//entrySet() 활용
for (Entry<Integer, String> entry : map.entrySet()) {
    System.out.println("[Key]:" + entry.getKey() + " [Value]:" + entry.getValue());
}
//[Key]:1 [Value]:사과
//[Key]:2 [Value]:바나나
//[Key]:3 [Value]:포도

//KeySet() 활용
for(Integer i : map.keySet()){ //저장된 key값 확인
    System.out.println("[Key]:" + i + " [Value]:" + map.get(i));
}
//[Key]:1 [Value]:사과
//[Key]:2 [Value]:바나나
//[Key]:3 [Value]:포도

//keySet().iterator()
Iterator<Integer> keys = map.keySet().iterator();
while(keys.hasNext()){
    int key = keys.next();
    System.out.println("[Key]:" + key + " [Value]:" +  map.get(key));
}

- HashSet

HashSet<Integer> set = new HashSet<Integer>();//HashSet생성
set.add(1); //값 추가
set.add(2);
set.remove(1);//값 1 제거
set.clear();//모든 값 제거

//출력
Iterator iter = set.iterator();	// Iterator 사용
while(iter.hasNext()) {//값이 있으면 true 없으면 false
    System.out.println(iter.next());
}

//검색
System.out.println(set.contains(1)); //set내부에 값 1이 있는지 check : true

 

  •  출처

+ Recent posts