|  파일 시스템과 데이터베이스 관리시스템

- 영구적인 데이터 저장을 위해서는 보조기억장치에 파일 형식으로 저장을 하는 것이 필요하다.

- 다만, 이런 파일 시스템은 응용 프로그램의 구조와 환경에 의존적이고(데이터 종속성),

  그렇기에 같은 데이터여도 구조가 다르다면 여러 개로 중복되어 저장되는(데이터 중복성) 단점이 있다.

- 데이터베이스는 이러한 파일시스템의 데이터 종속성과 데이터 중복성을 제거하기 위해 만들어졌다.

1. 파일 시스템 vs 데이터베이스 관리 시스템

  파일 시스템 데이터베이스 관리 시스템
정의 컴퓨터에서 자료를 쉽게 찾을 수 있도록 
파일 형식으로 보관 및 관리하는 체계
데이터베이스를 체계적으로 관리하기 위한 시스템
종류 FAT/NTFS(windows), ext(linux), APFS(macOS) 구분1 : RDBMS(정형), NoSql(비정형)
구분2 : 계층형/망형/관계형/객체지향형/객체관계형
장점 처리 속도가 빠르다.
구현이 간편하다.
비용이 저렴
데이터 종속성과 데이터 중복성을 해결
일관성, 무결성, 보안성 유지
데이터 공유 등..
단점 데이터 종속성*
데이터 중복성*
DB전문가의 부족
전산화 비용의 증가
대용량 디스크로의 집중적인 접근으로 과부하 발생
파일의 백업과 회복이 어려움
시스템이 복잡

 

1-1. 파일 시스템의 문제점

데이터의 종속성 응용 프로그램과 데이터간의 상호 의존관계 

* 프로그램 명세에는 보조기억장치에 들어가는 프로그램의 파일 구조 및 접근방식에 대해서 명시되어 있다.
* 프로그램은 이러한 명세에 따라서 동작하기 때문에
* 만약 데이터의 구성 방법 및 접근 방법을 수정해야할 경우, 이에 맞춰 프로그램도 수정되어야 한다.
데이터의 중복성 한 시스템 내에 내용이 같은 데이터가 중복되게 저장 관리되는 것

* 현실 세계에서는 여러 개의 프로그램이 하나의 데이터를 동시에 사용하는 경우가 있다.
* 예를 들어,
   대학교 도서관에서 책을 대여 기록을 담는 프로그램A가 있고,
   학교 재학생들의 명단을 담고 있는 프로그램 B가 있다고 하면 
   프로그램 A와 프로그램B 모두 학교 재학생들의 이름과 학번을 데이터로 사용할 것이다.
* 이와 같이 여러 개의 프로그램이 같은 데이터를 중복해서 저장/관리할 경우, 
   다수의 프로그램들을 시간차를 두고 수정하는 작업이 필요할 것이다. (동시 수정x)

 

1-2. DBMS의 논리적 독립성 / 물리적 독립성

논리적 독립성 응용 프로그램과 DB를 독립 시킴으로써,
데이터의 논리적 구조를 변경시키더라도 응용 프로그램은 변경되지 않음
물리적 독립성 응용 프로그램과 보조기억장치같은 물리적 장치를 독립시킴으로써,
DBMS의 성능 향상을 위해 물리적 장치를 추가해도 프로그램에는 영향을 주지 않음.

 

|  DB와 DBMS, RDMBS

데이터베이스 데이터의 저장소
DBMS(데이터베이스 관리시스템) 데이터베이스를 운영하고 관리하는 시스템

- DB의 특징

실시간 접근성 실시간으로 사용자의 요청이 있을 때 수 초 내로 결과를 제공한다
계속적인 변화 데이터베이스의 내용은 어느 한 순간의 상태이나, 데이터의 값은 시간에 따라 항상 변화한다.
동시 공유 데이터베이스는 서로 다른 업무 또는 사용자에게 동시 공유된다.

* 동시(= 병행, concurrent) : 데이터베이스에 접근하는 프로그램이 여러 개임을 뜻한다.
내용에 따른 참조 데이터베이스에 저장된 데이터는 데이터의 물리적인 위치가 아니라 데이터 값에 따라 참조한다.
데이터 독립성 데이터의 논리적 구조를 변경시켜도 응용 프로그램은 변경되지 않는다.

* 응용 프로그램과 데이터베이스를 독립시킨 것을 의미한다.(데이터 종속성 해결)

- RDBMS

RDBMS(관계형 데이터베이스) 정형화된 데이터를 관리하는 시스템으로, 가장 많이 사용되는 DBMS이다.

2-1. RDMS의 구성

- 테이블의 행과 열로 구성된다.

  열(columm), 필드(field),속성(attribute) ↓
행(row),
튜플(tuple), ---->
레코드(record)
     
     
     

2-2. RDMS의 종류

- 종류 : 오라클, MySQL, MariaDB, Microsoft SQL Sever, PostgreSQL, IBM DB2....

 

 

[ 참고 및 출처 ]

부트캠프의 강의를 듣고 정리한 내용입니다.

DBMS의 필수 기능 및 장단점 https://floating-library.tistory.com/75

데이터의 종속성과 중복성 https://rain-bow.tistory.com/20

 

|  날짜, 시간 관련 클래스

1. 날짜 데이터는 언제 쓸까?

- 날짜 및 시간에 관한 데이터는 아래의 예시와 같이 다양한 웹 or 앱 서비스에서 사용하고 있다. 

- 일반적인 회원가입일자 
- 쇼핑몰의 주문일자
- 그룹웨어 회의일자 및 시간/장소대여일자 
- 모바일앱 특정시간에 알람 
- 등등

2. Java의 날짜 및 시간 관련 클래스

- 날짜 객체는 JDK업데이트 과정에서 Data -> Calender -> LocalDate로 발전하였다.

(1) Date today = new Date();  와 같이 Date 객체만을 사용해서 날짜를 표현

(2) Date today = Calendar.getInstance().getTime(); - Calendar를 통해 싱글톤 방식으로 인스턴스 생성하여 사용

(3) LocalDate today = LocalDate.now(); - Calendar를 보완한 LocalDate를 사용

- JDK8 이후부터는 공식적으로 Calendar는 deprecated(더 이상 사용하지 않는) 객체로 지정되었지만 

  국내의 현실 개발 환경에서는 아직까지 Calendar를 사용하는 경우도 많으며, Date객체만 사용하는 경우도 더러 있다고 한다.

  JAVA 8 이전 (deprecated) JAVA 8 이후
날짜 클래스 java.util의 Data와 Calendar java.time의 LocalDate/ LocalTime/ LocalDateTime
포맷 클래스 SimpleDataFormat DateTimeFormatter
*TimeStamp.of()를 쓰기도 한다.
제점/보 Thread-safe X
0부터 시작하는 월 지정 인덱스
Data, Calendar 클래스 사용의 혼란 
Thread-safe O
월이 1, 2, 3...으로 시작한다

isBefore(), isAfter()와 같은 메소드 추가

 

|  Date와 Calendar  사용

- Calendar의 단점 중에 하나가 월을 0부터 표현한다는 것이었다.

- 아래 코드를 보면 알 수 있겠지만, 연도나 일은 그럼에도 1부터 시작하는 걸 볼 수 있다.

import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;

public class CalendarTest {
    public static void main(String[] args) {
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd E요일 HH:mm:ss:SSS");

        Date today = Calendar.getInstance().getTime();
        System.out.println(simpleDateFormat.format(today));

        Calendar calendar = Calendar.getInstance();
        int year = calendar.get(Calendar.YEAR);
        int month = calendar.get(Calendar.MONTH);
        int day = calendar.get(Calendar.DATE);
        System.out.printf("%d년 %d월 %d일\n", year, month, day); // 월이 0부터 시작
        month += 1;
        System.out.printf("%d년 %d월 %d일\n", year, month, day); // 1을 더해주어야 한다.

    }
}

>> 결과

