본문 바로가기

카테고리 없음

[JAVA]비선형 자료구조, 선형자료구조, 이진트리(Binary tree), preorder, inorder, postorder, levelorder, 비선형자료구조 공부법

자료구조란?(Data Structure)

-자료를 효율적으로 관리하기 위한 구조

(여기서 관리란 저장, 삭제, 탐색 등을 말함)

-알고리즘과 밀접한 관계에 있다.

 

비선형 vs 선형 자료구조

선형 자료구조 비선형 자료구조
자료에 순서가 있다.(set, Map은 명시적인 순서x) 자료에 순서가 없다.
순서를 중요시 하지 않는 자료구조여도,
자연스럽게 읽는 순서(natural order)가 있다.
단순히 자료를 한번 순회하는 데에도 방법론이 필요하다.
(DFS, BFS가 많이 쓰인다.)

 

*선형 자료구조:

-배열

-연결리스트

-스택, 큐, 데크

-해시 테이블

 

*비선형 자료구조:

-트리

-그래프

-힙/우선순위 큐

-트라이

 

*트리:

제로베이스

-루트 노드를 시작으로, 뻗어 나가는 자료구조

-하나의 노드에서 다른 노드로 이동하는 경로는 유일

-노드가 N개인 트리의 Edge의 수는 N-1 개

-모든 노드는 서로 연결돼 있음

 

*이진트리(Binary Tree):

각 노드는 최대 2개의 자식을 가질 수 있음

노드는 좌우가 구분됨

 

포화 이진 트리 완전 이진 트리
모든 레벨에서 노드들이 꽉 채워져 있는 트리
(주로 포화 이진트리를 많이 쓴다.)
마지막 레벨을 제외한 노드들이 모두 채워진 트리

 

*이진 트리의 순회: 모든 노드를 빠뜨리거나 중복하지 않고 방문하는 연산

-순회의 종류:

1.전위 순회(Preorder), 중위 순회(Inorder), 후위 순회(Postorder) ->DFS

2.레벨 순회(level) -> BFS

순서대로 preorder, inorder, postorder

 

-대표적으로 두 가지 탐색 방법이 사용

1. 깊이우선탐색(Depth-First Search;DFS):

->트리에서 빈출

-찾고자 하는 노드가 리프 노드에 있을 때 사용

-모든 노드를 반드시 다 탐색해야 할 때 사용

 

2. 너비우선탐색(Breadth-First Search; BFS):

-조건에 맞는 가장 가까운 노드를 찾을 때 사용

-모든 노드를 다 탐색할 필요가 없을 때 사용

 

*그래프:

-노드와 연결이 자유롭게 이루어지는 자료구조

-트리와 달리 회로(cycle)가 발생할 수 있어 더 복잡(visited 이용하기)

-대표적으로 두 가지 탐색 방법이 사용된다.

1. DFS 2.BFS-> 그래프에선 BFS가 많이 사용

 

DFS와 BFS 문제의 허들

*DFS:

-재귀 구현을 잘해야 함(스택이용하지 말 것)

-탈출 조건을 잘 이해해야 함(백트래킹 최적화)

*BFS:

-큐를 잘 이해하고 있어야 함

-동적계획법(DF)와 결합되는 경우가 많음

-BFS를 잘 이해해야 Dijkstr 알고리즘 구현가능

 

비선형 자료구조 학습 순서

1. DFS를 잘 이해한다.

-> DFS 이해가 어려우면 재귀부터 다시 학습

2.BFS를 잘 이해한다.

->BFS 이해가 어려우면 큐 개념부터 다시 학습

3. DFS와 BFS 기초 문제들을 풀이한다.

->백준온라인저지나 프로그래머스 문제 활용

4. 더 난도가 있는 문제들(백트래킹, DP, 비트마스킹, 해싱 이용한) 풀이한다.

5. 고급 알고리즘에 도전한다.

(다익스트라, 벨만-포드 -> 최단거리, 크루스컬, 프림->MST)

 