2022-08-10 수요일 00:10:32:924
2022년 7월 10일
2022년 8월 10일

 

|  LocalDate / LocalTime / LocalDateTime 사용

- 간단하게 생성하는 법, 연/월/일을 얻는 법을 작성해보았다. 

더보기

|  LocalDate를 활용해 달력 출력하기

- 먼저 구하려는 날짜의 LocalDate를 생성하고, lenthOfMonth()로 그 달의 일수를 구한다.

// 해당 달의 일수 구하기 : lengthOfMonth()
LocalDate date = LocalDate.of(2012,12,1);
int lengthOfMonth = date.lengthOfMonth();

- 해당 월의 1일자가 몇 요일인지, getDayOfWeek().getValue()로 구한다.

int dayOfWeek = date.getDayOfWeek().getValue(); //월화수목.. = 1234..

- 주차를 구하는 방법은 임의로 만들었는데, (전체월 일수 + (7 - 1일자 요일번호))를 7로 나누어 반올림하였다.

int weeksOfMonth = Math.round((lengthOfMonth + (7 - dayOfWeek))/7f);

- 이 세가지를 구한 후, 2차원 배열을 만들고, 1일자의 시작 요일에 맞춰 입력 후, 출력은 이중 for문으로 했다.

- 전체 코드

- 출력 결과  *한글와 숫자깨짐은 무시한 결과다.

[달력 출력 프로그램]
달력의 년도를 입력해 주세요.(yyyy):2022
달력의 월을 입력해 주세요.(mm):7
[2022년 07월]
일 월 화 수 목 금 토  
               01 02 
03 04 05 06 07 08 09 
10 11 12 13 14 15 16 
17 18 19 20 21 22 23 
24 25 26 27 28 29 30

 

|  Date -> LocalDateTime 또는 LoalDateTime -> Date로 변환 

- JDK8부터 Date객체에 from() 메소드가 추가되어 LocalDateTime -> Date로 변환이 가능해졌다.

- Date -> LocalDateTime

LocalDateTime.ofInstant(date.toInstant(), /* ZoneId */);

- LocalDateTime -> Date

Date.from(local.atZone(/*ZoneId*/).toInstant())

 

- atZone()을 넣는 것은 시간대를 구분하기 위해서 넣는 것이다. (아래는 컴퓨터의 시간대 적용)

import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.Calendar;
import java.util.Date;

public class LocalDateTest {
    public static void main(String[] args) {

        // A. Date와 LocalDateTime으로 현재시각 구하기
        // (1) Date 타입의 날짜
        Date date = Calendar.getInstance().getTime();
        // (2) LocalDateTime 타입의 날짜
        LocalDateTime localDateTime = LocalDateTime.now();

        // B. LocalDateTime -> Date
        Date localToDate = Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant());

        // C. Date -> LocalDateTime
        LocalDateTime dateToLocal = LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault());

    }
}

 

 

[ 참고 및 출처 ]

- 부트 캠프 강의를 듣고 정리한 내용입니다.

- 날짜 타입 변환 관련 :  https://recordsoflife.tistory.com/336

- [기본기를 쌓는 정아마추어 코딩블로그:티스토리], https://jeong-pro.tistory.com/163 

- [슬기로운 개발생활:티스토리] https://dev-coco.tistory.com/31

- [삼바:티스토리], https://samba-java.tistory.com/18

|  알고리즘이란?

알고리즘이란, 어떤 문제를 해결하기 위한 절차나 방법을 말한다.

- 알고리즘의 조건

입력 데이터의 입력
출력 처리 후 출력
명확성 동작의 흐름(flow)에 대한 명확성
유한성 정해진 시간 및 공간 내에서의 처리
효율성 같은 동작을 하더라도 시간 및 공간 면에서 보다 효율적이어야 한다.

 

|  알고리즘은 결국 정확성과 시간 복잡도, 공간 복잡도를 말한다.

- 어떻게 보다 적은 자원을 효과적으로, 정확하게 만들어 내느냐가 알고리즘의 핵심

 

|  알고리즘 정리 개요 

- 앞으로 정리할 알고리즘 목록입니다.

- (글 작성 후 링크 업로드 예정)

정렬  
이진탐색 / 투 포인터  
그리디 알고리즘  
분할 정복 / 다이나믹 프로그래밍  
백 트래킹  
최단 경로  
최소 신장 트리  

 

 

[ 참고 및 출처 ]

부트 캠프 강의를 들은 후 정리한 내용입니다.

|  Java.lang 패키지

- 가장 기본이 되는 클래스를 담고 있으며, import 없이 사용 가능하다.

- Oracle에서 제공하는 java.lang 패키지에 대한 상세 정보는 아래 링크에 (JDK8 기준)

   https://docs.oracle.com/javase/8/docs/api/java/lang/package-summary.html

- 아래 표는 위 링크에서 소개하는 java.lang의 인터페이스와 클래스 중 자주 사용할만한 것만 일부를 가져온 표이다.

Interface Class
Comparable Object
Cloneable Math
Runnable Process
Readable Thread
Thread.UncaughtExceptionHandler String
Iterable System
... Character, Integer, Long....

 

|  Object

메소드 내용
clone 현재 객체를 복사한 객체를 반환
equals 참조되는 주소값을 비교 (* 주의 : null값은 비교할 수 없다)
getClass 현재 객체타입의 런타임 객체를 반환
hashCode 객체의 해쉬코드를 반환
toString java.lang.Object@해쉬코드를 반환
오버라이딩하여 주로 사용

 

|  String

1. String은 힙에 저장되며, 두 가지 방식으로 저장된다.

종류 저장 영역
문자열 리터럴 Heap 영역 내 "String Constant Pool"에 생성
문자열 객체 Heap 영역 내 별도의 객체로 생성
// s1과 s2는 같은 곳을 가리킨다.
String s1 = "Spring";
String s2 = "Spring";

// s3와 s4는 다른 곳을 가리킨다.
String s3 = new String("Spring");
String s4 = new String("Spring");

 

2. String 메소드들

contains trim compareTo toString
indexOf
lastIndexOf
substring equals
equalsIgnoreCase
toLowerCase
toUpperCase
startsWith
endsWith
split valueOf toCharArray
length charAt replace
replaceAll
concat *

* concat : 문자열을 덧붙이는 메소드로 +를 사용하는 것과 동일하다.(자바에서는 잘 사용하지 않는다)

  pf. mariaDB, mysql에서 문자열을 조합할 때 concat('a', 'b', 'c')를 사용한다. (Oracle은 ||를 통해 문자열을 조합)

 

3. 자바는 기본적으로 UTF-16 인코딩 방식을 다른다.

더보기

※ EUC-KR과 UTF-8방식에 대하여 

> 확장된 아스키 코드와 유니코드의 역사
유니코드 이전에는 각 나라별로 확장된 아스키코드(Extended Ascii)를 사용했다.(아스키 코드의 문자는 1byte)

- 나라별로 인코딩 방식이 상이하다보니 전 세계의 데이터가 모두 다르게 읽히는 문제가 발생했다.
- 이를 해결하고자 유니코드를 만들었다. (유니코드의 문자는 2byte)
- 하지만 유니코드는 영문자의 경우 사용하는 메모리 공간이 1byte -> 2byte가 된다는 단점이 있어,
  영어권 같은 나라에서는 유니코드를 사용하는 것이 큰 메리트가 되지 않았다.
- 그래서 나온 것이 UTF-8. UTF-8은 가변 길이를 제공하여, 각 나라의 언어별로 최적화된 문자 크기를 할당한다.
  ex. 영문자의 경우 1byte, 한글의 경우 3byte

> EUC-KR과 UTF-8. 
- EUC-KR은 유니코드가 나오기 이전에 사용하던 한글 인코딩 방식(Extended Ascii)이다.
  * 자바의 KSC5601기법은 곧 EUC-KR 인코딩을 말하며, 윈도우OS에서 MS949와 동일한 기법을 사용한다.
  * 윈도우의 기본 인코딩 방식은 MS949다. 이클립스를 키면 늘 이 인코딩이 디폴트로 설정되어 있어 UTF-8로 바꾸는게 필요

- EUC-KR을 사용하면 온전한 한글을 만들기가 어렵다. (ex.'뷁' 이라는 문자가 없음)
- 당연한 얘기지만, 인코딩 방식이 EUC-KR일 때, 디코딩을 UTF-8로 하면 문자가 깨지게 된다. (제대로 인식을 못함)
- 그렇기에 항상 에디터를 쓸 때 사전에 인코딩 방식을 UTF-8로 설정을 해두는 것이다.

 

4. 자바에서 인코딩을 변환하는 메소드 getBytes()

import java.nio.charset.Charset;

public class StringTest {
    public static void main(String[] args) {
        String sample = "행복한개발자가되자"; // 설정에서 UTF-8 방식으로 바꾼 상태

        try {
            System.out.println("Default Encoding : " + Charset.defaultCharset());
            byte[] default_encoding = sample.getBytes();
            byte[] euc_kr = sample.getBytes("EUC-KR");
            System.out.println(">> default decoding : " + new String(default_encoding));
            System.out.println(">> EUC-KR decoding : " + new String(euc_kr));
        } catch (Exception e) {
            System.out.println("Encoding Error");
            System.out.println(e.getMessage());
        }
    }
}

>> 결과

Default Encoding : UTF-8
>> default decoding : 행복한개발자가되자
>> EUC-KR decoding : �ູ�Ѱ����ڰ�����

 

|  StringBuffer

1. 문자열 불변 법칙

String 불변의 법칙이란?
- 문자열 할당 후 수정이 있을 때, 기존 메모리에서 데이터를 수정하지 않고,
- 새로운 메모리 공간을 할당하여 그곳의 주소를 가리키도록 하는 방식

>> 문자열의 변경(추가/삭제/수정)이 빈번한 경우, 단순 덧셈 시 시간/공간복잡도에 영향을 준다.

- 앞서 문자열 리터럴의 경우, 힙의 string constant pool에 저장되며,

  서로 다른 참조변수(ex. s1, s2)가 같은 메모리 공간을 참조한다고 했다.

- 문자열에 수정이 있어날 때마다 메모리 공간을 재설정해주지 않으면 s1에서 수정한게 s2에 영향을 미치기 때문에

  String에 수정사항이 발생할 때마다 매번 메모리를 재할당해준다. (문자열 불변 법칙)

- 문자열 불변 법칙의 특성 때문에 많은 문자열 데이터를 수정/삭제해야하는 경우, 단순 연산을 사용하면(ex. +) 시간복잡도/공간복잡도 면에서 효율성이 떨어질 수 있다.

- 그래서 사용하는 게, StringBuilder/StringBuffer다.

public class StringBufferTest {
    public static void main(String[] args) {
        StringBuffer sb = new StringBuffer();
        sb.append("hello");
        sb.append(System.lineSeparator());
        sb.append("this is StringBuffer!");

        System.out.println(sb);
    }
}

>> 결과

hello
this is StringBuffer!

 

2. StringBuilder와 StringBuffer의 차이점

  StringBuilder StringBuffer
공통점 char[] 배열로 이루어진 버퍼로, String 추가/수정/삭제 연산시 시간/공간 사용을 절약

* 디폴트로 16의 사이즈를 가지며 추가적으로 늘릴 수 있다.
차이점 Thread-safe Thread-safe x
public synchronized StringBuffer append(String str) {
    /*생략*/
}

public AbstractStringBuilder append(Object obj) {
    /*생략*/
}

 

|  Math

- 수학 계산에 사용되는 클래스. 

- 아래는 Math를 사용하여 소수점 n번째까지 반올림/올림/버림을 하는 법과,

- 두 좌표 간의 거리를 구하는 법을 작성한 코드이다.

public class MathTest {
    public static void main(String[] args) {
    
    	// 1. 반올림, 올림, 버림
        double d = 123.2364;
        int n = 3; // 소수점 자릿수
        
        // 소수점 n번째까지 반올림
        double d1 = Math.round(d * Math.pow(10, n)) / Math.pow(10, n);
        System.out.println(d1);

        // 소수점 n번째까지 올림
        n = 2;
        double d2 = Math.ceil(d * Math.pow(10, n)) / Math.pow(10, n);
        System.out.println(d2);

        // 소수점 n번째까지 버림
        double d3 = Math.floor(d * Math.pow(10, n)) / Math.pow(10, n);
        System.out.println(d3);
        
        // -----------------------------------------------------------
        
        // 2. 피타고라스의 법칙을 활용하여 두 좌표의 거리 구하기 
        int[][] pos = {{2, 1}, {5, 5}};

        int xDistance = pos[1][0] - pos[0][0];
        int yDistance = pos[1][1] - pos[0][1];
        double distance = Math.sqrt(Math.pow(xDistance, 2) + Math.pow(yDistance, 2));

        System.out.println(distance);
    }
}

 

[ 참고 및 출처 ]

부트캠프 강의를 들은 후 여러 자료를 참고하여 정리하였습니다.

- 인코딩 관련 참조

https://codechacha.com/ko/java-convert-bytes-to-string/

https://m.blog.naver.com/PostView.naver?isHttpsRedirect=true&blogId=pjok1122&logNo=221505713248 

https://corock.tistory.com/139

https://dogcowking.tistory.com/289

- 문자열 불변 법칙

https://starkying.tistory.com/entry/what-is-java-string-pool

https://readystory.tistory.com/139

|  들어가기 전에 

- 왜 우선순위 큐는 비선형 구조일까? 
: 큐는 선형구조로 1 : 1의 대응방식을 가진다. 앞에서 부터 데이터를 꺼내고, 뒤에서부터 데이터를 축척한다.
: 우선순위 큐는 이러한 큐에 우선순위 개념을 추가한 형태로, 우선순위를 따져 우선순위가 높은 데이터를 먼저 뽑는다.
: 우선순위 큐는 배열, 연결리스트, 힙으로 구현이 가능한데 이중에서 힙으로 구현을 하는 것이 가장 효율적이다.
: 따라서, 자바에서도 우선순위 큐는 힙으로 구현되었다. 힙은 완전 이진 트리 형태이며, 이는 1 : N 관계를 갖는 비선형구조이다.

자바에서는 우선순위 큐를 힙으로 구현한다. 왜?
: 위에서도 잠시 언급했던 것처럼, 힙으로 큐를 구현하는 것이 가장 효율적이기 때문에, 자바도 구현방식으로 힙을 쓴다.
  - 배열의 경우, 중간 삽입 또는 삭제 시 O(N)의 시간이 소요되고,
  - 연결리스트의 경우, 첫 위치에 삽입/삭제할 경우 O(1)이 걸리지만 그렇지 않다면 모든 구간을 돌아 O(N)이 소요된다.
  - 힙의 경우, 루트에 최소값 또는 최대값을 두며 하위 트리로 파생되기 때문에 삽입/삭제 모두 logN이 걸린다.
자료구조 삽입 삭제
순서 없는 배열 O(1) O(N)
순서 없는 연결리스트 O(1) O(N)
정렬된 배열 O(N) O(1)
정렬된 연결리스트 O(N) O(1)
O(logN) O(logN)
: 이처럼, 힙을 사용하면 삽입/삭제의 시간 복잡도를 줄일 수 있기에 자바도 우선순위 큐를 힙으로 구현한다.
: 참고로, 자바의 PriorityQueue 객체는 Queue와 달리 인터페이스가 아니라 일반 클래스이다.

 

|  힙이란?

- 사전적으로, "무언가를 차곡차곡 쌓아올린 자료구조"라는 뜻이다.

- 완전 이진 트리 형태의 자료구조

  ** 완전 이진 트리란? 마지막 레벨을 제외하고 모든 레벨의 노드가 꽉 채워진 상태인 트리