오늘 푼 문제(Tree):

//종이접기
//종이를 반으로 접었을 때, 안으로 파인 부분은 0, 볼록 튀어나온 부분은 1이라고 하자.
//종이를 N번 접었을 때의 접힌 상태를 출력하는 문제를 작성하세요.
public class Main {
    public static void solution(int n){
        int[] binaryTree = new int[(int)Math.pow(2,n)]; //꼭 사이즈 딱 맞추지 않아도 된다.

        binaryTree[0] = 0; //root는 무조건 0이기 때문
        for (int i = 0; i < (int)Math.pow(2, n-1)-1; i++) {
            binaryTree[i*2 +1] = 0; //왼쪽
            binaryTree[i*2 +2] = 1;//오른쪽
        }

        inOrder(binaryTree, 0);
        System.out.println();
    }

    public static void inOrder(int[] arr, int idx){
        int left = 2*idx +1;
        int right = 2*idx +2;

        if (left<arr.length-1){  //배열의 마지막 부분은 사용하지 않기에 -1해줌
            inOrder(arr, left);
        }
        System.out.print(arr[idx]+" ");

        if (right< arr.length-1){// 여기도
            inOrder(arr, right);
        }
    }
    public static void main(String[] args) {
        //Test code
        solution(1);
        solution(2);
        solution(3);
    }
}

 

//각각의 edge에 가중치가 있는 포화 이진 트리가 있다.
//루트에서 각 리프까지의 경로 길이를 모두 같게 설장하고,
//이 때, 모든 가중치들의  총합이 최소가 되도록 하는 프로그램을 작성하세요.
class BinaryTree{
    int h;
    int[] arr;
    int res; //가중치 합을 담을 result

    public BinaryTree(int h, int[] w){
        this.h = h;
        arr = new int[(int)Math.pow(2,h+1)]; //1개는 안씀
        res = 0;

        for (int i = 2; i < (int)Math.pow(2, h+1); i++) { //i가 2에서부터 시작하는 이유
            //맨 앞은 안쓰고, 노드가 아닌 링크(가지)를 세는 것이기 때문에 idx 0,1은 사용하지 x
            arr[i] = w[i-2];
        }
    }

    public int dfs(int idx, int h){
        if(this.h == h){
            res += arr[idx];
            return arr[idx];
        }

        int left = dfs(idx*2, h+1);
        int right = dfs(idx*2+1, h+1);
        res += arr[idx] + Math.abs(left-right); //Mast.abs -> 절대값 기준
        return arr[idx] + Math.max(left, right);

    }
}
public class Practice2 {
    public static void solution(int h, int[] w){
    BinaryTree bt = new BinaryTree(h,w);
    bt.dfs(1,0);
        System.out.println(bt.res);
    }

    public static void main(String[] args){
        //Test code
        int h =2; //트리의 높이
        int[] w = {2,2,2,1,1,3}; //트리에서 각각 에지의 가중치
        solution(h,w);
        System.out.println();

        h = 3;
        w = new int[]{1,2,1,3,2,4,1,1,1,1,1,1,1,1,1};
        solution(h,w);
    }
}

 


마지막 문제 코드의 이해가 어렵고 아직 잘 안된다.

먼저, 트리의 구조를 명시적으로 그리고 어레이에 할당하는 것이 익숙해지게 해야할 거 같다.

DFS를 구현하는 방법을 완전히 이해하기까지 시간이 걸릴 거 같으니,

먼저 코드를 암기하고 문제에 적용하며 익숙해지게 해야할 거 같다.

트리를 구현하는 코드의 앞 부분이 거의 동일하기에,

다시 한 번 문제에서의 트리를 손으로 그려보고, 코드를 구현해야겠다.

순회라는 개념을 글로 보는 것보다, 코드로 구현하는 것이 더 명시적으로 이해가 갔다.

(항상 개념보다 코드 구현이 어려웠는데, 처음으로 코드 구현으로 개념이 이해가 더 갔다.)