- 중복값을 허용하며, 반 정렬 상태(느슨한 정렬 상태)이다.

  ** 반 정렬 (또는 느슨한 정렬) 이란?
  : 큰 값이 상위에 있고, 작은 값이 하위에 있을 뿐, 이진 탐색 트리처럼 좌우가 정렬되어 있지 않다.
  ** 중복값을 허용한다는 것은?
  : 이진 탐색 트리와 달리, 힙은 중복값을 허용한다.

- 종류 : 최소힙, 최대힙

출처:https://gmlwjd9405.github.io/2018/05/10/data-structure-heap.html

 - 응용 분야 :

힙은 최대값과 최소값을 구해야할 때 가장 적절하다.
그렇기에 우선순위 큐를 구현하거나, 힙 정렬를 만드는 게 사용되며, 허프만 코드와 같은 알고리즘에 쓰인다.

 

|  힙의 구현

- 힙은 배열로 가장 잘 구현할 수 있다.

- 힙의 삽입은 아래와 같이 가장 끝에 데이터를 삽입하고 각 구간을 비교하여 교체하는 방식이며,

- 힙의 삭제는 최상단의 루트를 삭제하는 것으로, 가장 끝의 노드를 루트에 놓고 비교하여 교체하는 방식을 취한다.

삽입 (1) 트리의 가장 끝 위치에 데이터를 삽입
(2) 삽입한 위치의 부모노드와 비교하여 작으면 부모와 자리를 교체한다 (작지 않을 때까지 반복)
삭제 (1) 루트를 꺼내서 삭제한다.
(2) 트리의 가장 끝 위치의 노드를 루트에 삽입
(3) 루트부터 시작해서 자식과 비교하여 자식보다 크면 교체한다. (크지 않을 때까지 반복)

[ 코드 - 더보기 ]

더보기

1. 최소힙 구현하기

import java.util.ArrayList;

class MinHeap{
    ArrayList<Integer> tree;

    public MinHeap(){
        this.tree = new ArrayList<Integer>();
        this.tree.add(0); // dummy data
    }

    // 삽입 : (1) 트리의 가장 뒤에 삽입 (2) 삽입 위치에서부터 부모와 비교
    public void add(int data){
        this.tree.add(data);

        int curIdx = this.tree.size() - 1; // 현재 위치
        int parentIdx = curIdx / 2; // 부모 위치
        while (curIdx > 1 && tree.get(parentIdx) > tree.get(curIdx)) {
            int tmp = tree.get(parentIdx);
            tree.set(parentIdx, tree.get(curIdx));
            tree.set(curIdx, tmp);
            curIdx = curIdx / 2;
        }
    }

    // 삭제 : (1) 루트를 삭제 (2) 루트에 가장 끝 노드를 삽입 (3) 루트에서부터 비교
    public int remove(){

        if (this.tree.size() == 1){
            System.out.println("this heap is empty!");
            return -1;
        }

        // (1)-(2)
        int min = this.tree.get(1);
        int last = this.tree.remove(this.tree.size() - 1);
        this.tree.set(1, last);

        // (3)
        int curIdx = 1;
        while(true){
            int leftIdx = curIdx * 2;
            int rightIdx = curIdx * 2 + 1;
            int childIdx = -1;

            if (rightIdx < this.tree.size()) { // 자식노드가 둘 다 있는 경우
                childIdx = tree.get(leftIdx) < tree.get(rightIdx) ? leftIdx : rightIdx;
            } else if (leftIdx < this.tree.size()) { // 자식노드가 하나인 경우
                childIdx = leftIdx;
            } else { // 자식이 하나도 없을 때
                break;
            }
            
            if (tree.get(curIdx) < tree.get(childIdx)){
                break;
            } else {
                int tmp = tree.get(curIdx);
                tree.set(curIdx, tree.get(childIdx));
                tree.set(childIdx, tmp);
                curIdx = childIdx;
            }
        }

        return min;
    }
}

 

2. 최대힙 구현하기

import java.util.ArrayList;

class MaxHeap{
    ArrayList<Integer> tree;

    public MaxHeap(){
        this.tree = new ArrayList<Integer>();
        this.tree.add(0); // dummy data
    }

    // 삽입 : (1) 트리의 가장 뒤에 삽입 (2) 삽입 위치에서부터 부모와 비교
    public void add(int data){
        this.tree.add(data);

        int curIdx = this.tree.size() - 1; // 현재 위치
        int parentIdx = curIdx / 2; // 부모 위치
        while (curIdx > 1 && tree.get(parentIdx) < tree.get(curIdx)) {
            int tmp = tree.get(parentIdx);
            tree.set(parentIdx, tree.get(curIdx));
            tree.set(curIdx, tmp);
            curIdx = curIdx / 2;
        }
    }

    // 삭제 : (1) 루트를 삭제 (2) 루트에 가장 끝 노드를 삽입 (3) 루트에서부터 비교
    public int remove(){

        if (this.tree.size() == 1){
            System.out.println("this heap is empty!");
            return -1;
        }

        // (1)-(2)
        int max = this.tree.get(1);
        int last = this.tree.remove(this.tree.size() - 1);
        this.tree.set(1, last);

        // (3)
        int curIdx = 1;
        while(true){
            int leftIdx = curIdx * 2;
            int rightIdx = curIdx * 2 + 1;
            int childIdx = -1;

            if (rightIdx < this.tree.size()) { // 자식노드가 둘 다 있는 경우
                childIdx = tree.get(leftIdx) > tree.get(rightIdx) ? leftIdx : rightIdx;
            } else if (leftIdx < this.tree.size()) { // 자식노드가 하나인 경우
                childIdx = leftIdx;
            } else { // 자식이 하나도 없을 때
                break;
            }

            if (tree.get(curIdx) > tree.get(childIdx)){
                break;
            } else {
                int tmp = tree.get(curIdx);
                tree.set(curIdx, tree.get(childIdx));
                tree.set(childIdx, tmp);
                curIdx = childIdx;
            }
        }

        return max;
    }
}

 

우선순위 큐란?

- 우선순위가 있는 큐이다.

- 모든 데이터가 우선순위가 있으며, Dequeue를 할 때에 우선순위가 높은 순으로 출력된다.

  * 만약, 우선순위가 같은 경우에는 FIFO 순으로 출력된다.

■ 우선순위 큐를 어떻게 쓸 수 있을까?

- 우선순위 큐는 데이터를 삽입할때, 정해진 기준대로 오름차순/내림차순으로 데이터를 넣을 수 있고

- 최소값 또는 최대값부터 데이터를 뽑을 수 있다.

- 삽입/삭제 시간복잡도는 그럼 얼마나 될까? 앞에서 힙에서 언급했듯 logN이 걸린다.

- 이 점을 이용해서 K번째로 가장 큰 수를 뽑는다거나, 강도가 다른 돌을 부딪혀 남는 돌을 다시 넣거나, 

  중복된 수가 있는 수열에서 빈도수 기준으로 정렬하되 같은 빈도면 더 작은 수가 앞에 오게 정렬하는 등

  순서에 따라 넣고 빼는 일이 잦거나 재정렬이 필요한 데이터들에서 사용할 수 있다.

 

Comparable 인터페이스

- 우선순위 큐를 사용할 때, 우선순위의 기준이 되는 객체에 Comparable인터페이스를 사용하면

  오름차순 또는 내림차순으로 데이터를 enqueue/dequeue할 수 있다.
- Comparable인터페이스를 사용하는 방법은 크게 두가지이다.


[1] 객체에 Comparable를 구현하는 방법

class Student implements Comparable<Student>{
    String name;
    int score;

    public Student(String name, int score) {
        this.name = name;
        this.score = score;
    }

    @Override
    public int compareTo(Student o) {
        // this.Student(new)와 o(old) Student를 비교
        // 1 : 변경하지 않음, -1 : 변경
        return this.score >= o.score ? 1 : -1;
        // return this.score - o.score; -- 같은 말이다.
    }
}

[2] 람다식을 사용하여 비교 연산 메소드(compareTo)를 바로 구현하는 방법

import java.util.PriorityQueue;

class Student{
    String name;
    int score;

    public Student(String name, int score) {
        this.name = name;
        this.score = score;
    }
}

class test{
    public static void main(String[] args) {
        PriorityQueue<Student> pq = new PriorityQueue<>(
            (Student newSt, Student oldSt) ->  newSt.score - oldSt.score
        );

        for (int i = 0; i < 10; i++) {
            pq.add(new Student("Student"+(i+1), i * 10));
        }

        while(!pq.isEmpty()) {
            Student st = pq.poll();
            System.out.println(st.name + " : " + st.score);
        }
    }
}

[+] 문자열을 Comparable를 통해 사전순으로 offer/poll하는 방법

/* 생략 (위의 Student 객체 그대로 사용) */

class test{
    public static void main(String[] args) {
        PriorityQueue<Student> pq = new PriorityQueue<>(
            (Student newSt, Student oldSt) ->  newSt.name.compareTo(oldSt.name)
        );

        for (int i = 0; i < 10; i++) {
            char c = (char)('A' + (int)(Math.random()*32));
            pq.add(new Student((char)(c+i)+"_Student", i * 10));
        }

        /* 출력 생략 */
    }
}

 

|  힙과 우선순위 큐 문제

https://www.acmicpc.net/workbook/view/1819

 

문제집: 힙 쓰는 문제 (kcaladram)

 

www.acmicpc.net

https://www.acmicpc.net/step/13

 

우선순위 큐 단계

우선순위 큐를 응용하여 중앙값을 빠르게 찾는 문제

www.acmicpc.net

 

[ 참고 및 출처 ]

- 부트 캠프의 강의를 듣고 여러 자료들을 참조하여 정리한 내용입니다.

- https://namu.wiki/w/%ED%9E%99%20%ED%8A%B8%EB%A6%AC

- https://gmlwjd9405.github.io/2018/05/10/data-structure-heap.html

 

|  요약

  핵심 단어 핵심 특성 언제 쓰면 좋을까
트리 계층, Acyclic, 4가지의 순회 포화 이진 트리의 경우, 하위로 갈수록
노드의 갯수가 2^0, 2^1, 2^2..로 증가
계층 관계의 데이터를 다룰 때
그래프 네트워크, cyclic, DFS/BFS 루트가 없고, 각 정점들간 단방향 또는
양방향의 관계를 가질 수 있음
연관 관계를 가진 데이터들에서
최적의 결과를 찾아야할 때

 

|  트리와 그래프의 차이

  트리 그래프
개요 그래프의 한 종류 정점(vertex, 노드)과 간선(edge)으로 이루어진 자료구조
그림

출처 : 위키백과

출처 : 위키백과
방향 (단)방향 그래프  무(양)방향, (단)방향 그래프
사이클 Acyclic Cyclic
모델 계층 모델 네트워크 모델
루트 최상위 노드 없음
상-하위 관계 인접한 상하위 노드 없음
간선의 갯수 N개의 노드의 간선은 N-1개 그래프에 따라 다름
순회 PreOrder, InOrder, PostOrder
LevelOrder
DFS
BFS
경로 두 노드 간 경로는 하나(단방향) 두 노드 간 경로 여러 개 가능(양방향)
종류 이진 트리, 이진 탐색 트리,
균형 트리(AVL 트리, red-black트리),
이진 힙(최대힙, 최소힙) 등
-
예시 폴더 구조(디렉토리, 서브 디렉토리),
조직도, 가계도 등
지도, 지하철 노선도의 최단 경로, 
전기 회로의 소자들,
도로 (교차점과 일방 통행길), 선수 과목

 

■ 트리 개요

- 트리의 구조와 특징
- 이진 트리
- 이진 탐색 트리
- 균형 이진 탐색 트리

|  트리의 구조와 특징

출처:https://velog.io/@alstn3265/%EC%9E%90%EB%A3%8C%EA%B5%AC%EC%A1%B0-%ED%8A%B8%EB%A6%AC

[특징]
1. 모든 노드는 연결되어 있다.
2. 노드 간 이동 경로는 하나이다.(단방향)
3. 하나의 간선을 끊으면 2개의 sub-Tree로 나뉜다.
4. 이진트리에서, 노드가 N개인 트리의 간선 갯수는 N-1개이다.
5. 그래프와 달리 cycle이 존재하지 않는다(acyclic)
Basic 노드(Node, 또는 정점) data를 담고 있는 트리구조의 단위
간선(Edge) 노드의 연결선 (= link, branch)
구분 루트(Root) 꼭대기 노드
내부 노드(Internal) 단말을 제외한 모든 노드
단말(Leaf) 자식이 없는 노드
관계 부모(Parent) 연결된 두 노드 중 상위 노드
자식(Child) 연결된 두 노드 중 하위 노드
형제(Sibling) 같은 부모를 공유하는 노드
깊이 깊이(Depth) 루트에서 어떤 노드까지의 간선의 수 (ex. P에서 E까지 깊이 : 4)
레벨(Level) 트리의 특정 깊이를 가지는 노드 집합 (ex. level 1의 노드는 Q와 R)
높이(Height) 트리에서 가장 큰 레벨 값 (ex. 위에 트리는 4)
노드 갯수 크기(Size) 자신을 포함한 자식 노드의 갯수
간선 갯수 차수(Degree) 각 노드가 가진 간선 갯수 (ex. L의 차수는 3)
트리의 차수 트리의 최대 차수 (ex. 위 트리의 최대 차수는 L의 차수인 3)

 

|  이진트리(BT, Binary Tree)

이진트리 : 최대 2개의 자식을 가질 수 있는 트리

1. 이진 트리의 종류와 특징

포화 이진 트리(Perfect Binary Tree) 모든 레벨에서 노드가 꽉 채워진 트리
완전 이진 트리(Complete Binary Tree) 마지막 레벨을 제외하고
모든 레벨에서 노드가 꽉 채워진 트리

정 이진 트리(Full Binary Tree) 모든 노드가 0 또는 2개의 자식을 가진 트리
(자식이 없거나 2개 모두 있거나)
편향 트리(Skewed Binary Tree, 사향 트리) 한쪽으로 기울어진 트리
균형 이진 트리(Balanced Binary Tree) 모든 노드의 좌우 서브 트리 높이가
1 이상 차이나지 않는 트리

(ex. 옆 그림에서 A의 좌측은 2개, 우측은 1개의 간선이 있어 차이가 1이 난다)

- 관련 수식

N = 2^(h+1) - 1

* N : 포화트리 노드의 갯수
* h : 높이
h = logN
* N : 노드의 갯수
* h : 포화 또는 완전 트리 높이
maxH = N
*maxH : 이진 트리의 최대 가능 높이
*N : 노드의 갯수

 

2. 이진 트리의 순회(Traversal)

전위 순회(PreOrder) 현재 -> 왼쪽 -> 오른쪽 
중위 순회(IntOrder) 왼쪽 -> 현재 -> 오른쪽 
후위 순회(PostOrder) 왼쪽 -> 오른쪽 -> 현재
레벨 순회(LevelOrder) 각 레벨에서 왼쪽 -> 오른쪽
[ 이진 트리를 구현할 때의 Tip ]
- 배열을 통해 이진 트리를 구현한다고 가정할 때 위치를 찾는 방법
(1) 좌측 노드 = 나의 노드 x 2 + 0 --> 배열idx에서는 나의 노드 x 2 + 1
(2) 우측 노드 = 나의 노드 x 2 + 1 --> 배열idx에서는 나의 노드 x 2 + 2
(3) 부모 노드 = 나의 노드 / 2
- 연결리스트를 통해 구성할 수도 있다.

- 이진 트리 순회 구현  ▼ 더보기

더보기

1. 배열을 통한 이진트리 순회

// 배열을 통한 이진트리 구현
class BinaryTree{
    char[] arr;
    
    BinaryTree(char[] data){
    	this.arr = data;
    }
    
    public void preOrer(int idx){      
        System.out.print(arr[idx] + " ");
        
        int left = 2 * idx + 1;
        int right = 2 * idx + 2;
        
        if (left < this.arr.length){
        	preOrder(left);
        }
        
        if (right < this.arr.length){
            preOrder(right);
        }
    }
    
    public void inOrder(int idx){
    	int left = 2 * idx + 1;
        int right = 2 * idx + 2;
        
        if (left < arr.length){
           inOrder(left);
        }
        
        System.out.print(arr[idx] + " ");
        
        if (right < arr.lenght){
           inOrder(right);
        }
    }
    
    public void postOrder(int idx){
    	int left = idx * 2 + 1;
        int right = idx * 2 + 2;
        
        if (left < arr.length) {
           postOrder(left);
        }
        
        if (right < arr.length) {
           postOrder(right);
        }
        
        System.out.print(arr[idx] + " ");
    }
    
    public void levelOrder(int idx){
    	for (int i = idx; i < arr.length; i++){
        	System.out.print(arr[i] + " ");
        }
        System.out.println("");
    }
}

public class Main{
   public static void main(String[] args){
      BinaryTree bt = new BinaryTree(new char[]{'A','B','C','D','E','F','G'});
      //     A
      //   B   C
      //  D E F G
   }
}

 

2. 연결리스트를 통한 이진트리 순회

// 노드의 연결(연결리스트)을 통한 이진트리 구현
class Node{
    char data;
    Node left;
    Node right;
    
    Node(char data){
    	this.data = data;
    }
}

class BinaryTree{
    Node root;
    
    BinaryTree(char[] datas){
    	Node nodes[] = new Node[datas.length];
    
        // 각각의 노드를 생성
    	for (int i = 0; i < datas.length; i++){
        	nodes[i] = new Node(datas[i]);
        }
        
        // 트리 형태로 노드를 연결
        root = nodes[0];
        for (int i = 0; i < nodes.length; i++){
        	int left = i * 2 + 1;
            int right = i * 2 + 2;
            
            nodes[i].left = nodes[left];
            nodes[i].right = nodes[right];
        }
    }
    
    public void preOrder(Node node){
    	if (node == null) {
        	return;
        }
        
        preOrder(node.left);
        System.out.print(node.data + " ");
        preOrder(node.right);
    }
    
    public void inOrder(Node node){
    	if (node == null) {
        	return;
        }
    
    	inOrder(node.left);
        System.out.print(node.data + " ");
        inOrder(node.right);
    }
    
    public void postOrder(Node node){
    	if (node == null){
        	return;
        }
        
        postOrder(node.left);
        postOrder(node.right);
        System.out.print(node.data + " ");
    }
    
    public void levelOrder(Node node){
    	Queue<Node> queue = new LinkedList();
        queue.add(node);
        
        while (!queue.isEmpty()){
			Node cur = queue.poll();
            System.out.print(cur.data + " ");
            
            if (cur.left != null) {
            	queue.offer(cur.left);
            }
            
            if (cur.right != null) {
            	queue.offer(cur.right);
            }
        }
    }
}

public class Main{
   public static void main(String[] args){
      BinaryTree bt = new BinaryTree(new char[]{'A','B','C','D','E','F','G'});
      //     A
      //   B   C
      //  D E F G
   }
}

 

3. 가중치가 있는 포화이진트리의 루트~리프까지의 가중치 총합을 동일하게 만드는 최소합

BinaryTree{
    int h; //높이
    int sum; //가중치의합
    int[] w; //가중치 배열
    
    BinaryTree(int h, int[] w){
    	this.h = h;
        this.w = new int[(int)(Math.pow(2, (h+1)))];
        
        for (int i = 2; i < this.w.length; i++){
        	this.w[i] = w[i-2];
        }
    }
    
    // 하위 좌우 중 더 큰 가중치에 맞춘 후 -> 상위 좌우 중 더 큰 가중치 합에 맞춘다.
    public int dfs(int h, int idx){
    	if (h == this.h){
        	res += this.w[idx]; //리프 가중치 추가
        	return this.w[idx];
        }
        
        int leftW = dfs(h + 1, idx * 2 + 1); // 좌측 서브 트리의 가중치 합
        int rightW = dfs(h + 1, idx * 2 + 2); // 우측 서브 트리의 가중치 합
        res += this.w[idx] + Math.abs(leftW - rightW); 
        return this.w[idx] + Math.max(leftW, rightW); // 서브 트리에서 더 큰 가중치 합 반환
    }
}

public class Main{

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

	public static void main(String[] args){
    	int h = 2;
        int[] w = new int[]{1, 1, 2, 1, 3, 1};
        solution(h, w);
    }
}

 

|  이진 탐색 트리(BST, Binary Search Tree)

이진 탐색 트리 : 루트부터 시작하여 좌측에는 더 작은 키가, 우측에는 더 큰 키가 들어간 형태

* 모든 서브 트리 또한 이진 탐색 트리이며,
* 중복된 키는 허용하지 않는다.

1. 이진 탐색 트리의 탐색 시간 복잡도

탐색 균형 상태 O(logN)
불균형 상태 O(N)   

2. 이진 탐색 트리의 구현 - 탐색, 삽입과 삭제 (▼ 더보기)

더보기
탐색* root에서부터 탐색.
찾으려는 key값이 현재 노드보다 작은 경우 좌측으로 이동, 큰 경우 우측으로 이동.

찾으려는 key값과 현재 노드의 키값이 같은 경우 탐색 완료
O(logN)
삽입 root에서부터 key값을 비교.
삽입하는 key값이 현재 노드보다 작은 경우 좌측으로 이동, 큰 경우 우측으로 이동.
이동한 현재 위치에 노드가 없는 경우(null), 그 위치에 노드를 추가
O(logN)
삭제 case1 삭제 노드가 리프인 경우 : 부모노드에서 리프방향을 null 처리 O(logN)
case2 삭제 노드의 자식이 하나 있는 경우 : 부모노드에서 내 방향(ex.left)에 자식을 연결
case2 삭제 노드의 자식이 둘인 경우 : 
(1) 부모노드에서 내 방향에 내 좌측자식에서 가장 큰 노드를 연결
(2) 부모노드에서 내 방향에 내 우측자식에서 가장 작은 노드를 연결

 * 여기서의 탐색은 루트에서부터의 탐색으로, 이진 탐색 트리를 실제로 조회할 때는 4가지 순회 중 하나를 선택하는 것이 더 유리하다.(ex. preOrder/InOrder/....)

class Node{
    int key;
    Node left;
    Node right;
    
    Node(int key, Node left, Node right){
    	this.key = key;
        this.left = left;
        this.right = right;
    }
}

class BinarySearchTree{

    Node root;
	
    BinatySearchTree(int key){
    	this.root = new Node(key, null, null);
    }
    
    // 삽입
    public void add(int key){
    	if (root == null){
        	root = new Node(key, null, null);
        } else {
        	Node cur = this.root;
            Node pre = cur;
            
            while (cur != null) {
            	pre = cur;
            
            	if (key < cur.key){
                	cur = cur.left;
                    
                    if (cur == null){
                    	pre.left = new Node(key, null, null);
                        break;
                    }
                }
                
                if (key > cur.key){
                	cur = cur.right;
                    
                    if (cur == null) {
                    	pre.right = new Node(key, null, null);
                        break;
                    }
                }
            }
        }
    }
    
    // 삭제
    public void remove(int key){
    	// 탐색 (해당 key값을 가진 노드 탐색)
        Node cur = this.root;
        Node parent = null;
        
        while (cur != null){
        	if (cur.key == key){
            	break;
            }
            
            parent = cur;
            if (key < cur.key) {
            	cur = cur.left;
            } else {
            	cur = cur.right;
            }
        }
        
        if (cur == null){ //없을 때
        	System.out.println("BinarySearchTree doesn't have key " + key);
        }
        
        // 세가지 case에 따라 삭제 진행
        Node child = null;
        Node leftRightMax = null;
        Node ex;
        
        // case 1 : 삭제 노드가 leaf 일 때
        if (cur.left == null && cur.right == null){
        	if (cur == this.root){
            	this.root = null;
            } else {
            	if (parent.left == cur){
                	parent.left = null;
                } else {
                	parent.right = null;
                }
            }
        } 
        // case 2 : 삭제 노드에 자식이 하나 있을 때
        else if (cur.left != null && cur.right == null || cur.left == null && cur.right != null) {
        	// 자식 위치를 먼저 확인
            if (cur.left != null) {
            	child = cur.left;
            } else {
            	child = cur.right;
            }
            
            // 자식 노드를 현재 위치로 올린다.
            if (parent == null){ // = cur 가 root
            	this.root = child;
            } else {
            	if (parent.left == cur){
                    parent.left = child;
                } else {
                    parent.right = child;
                }
            }
        }
        // case 3 : 삭제 노드에 자식이 둘 있을 때
        else {
        	// 나의 좌측 노드 중 가장 큰 노드를 찾는다.
            ex = cur;
            leftRightMax = cur.left;
            
            while (now.right != null){
            	ex = leftRightMax;
            	leftRightMax = leftRightMax.right;
            }
            
            // 나의 위치에 찾은 노드를 넣어준다.
            ex.right = leftRightMax.left; // 먼저, 찾은 노드에 좌측노드가 있는 경우
            leftRightMax.left = cur.left;
            leftRightMax.right = cur.right;
            
            if (parent == null){
            	this.root = leftRightMax;
            } else {
            	if (parent.left == cur) {
                	parent.left = leftRightMax;
                } else {
                	parent.right = leftRightMax;
                }
            }
        }
    }
    
}

 

|   균형 이진 트리 (추가 정리 예정)

(1) 이진 탐색 트리의 편향 발생

(2) 균형 이진 탐색 트리 종류

  AVL 트리 red-black 트리
공통점    
차이점    

 

■ 그래프 개요

- 그래프의 구조와 특징
- 그래프의 탐색
- 그래프의 구현

|  그래프의 구조와 종류

출처:https://medium.com/@sarinyaswift/intro-to-the-graph-data-structure-a8277c6a2ad9

Basic 정점(Vertex, 노드) 데이터를 담고 있는 노드
간선(Edge) 연결선 = link, brank
관계 인접 정점(Adjacent vertex) 간선 하나로 바로 연결된 정점
간선 갯수

정점의 차수(Degree) 무방향 그래프에서 하나의 정점에 인접한 정점의 수 (ex. a노드의 차수는 2)
* 무방향 그래프의 모든 정점 차수의 합 = 전체 간선의 수 x 2
진입 차수(In-degree) 들어오는 간선의 수 
진출 차수(Out-degree) 나가는 간선의 수
경로 경로 길이(Path length) 어떤 경로를 구성하는 데 사용된 간선의 수
단순 경로(Simple path) 어떤 경로를 가기 위해 특정 정점을 반복해서 가지 않아도 되는 경우
사이클 사이클(Cycle) 단순 경로의 시작과 끝 정점이 동일한 경우

 

출처:https://medium.com/@sarinyaswift/intro-to-the-graph-data-structure-a8277c6a2ad9
출처:위키백과(완전그래프)

종류 내용 정점 A와 B의 간선 표현 / 비고
무방향 그래프(Undirected) 간선에 방향이 없는 그래프 (A, B) = (B, A)
방향 그래프(Directed) 간선에 방향이 있는 그래프 <A, B> != <B, A>
가중치 그래프(Weighted) 간선에 값(이동 비용)이 있는 그래프 -
완전 그래프(Complete) 모든 정점이 서로 연결된 그래프 정점이 N개일 때, 간선의 수 n(n-1)/2

 

|  그래프의 탐색

DFS(깊이 우선 탐색) = depth first search, Stack 또는 재귀를 사용하여 깊이 탐색
BFS(너비 우선 탐색) = breath first search, Queue를 사용하여 같은 레벨의 노드를 탐색

 

■ 그래프 탐색의 핵심

(1) 정점(Vertex)
(2) 간선(Edge, 곧 연결상태) : 어느 방향으로, 몇 개의 데이터가 연결되어 있는가
(3) 요구사항이 무엇인가? 
ex.
- DFS
  : 만약 root~leaf까지의 가중치를 구해야하거나, 
  : 바둑판과 같은 2차원 배열 or x축과 y축 좌표의 특정 위치에서 사방위로 움직일때
  : 네트워크 방식으로 서로 복잡하게 연결된 정점들을 새로운 자료구조로 재구성할 경우
- BFS
  : 어떤 정점 X에서 파생되는 여러개의 정점들이 있고, 각 하위 정점이 같은 규칙으로 파생될때
    특정 타켓까지 구하기 위해서 얼만큼의 시도가 필요한가 (== level를 묻는 문제)

 

■ 인접 행렬 VS 인접리스트

- 간선 정보를 저장하는 자료구조로 인접 행렬과 인접 리스트를 주로 같이 설명하는데,

- 실제로 코딩테스트 문제를 풀다보면 HashSet를 쓰거나 HashMap을 통해서 간선 정보를 저장하기도 한다.

- DFS나 BFS를 써서 문제를 풀 때는 상황에 따라 가장 적절한 자료구조를 사용하는 것이 필요.

  인접 행렬 인접 리스트
내용 2차원 배열로 인접 간선을 저장 연결리스트(LinkedList[])로 인접 간선을 저장
장단점 간선 정보 확인/업데이트가 빠름
메모리 사용량이 높다(간선)
정점의 추가/삭제가 빠르다.
메모리 사용량이 적다.(간선)
간선 정보 확인이 오래걸린다.
언제 유리한가 정점의 갯수가 적고, 간선 갯수가 많을 때 정점의 갯수가 많고, 간선 갯수는 적을 때
시간복잡도





특정 간선 탐색 O(1) O(degree(V)) *V: 차수 (특정 정점의 간선들)
정점 차수 계산 O(N) O(degree(V)) 
전체 정점 탐색 O(N^2) *N : 정점 갯수 O(E) *E : 간선 갯수
공간복잡도 N x N N + E

 

|  그래프 문제

https://school.programmers.co.kr/learn/courses/30/parts/14393

 

프로그래머스

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

programmers.co.kr

https://school.programmers.co.kr/learn/courses/30/parts/12421

 

프로그래머스

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

programmers.co.kr

 

[ 참조 및 출처 ]

부트 캠프 수업을 들은 후 여러 참고 자료를 통해 내용을 정리하였습니다.

https://medium.com/@sarinyaswift/intro-to-the-graph-data-structure-a8277c6a2ad9

https://velog.io/@alstn3265/%EC%9E%90%EB%A3%8C%EA%B5%AC%EC%A1%B0-%ED%8A%B8%EB%A6%AC

https://velog.io/@jeewoo1025/%ED%8A%B8%EB%A6%AC-Tree

|  파일 시스템 

운영체제가 저장매체에 파일을 쓰기 위한 자료구조 및 알고리즘

1) 파일은 어떻게 만들어진 걸까?

- 0과 1만으로 이루어진 비트 데이터를 그대로 저장하면 관리하기 어렵다.

- 따라서 블록으로 (ex. 4kb) 데이터를 나눠 저장한다. (블록에 고유 번호 부여)

- 블록의 고유 번호도 구분하기 어려우니, 추상적인 객체가 필요하다. (객체 : 파일)

- 그렇게 파일이 만들어졌다.

 

2) 저장 방법

- 가능하면 연속적인 공간에 저장하는 것이 좋지만, 외부 단편화*나 파일 사이즈 변경 문제로 어렵다.

- 그렇기에 자료구조로 연결리스트를 사용한다.

* 외부 단편화 : 총 메모리 공간이 남았으나, 남아있는 공간이 연속적이지 않아서 발생하는 현상

외부단편화,출처:https://code-lab1.tistory.com/54

3) 다양한 파일 시스템

- 인도우는 FAT이라는 자료구조에 블록 위치를 기록

- 리눅스는 inode 방식 - 일종의 인덱스 블록 기법 - 을 사용

* 가장 기본 파일 시스템 방식은 inode이다.

 

|  inode 파일 시스템 구조

수퍼 블록 파일 시스템 정보 (파일 시스템 전반의 메타 데이터)
아이노드 블록 파일 상세 정보 (파일 각각의 메타 데이터)
데이터 블록 실제 데이터

- 파일은 inode 고유값과 자료구조로 주요 정보를 관리한다.

- '파일이름:inode'로 이름과 inode번호를 매칭

- inode를 기반으로 파일에 접근한다. (inode 기반 메타데이터* 저장)

super block
↓ inode ↓ inode
fileA fileB
disk
block
disk
block
disk
block
disk
block
disk
block
disk
block

* inode 메타데이터 : 파일 권한, 소유자 정보, 파일 사이즈....등등등

출처:https://www.science.unitn.it/~fiorella/guidelinux/tlk/node96.html

더보기

ext2_inode 구조

  • i_blocks: 파일이 몇 개의 데이터 블록을 가지고 있는지 나타냄.
  • i_mode: 이 inode가 관리하는 파일의 속성 및 접근 제어 정보 유지
  • i_links_count: 이 inode를 가리키고 있는 파일 수(또는 링크 수)를 의미. (파일 이름 -- inode)
  • i_uid, i_gid: 파일을 생성한 소유자의 userID, groupID 의미.
  • i_atime, i_ctime, imtime: 파일의 접근 시간, 생성 시간, 수정 시간 의미.
  • i_block[15]: 파일에 속한 디스크 블록들의 위치를 관리.

[ 출처 ] https://velog.io/@jinh2352/inode-%EA%B5%AC%EC%A1%B0

 

|  가상 파일 시스템 (VFS)

가상적(virtual) 층으로 다양한 파일시스템에 일관된 방식으로 접근할 수 있게 하는 것

- 원래 EXT2, EXT4, XFS 파일 시스템들에 대해 각각에 맞는 고유 함수를 호출해야하나,

- VFS를 사용하면 open(), read(), write() 와 같은 동일 함수를 사용해 호출할 수 있다.

- 어느 파일 시스템으로 저장되었는지 상관 없이 일관된 인터페이스를 사용해서 파일에 접근 가능

 

|  가상 머신

하나의 하드웨어에 다수의 운영체제를 설치하고 개별 컴퓨터처럼 동작하게 한 것

- 하이퍼바이저, KVM(AWS), VMWare 등이 있다.

 

|  부팅 

- 컴퓨터를 켜서 동작시키는 절차

 

/ 확인하기

1. inode 파일 시스템 방식이 뭔가?

2. 가상 머신에 대해 설명하라

3. 외부 단편화에 대해 설명하라

 

[ 출처 ]

부트 캠프 강의를 듣고 책, 블로그 내용 등을 참조하여 정리하였습니다.

 

[ 참고 ] 

https://blog.naver.com/PostView.naver?blogId=n_cloudplatform&logNo=222481521174&parentCategoryNo=&categoryNo=11&viewDate=&isShowPopularPosts=false&from=postView 

https://hyoje420.tistory.com/53

https://code-lab1.tistory.com/54

|  쓰레드란?

- Light Weight Process

- 어떠한 프로그램 내에서, 특히 프로세스 내에서 실행되는 흐름의 단위

출처:위키백과

 

|  프로세스와 쓰레드의 차이점

  프로세스 스레드
차이점 프로세스 간에 독립적이다 프로세스의 서브셋
각각의 프로세스가 독립적인 자원 가짐 프로세스 자원 공유
프로세스 자신만의 주소영역을 가짐 스레드는 주소 영역을 공유한다
IPC 기법으로 통신 가능 별도의 통신 기법이 필요 없다

 

|  스레드 장단점 정리

  내용 추가 설명
장점 성능 향상 멀티 스레드로 병렬 처리
응답성 향상 다수의 사용자의 요청이 오거나,
여러개의 처리와 동시에 요청이 이루어질 때 응답성이 향상
자원 공유 효율 프로세스 내부에서 스레드 간 자원 공유(IPC X)
단점 한 스레드가 프로세스 전체에 영향을 준다 멀티 프로세스의 경우, 하나의 프로세스가 문제가 있어도
다른 프로세스에 영향 주지X
동기화 이슈 동기화 코드를 적절히 추가해야한다.

 

|  스레드 동기화 이슈 이해

동기화(Synchronization) : 작업들 사이에 실행 시기를 맞추는 것

- 여러 개의 스레드가 하나의 자원에 동시에 접근, 수정할 때 동기화 이슈가 발생한다. 

 

|  동기화 이슈 해결 코드 작성 방법

임계 구역에 대한 접근을 막기 위해 Locking 매커니즘이 필요하다

  Mutual exclusion (상호 배제) = Mutex Semaphore (세마포어)
공통점 임계 구역에 대해 Locking(자바는 synchronized)을 걸어 동기화 이슈 해결
차이점 임계 구역에 하나의 스레드만 들어갈 수 있게 한다. 임계 구역에 여러 스레드가 들어갈 수 있게 한다
(Counter를 두어 동시 접근 가능 수 제어)

[ 자바 코드 예시 ]

class Shared { // 공유 자원
 
    // 임계 자원(공유 데이터)
    private static int sharedData = 0; 
 
    // 임계 영역(임계 자원을 사용하는 부분에 동기화 코드 추가)
    public synchronized void count(int n){
        sharedData = sharedDate + n;
    }
}

class Visitor extends Thread { // 공유 자원 방문자
	
    private int n;
    
    Visitor(int n) {
    	this.n = n;
    }
    
    @Override
    public void run(){
        for (int i = 0; i < 100; i++){
            MainThread.shared.count(n);
        }
    }
}

public class MainThread{

    public static Shared sharedData = new Shared();

    public static void main(String[] args) throws Exception{
        
        for (int i = 0; i < 100; i++){
            Visitor visitor = new Visitor(i);
            visitor.start(); // 누가 언제 sharedData를 갱신하는지 모른다.
        }
    }
}

 

|  데드락(교착 상태)과 스타베이션(기아 상태) 개념 이해

  데드락(교착상태) 스타베이션(기아 상태)
의미 무한 대기 상태 우선순위에 따른 자원 미할당 상태
언제? 여러 프로세스가 동일 자원 점유 요청할 때 여러 프로세스가 부족한 자원을 점유하려 경쟁할 때
어디서? 프로세스, 스레드 모두에서 발생 가능 프로세스에서만 발생
어떻게? 두 개 이상의 작업이
상대방의 작업이 끝나는 걸 기다림
특정 프로세스의 우선순위가 낮아
영원히 자원을 할당받지 못하게 됨
비고

내 폰의 어떤 앱이 갑자기 멈춰버렸다.
-> 운영체제가 보통 어느정도 시간이 지나면
    강제 종료시켜 버린다.
기아 상태는 우선순위를 변경하면 해결된다.
- 프로세스 우선순위 수시 변경
- 오래 기다린 프로세스 순위 변경
- 우선순위가 아닌 FIFO 기반 요청큐 사용

출처 - 위키 백과

 

|  확인하기

1. 프로세스 간에 어떻게 통신하는지, 쓰레드와 비교하여 설명하라

2. 프로세스와 쓰레드의 차이점

3. 언제 멀티 프로세스를 사용하고, 언제 쓰레드를 써야하나

4. 쓰레드 동기화란 무엇이며 왜 사용해야 하나

5. 뮤텍스와 세마포어의 차이점에 대해 설명하라

 

[ 출처 ]

부트 캠프 강의를 듣고 책, 블로그 내용 등을 참조하여 정리하였습니다.

 

[참고]

위키백과

https://jhnyang.tistory.com/3

https://donghoson.tistory.com/8?category=799810 

https://brunch.co.kr/@kd4/3

 

+ Recent posts