JOIN

JOIN은 두 개의 테이블을 서로 묶어서 하나의 결과를 만들어 내는 것을 말한다.

  • INNER JOIN(내부 조인)
    • 교집합에 해당하는 개념으로 두 테이블을 연결할 때 가장 많이 사용한다. 그래서 흔히 조인이라고 부르면 내부 조인을 의미하는 경우가 많다.
    • 두 테이블을 조인할 때 두 테이블에 모두 지정한 열의 데이터가 있어야 한다.
    • SELECT <열 목록>
      FROM <첫 번째 테이블>
          INNER JOIN <두 번째 테이블>
          ON <조인 조건>
      [WHERE 검색 조건]
      
      #INNER JOIN을 JOIN이라고만 써도 INNER JOIN으로 인식합니다.
  • OUTER JOIN(외부 조인)
    • 내부 조인은 두 테이블 모두 데이터가 있어야 결과가 나오지만 외부 조인은 두 테이블을 조인할 때 1개의 테이블에만 데이터가 있어도 결과가 나온다.
    • SELECT <열 목록>
      FROM <첫 번째 테이블(LEFT 테이블)>
          <LEFT | RIGHT | FULL> OUTER JOIN <두 번째 테이블(RIGHT 테이블)>
           ON <조인 조건>
      [WHERE 검색 조건]
    • LEFT OUTER JOIN
      • 다양한 JOIN 중에서 일반적으로 가장 많이 사용된다.
      • 좌측 테이블 데이터에 추가로 우측 정보를 조인하는 문법이다. 모든 좌측 테이블을 가져오고 조인 가능한 것은 붙이고, 조인 불가능한 것은 NULL로 채운다.
    • RIGHT OUTER JOIN
      • 오른쪽 테이블의 모든 값이 출력되는 조인
      • LEFT OUTER JOIN과 같은 개념이다.
    • FULL OUTER JOIN
      • 왼쪽 외부 조인과 오른쪽 외부 조인이 합쳐진 모든 데이터 조회를 위한 합집합 JOIN
      • 모든 테이블을 확인해 데이터가 있는 것은 모두 결과로 만들고 데이터가 없는 것은 모두 NULL이 된다.
  • INNER JOIN vs LEFT JOIN

INNER 조인은 양측 테이블 모두 존재하는 것만 결과로 만든다.
반면 LEFT 조인은 좌측 테이블 중 조인 불가능한 것들도 NULL로 채워 결과로 만들어진다.

  • CROSS JOIN(상호 조인)
    • 카티션 곱(CARTESIAN PRODUCT) 라고도 한다.
    • 한쪽 테이블의 모든 행과 다른 쪽 테이블의 모든 행을 조인하는 기능이다.
    • 조인의 결과로 전체 행 개수는 두 테이블의 각 행의 개수를 곱한 수 만큼 된다.
    • SELECT *
      FROM <첫 번째 테이블>
          CROSS JOIN <두 번째 테이블>

  • SELF JOIN(자체 조인)
    • 자신이 자기 자신과 조인한다는 의미로 1개의 테이블을 사용한다.
    • SELECT <열 목록>
      FROM <테이블> 별칭A
          INNER JOIN <테이블> 별칭B
      [WHERE 검색 조건]

데이터베이스 인덱스

Index 기능은 찾고자하는 부분을 빠르게 찾아서 전달해주는 역할을 한다. 그리고 DB에서도 인덱스라는 기능은 성능을 최적화시켜줄 수 있는 유용한 도구로 많이 사용된다.

DB에서 조회를 할 때 조회 구문을 사용하게 되면 값의 마지막 위치가 어디인지 모르기 때문에 데이터 전체를 탐색함으로써 결과를 반환해준다. 만약 데이터가 수십만 개 이상의 데이터가 있을 때 조회 기능이 자주 실행된다면 계속해서 데이터베이스를 처음부터 끝까지 조회하고 값을 반환하기 때문에 성능이 저하될 수밖에 없다.

그래서 데이터베이스에서도 이러한 문제점을 방지하고자 Index를 활용해 자주 조회되는 column에 대한 Index Table을 따로 만들어 SELECT 문이 들어왔을 때 Index Table에 있는 값들로 결과 값을 조회해서 더 빠르게 원하는 값을 찾을 수 있다.

인덱스가 동작하는 과정은 테이블 생성 시 column에 대한 인덱스를 주면 column 에 대한 Index Table이 생성이 된다. 그리고 나중에 테이블에 column 에 대한 WHERE 문이 포함된 쿼리가 나갈 때 Index Table에 저장된 key-value 값을 참조해서 테이블에서 결과 값을 반환해온다.

그리고 DBMS는 Index를 다양한 알고리즘으로 관리를 하고 있는데 일반적으로 사용되는 알고리즘은 B+ Tree 알고리즘이다.

B - Tree

B-Tree는 다음과 같은 조건을 만족해야 한다.

  1. node의 key의 수가 k개라면, 자식 node의 수는 k+1개이다. 
  2. node의 key는 반드시 정렬된 상태여야 한다. 
  3. 자식 node들의 key는 현재 node의 key를 기준으로 크기 순으로 나뉘게 된다. 
  4. root node는 항상 2개 이상의 자식 node를 갖는다. (root node가 leaf node인 경우 제외) 
  5. M차 트리일 때, root node와 leaf node를 제외한 모든 node는 최소 ⌈M2⌉, 최대 M개의 서브 트리를 갖는다. 
  6. 모든 leaf node들은 같은 level에 있어야 한다. 

B-Tree는 어느 한 데이터의 검색은 효율적이지만, 모든 데이터를 한 번 순회하는 데에는 트리의 모든 노드를 방문해야 하므로 비효율적이다. 이러한 B-Tree의 단점을 개선시킨 자료구조가 B+Tree이다.

B+ Tree

B+Tree는 오직 leaf node에만 데이터를 저장하고 leaf node가 아닌 node에서는 자식 포인터만 저장한다.

그리고 leaf node끼리는 Linked list로 연결되어있다.

또, B+Tree에서는 반드시 leaf node에만 데이터가 저장되기 때문에 중간 node에서 key를 올바르게 찾아가기 위해서 key가 중복될 수 있다. 

B+ Tree를 사용한 장점으로는

1. leaf node를 제외하고 데이터를 저장하지 않기 때문에 메모리를 더 확보할 수 있다. 따라서 하나의 node에 더 많은 포인터를 가질 수 있기 때문에 트리의 높이가 더 낮아지므로 검색 속도를 높일 수 있다. 

2. Full scan을 하는 경우 B+Tree는 leaf node에만 데이터가 저장되어 있고, leaf node끼리 linked list로 연결되어 있기 때문에 선형 시간이 소모된다. 반면 B-Tree는 모든 node를 확인해야 한다. 

반면, B-Tree의 경우 최상의 경우 특정 key를 root node에서 찾을 수 있지만, B+Tree의 경우 반드시 특정 key에 접근하기 위해서 leaf node까지 가야 하는 단점이 있다.  

인덱스에서 B-Tree 대신 주로 B+Tree를 사용하는 이유 인덱스 컬럼은 부등호를 이용한 순차 검색 연산이 자주 발생할 수 있기 때문이다. 따라서 B+Tree의 Linked list를 이용하면 순차 검색을 효율적으로 할 수 있게 된다.  

B+Tree의 검색 과정은 B-Tree와 동일하다. 반면 B+Tree의 삽입과 삭제 과정은 약간의 차이가 있다. 기본적으로 B+Tree의 삽입과 삭제는 항상 leaf node에서 일어난다. 

DML의 사용법

      • SELECT : 테이블에서 데이터를 검색하는데 사용한다.
        1. 모든 열의 데이터를 선택하는 경우
          • 이블의 모든 열을 가져오는 것을 말한다
          • SELECT * FROM 테이블;
          • 테이블이 있다면 '*' 는 테이블의 모든 열을 가져오는 것을 말한다
        2. 특정 열의 데이터만 선택하는 경우
          • SELECT하려는 열을 작성하고 원하는(FROM) 테이블을 작성하면 테이블에서 원하는 열 값을 가져올 수 있다.
          • SELECT 열, 열 FROM 테이블;
        3. 조건에 따라 데이터를 선택하는 경우
          • SELECT 열 FROM 테이블 WHERE 조건;
          • 테이블에서 원하는 조건에 해당하는 열만 가져오겠다는 의미이다.
        4. 여러 테이블을 조인하여 데이터를 선택하는 경우
          • SELECT A테이블.A열, A테이블.B열, B테이블.A열
            FROM A테이블
            JOIN B테이블 ON A테이블.C열 = B테이블.B열;
          • 순수 SELECT만 있는 것이 아닌 JOIN 개념이 같이 들어갔으므로 같이 숙지하거나, 향후 본인 포스팅에서 확인하면 좋을 것 같다.
          • 주문 테이블(Orders)과 고객 테이블(Customer)을 조인하여 각 주문의 ID, 주문한 고객의 이름, 주문 일자를 선택할 때. 활용된다
        5. 데이터를 그룹화하고 집계 함수를 활용할 때 적용하는 경우
          • SELECT 열, 함수
            FROM 테이블
            GROUP BY 그룹화할 열;
          • 'Customers' 테이블에서 'Country' 열을 기준으로 그룹화하며, 각 그룹에 속하는 행들의 수를 Count 함수를 통하여 세어낸 뒤 그 결과를 반환한다.
        6. 정렬된 데이터를 선택하는 경우
          • SELECT 열, 열
            FROM 테이블
            ORDER BY 정렬하려는 기준 열 DESC;
          • 테이블에서 값을 선택한 후. 정렬하기 원하는 기준 열에 대해서 정렬하는 문법이다. SELECT 옆 열은 하나 이상만 오면 된다.
    • INSERT : 테이블에 새로운 데이터를 추가하는 데 사용된다.
      1. INSERT 문에 디폴트 값을 사용하는 경우
        • INSERT INTO 테이블 (열, 열, 열) VALUES
          (값, DEFAULT, 값),
          (값, 값, DEFAULT);

      2. 여러 개의 데이터를 동시에 삽입하는 경우
        • INSERT INTO 테이블 (열, 열, 열) VALUES
          (값, 값, 값),
          (값, 값, 값),
          (값, 값, 값);

      3. 특정 데이터를 조건에 따라 선택적으로 삽입하는 경우
        • INSERT INTO 테이블 (열, 열, 열)
          SELECT 열, 열, 열 FROM 테이블
          WHERE 조건을 넣을 기준 열 >= 값;

      4. 삽입할 데이터를 서브쿼리를 통해 가져오는 경우
        • INSERT INTO 테이블 (열, 열, 열)
          SELECT 열, 열, 열 FROM 테이블
          WHERE 열 = '조건';

    • UPDATE : 테이블의 기존 데이터를 수정하는 데 사용된다.
      1. 조건에 따라 특정 행의 데이터를 업데이트하는 경우
        • UPDATE 테이블 SET 변경 열 = 변경 값
          WHERE 조건 열 = 조건 값;

      2. 여러 열의 데이터를 한 번에 업데이트하는 경우
        • UPDATE 테이블 SET 변경 열 = 변경 조건, 변경 열 = 변경 조건
          WHERE 조건 = 조건 값;

      3. 업데이트할 값을 다른 열의 값으로 설정하는 경우
        • UPDATE 테이블 SET A.행 = A.행 + 값;

      4. 서브쿼리를 사용하여 업데이트할 값을 가져오는 경우
        • UPDATE 테이블 SET 열 = (SELECT 함수(열) FROM 테이블);

      5. 다른 테이블과 조인하여 업데이트할 값을 가져오는 경우
        • UPDATE A.테이블
          JOIN B.테이블 ON A테이블.A열 = B테이블.A열
          SET A테이블.B열 = 조건
          WHERE B테이블.B열 = 조건;

    • DELETE : 테이블에서 조건의 데이터를 삭제하는 데 사용된다.
      1. 조건에 따라 특정 행을 삭제하는 경우
        • DELETE FROM 테이블 WHERE 열 = 조건;

      2. 모든 행을 삭제하는 경우
        • DELETE FROM 테이블;

      3. 여러 조건을 결합하여 행을 삭제하는 경우
        • DELETE FROM 테이블 WHERE 조건 열 >= 23 AND 조건 열 = '아무개';

      4. 서브 쿼리를 사용하여 특정 조건을 충족하는 행을 삭제하는 경우
        • DELETE FROM 테이블 WHERE 열 IN (SELECT 열 FROM 테이블 WHERE 조건 열 = 값);

      5. 다른 테이블과 조인하여 특정 조건을 충족하는 행을 삭제하는 경우
        • DELETE 테이블
          FROM 테이블
          JOIN 조인할 테이블 ON 테이블.ID = 조인할 테이블.StudentID
          WHERE 조인할 테이블.CourseID = 1;

NoSQL vs 관계형


참고

https://hongong.hanbit.co.kr/sql-%EA%B8%B0%EB%B3%B8-%EB%AC%B8%EB%B2%95-joininner-outer-cross-self-join/

 

SQL 기본 문법: JOIN(INNER, OUTER, CROSS, SELF JOIN)

조인은 두 개의 테이블을 서로 묶어서 하나의 결과를 만들어 내는 것을 말한다. INNER JOIN(내부 조인)은 두 테이블을 조인할 때, 두 테이블에 모두 지정한 열의 데이터가 있어야 한다.OUTER JOIN(외부

hongong.hanbit.co.kr

https://rebro.kr/167

 

[DB] 11. 인덱스(Index) - (1) 개념, 장단점, B+Tree 등

[목차] 1. 인덱스(Index)란? 2. 인덱스(Index)의 장단점 3. 인덱스를 사용하면 좋은 경우 4. 인덱스의 자료 구조 1. 인덱스(Index)란? 인덱스(Index)는 데이터베이스의 테이블에 대한 검색 속도를 향상시켜

rebro.kr

https://okeybox.tistory.com/187

 

[Database] DML(Data Manipulation Language)이란? (Lightly)

안녕하세요 성조입니다. 잘못된 지식 전달 사항이 있다면 언제든지 댓글로 피드백 주시면 감사드리겠습니다! 이 포스팅은 MySQL을 기준으로 작성되었습니다. DML(Data Manipulation Language)이란? 데이터

okeybox.tistory.com

 

DNS 레코드 타입

HTTP 버전별 특징

웹 서버와 웹 애플리케이션 서버

소켓 프로그래밍

IP 단편화 피하기 - 경로 MTU 발견

IP 단편화(IP Fragmentation)

  • 패킷을 전송할 때 MTU(Maximum Transmission Unit)을 초과하면 한번에 전송할 수 없어 패킷을 MTU 이하의 조각으로 분할하는 것을 단편화(Fragmentation), 분할된 조각을 단편(Fragment)이라고 한다.
  • 거치는 라우터 마다 전송에 적합한 데이터 링크 계층 프레임으로 변환이 필요하다.
  • 재조립(Reassembly)은 항상 최종 수신지에서만 가능하다.

IPv4 단편화

  • IPv4에서는 발신지 뿐만 아니라 중간 라우터에서도 IP 단편화가 가능하다.
  • 각 조각들은 최종 목적지 시스템에 전달되기 전에는 재조립되지 않고, 최종 목적지에 전달되면 목적지 시스템의 IP 소프트웨어가 원래의 데이터그램으로 재조립 됨

IPv6 단편화

  • IPv6에서는 IP 단편화가 발신지에서만 가능하다.
  • IPv6에서는 라우팅 처리 효율을 높이기 위해, 가급적 IP 단편화를 필요없도록 한다.
  • Ipv4와 달리, 기본 헤더 상에 단편화 제어 관련 필드를 두지 않고, 단편화 확장 헤더를 통해 단편화한다.

MTU(Maximum Transmission Unit)

MTU는 최대 전송 단위로서 TCP/IP Network 등과 같이 패킷 또는 프레임 기반의 네트워크에서 전송될 수 있는 최대 크기의 패킷 또는 프레임을 가리키며 대개 옥텟을 단위로 사용한다.

  • MTU가 너무 크면 큰 크기의 패킷을 처리할 수 없는 라우터를 만나면 재전송을 해야하는 경우가 생길 수 있다.
  • MTU가 너무 작으면 상대적으로 헤더 및 송수신 확인에 따르는 오버헤드가 커진다

단편화 피하기

IP의 본래 목적은 주소 지정과 단편화지만 오늘날의 네트워크 환경에서는 잘 발생하지 않는데, 이는 네트워크 성능의 발전 때문도 있지만 단편화가 잦으면 불필요한 트래픽 증가와 대역폭 낭비를 초래할 수 있기 때문이다. 혹은 단편화된 패킷을 재조립하는 과정에서 발생하는 부하도 성능 저하로 이어질 수 있다.

단편화 피하는 방법

  • IP 패킷을 주고받는 경로에 존재하는 모든 호스트의 처리 가능한 MTU 크기를 고려해서 IP 단편화 없이 주고 받을 수 있는 크기만큼만 전송해야 함
  •  IP 단편화 없이 주고 받을 수 있는 크기 = 경로 MTU(Path MTU)
  • 주고받을 수 있는 경로 MTU를 구해서 해당 크기만큼만 송수신하여 IP 단편화를 회피하는 기술을 경로 MTU 발견(Path MTU discovery) 라고 함
  • 오늘 날의 네트워크는 경로 MTU 발견을 지원하고 처리 가능한 최대 MTU 크기도 균일하여 IP 단편화가 자주 발생하지 않게 됨.

IP 주소

IP 주소는 어느 네트워크의 어느 호스트라는 것을 식별하는 주소입니다. IP 주소는 호스트가 속한 네트워크 주소/브로드캐스트 주소인 네트워크 부(Network Part 또는 Network ID)와 호스트의 주소인 호스트 부 (Host Part 또는 Host ID)로 구성됩니다.

네트워크 부는 어떤 네트워크 인지를 나타내 다른 네트워크와 구분하는 역할을 하고, 호스트 부는 해당 네트워크의 어느 호스트인지를 나타내 다른 호스트와 구분하는 역할을 합니다. 여기서 호스트는 컴퓨터뿐만이 아니라 IP 주소가 할당되는 라우터를 포함합니다.

네트워크 부는 인터넷에 접속되어 있는 모든 네트워크 중에서, 호스트 부는 그 호스트가 속한 네트워크 내에서 유일한 번호를 할당하여 인터넷 전체에서 동일한 IP 주소를 갖는 호스트는 1대밖에 없도록 설정합니다.

따라서 같은 네트워크 안에 있는 컴퓨터, 즉 라우터 없이도 데이터 전송이 가능한 컴퓨터는 네트워크 부가 동일하고 호스트 부만 다릅니다. 달리 말하면 네트워크 부가 다르다는 것은 서로 다른 네트워크라는 의미이고, 라우터를 통하지 않고는 통신이 불가능하다는 뜻입니다. 서로 다른 네트워크가 라우터를 통해 통신이 가능한 것은 라우터가 IP 주소의 네트워크 부를 보고 라우팅을 하여 데이터를 전송하기 때문입니다.

인터넷에 접속 가능한 네트워크를 만들기 위해 IP 주소 할당 기관(NIC)에 IP 주소 할당을 신청하면 할당 기관에서는 네트워크 부까지만 할당합니다. 네트워크 부를 할당받으면 네트워크를 만드는 사람(네트워크 관리자)이 호스트 부를 결정하여 네트워크 부와 호스트 부를 합친 IP 주소를 개별 호스트에 설정하는 것입니다.

IPv4 도입 초기에는 클래스(class)를 기준으로 네트워크부와 호스트를 나누는 방식을 사용했지만, 클래스 방식의 비효율성으로 인해 현재는 클래스에 구애받지 않고 서브넷 마스크(subnet mask) 방식을 사용하고 있습니다.

IP 주소의 클래스

클래스 기준은 IP 주소를 앞에서 8비트씩 나눈 그룹을 조합하여 네트워크 부와 호스트 부를 정한 것입니다. 즉, 클래스에 따라 어디까지가 네트워크 부이고, 어디까지가 호스트 부인지가 결정됩니다.

클래스 A

클래스 A는 IP 주소 32비트 중 앞 8비트를 네트워크 부로, 다음 24비트를 호스트 부로 나눈 것입니다. 네트워크 부의 첫 비트는 클래스 A 식별 비트인 '0'이 할당되기 때문에 00000000 ~ 01111111의 번호가 네트워크 부로 사용됩니다. 이를 십진수로 표기하면 클래스 A의 네트워크 부는 0 ~ 127의 번호가 할당됩니다.

다음 24비트는 호스트 부로 사용되고, 한 네트워크 안에서 할당할 수 있는 호스트 번호는 0.0.0 ~ 255.255.255까지 약 16,777,214개입니다. 말하자면 IP 주소 관리기관이 IP 주소 신청자에게 클래스 A의 네트워크 부 1개를 할당하면, 신청자는 약 1,677만 개의 호스트 부를 마음대로 정할 수 있게 되는 것입니다. 1개의 네트워크에 약 1,677만 개의 호스트를 연결할 수 있기 때문에 클래스 A는 주로 대규모의 네트워크를 구축하는 기관에 할당됩니다.

호스트 부의 모든 비트가 0과 1인 번호는 특수 목적(네트워크 주소와 브로드캐스트 주소)으로 사용하기 때문에 IP 주소의 호스트 부를 할당하는 경우에는 이 두 번호를 제외합니다. 따라서 클래스 A의 호스트 부에서는 2의 24승인 16,777,216에서 2를 뺀 16,777,214개의 번호를 호스트 부에 할당할 수 있습니다.

네트워크 부와 호스트 부를 조합해 클래스 A에서 할당 가능한 IP 주소의 범위는 0.0.0.0 ~ 127.255.255.255이고, 이는 2,147,483,648(2의 31승) 개로 전체 IP 주소의 개수 중 약 50%에 해당합니다.

② 클래스 B

클래스 B는 IP 주소 32비트 중 앞 16비트를 네트워크 부로, 다음 16비트를 호스트 부로 나눈 것입니다. 네트워크 부의 맨 앞 2비트는 클래스 B의 식별 비트인 '10'으로 할당되기 때문에 10000000 ~ 10111111의 번호가 네트워크 부의 첫 8비트로 사용됩니다. 네트워크 부 16비트를 십진수로 표기하면 클래스 B의 네트워크 부는 128.0 ~ 191.255 번호가 할당됩니다.

다음 16비트는 호스트 부로 할당되고, 한 네트워크 안에서 할당할 수 있는 호스트 주소는 65,534(2의 16승 - 2) 개입니다.

네트워크 부와 호스트 부를 조합해 클래스 B에서 할당 가능한 IP 주소의 범위는 128.0.0.0 ~ 191.255.255.255이고, 이는 1,073,741,824(2의 30승) 개로 전체 IP 주소의 개수 중 약 25%에 해당합니다.

③ 클래스 C

클래스 C는 IP 주소 32비트 중 앞 24비트를 네트워크 부로, 다음 8비트를 호스트 부로 나눈 것입니다. 네트워크 부의 맨 앞 3비트는 클래스 C의 식별 비트인 '110'으로 할당되기 때문에 11000000 ~ 11011111의 번호가 네트워크 부의 첫 8비트로 사용됩니다. 네트워크 부 24비트를 십진수로 표기하면 클래스 C의 네트워크 부는 192.0.0 ~ 255.255.255 번호가 할당됩니다.

다음 8비트는 호스트 부로 할당되고, 한 네트워크 안에서 할당할 수 있는 호스트 주소는 254(2의 8승 -2) 개입니다.

네트워크 부와 호스트 부를 조합해 클래스 C에서 할당 가능한 IP 주소는 192.0.0.0 ~ 223.255.255.255입니다.

네트워크 주소와 브로드캐스트 주소

네트워크 1의 호스트나 라우터는 203.179.33.0이나 203.179.33.255의 IP 주소를 사용할 수 없습니다. 마찬가지로 네트워크 2의 호스트나 라우터도 192.168.24.0이나 192.168.24.255의 IP 주소를 사용할 수 없습니다. 다시 말해 클래스 C인 경우 호스트 부 8비트가 모두 0, 즉 십진수로 0인 번호와 호스트 부 8비트가 모두 1, 즉 10진수로 255인 번호는 컴퓨터나 라우터가 자신의 IP 주소로 사용할 수 없습니다.

클래스를 불문하고 IP 주소 중 호스트 부의 모든 비트가 0인 번호는 네트워크 주소로, 모든 비트가 1인 번호는 브로드캐스트 주소라는 특수 목적으로 사용하기 때문에 호스트와 라우터에는 할당하지 않습니다.

네트워크 주소는 전체 네트워크에서 작은 네트워크를 식별할 때 사용되고, 호스트 부가 십진수로 0이면 그 네트워크를 대표하는 주소가 됩니다.

브로드캐스트 주소는 하나의 네트워크에 있는 모든 호스트에 동시에 데이터를 보낼 때 사용되는 전용 IP 주소를 의미합니다.

즉, 전체 네트워크에 데이터를 전송할 때는 호스트 부에 255를 설정하면 됩니다. 만약 IP 주소 203.179.33.13인 호스트가 IP 주소 192.168.24.255로 데이터 를 전송하면 192.168.24.0의 네트워크에 있는 모든 호스트가 데이터를 수신합니다.

서브넷팅과 서브넷

클래스 기반 주소 지정 방식에서는 클래스가 정해지면 네트워크 부와 호스트 부의 길이 및 하나의 네트워크 당 사용 가능한 IP 주소가 정해집니다.

클래스 A를 사용할 경우 한 개의 네트워크 당 약 1,677만 대의 호스트를 연결할 수 있고 클래스 B를 사용할 경우 한 개의 네트워크 당 약 6만 5천대의 호스트를 연결할 수 있습니다. 하지만 실제로 이렇게 많은 호스트를 하나의 네트워크에 연결하는 경우는 거의 없기 때문에 전체 IP 주소의 75%를 차지하는 클래스 A와 클래스 B에서 많은 수의 IP 주소가 사용되지 않고 낭비됩니다.

IP 주소를 효율적으로 활용하기 위해서 클래스 A와 B 같은 대규모 네트워크를 좀 더 작은 네트워크로 분할하는 것을 서브넷팅(Subnetting)이라 하고, 분할된 네트워크를 서브네트워크(Subnetwork) 혹은 줄여서, 서브넷(Subnet)이라고 합니다.


네트워크 부가 0인 클래스 A의 네트워크 1개를 서브넷팅 하여 256개의 작은 네트워크로 분할한 것입니다. 호스트 부의 비트를 서브넷 부로 변경하여 서브넷으로 만듭니다. 서브넷팅을 하면 네트워크 부와 호스트 부로 구성되었던 클래스가 네트워크 부, 서브넷 부, 호스트 부로 변경됩니다.

약 1,667만 대의 컴퓨터를 연결할 수 있던 하나의 네트워크를 256개의 서브넷으로 분할하여 하나의 서브넷에 약 6만 5천 대의 컴퓨터를 연결할 수 있게 만든 것입니다.

서브넷팅이라는 논리적인 방법으로 분할된 네트워크는 라우터에 의해 물리적으로 구별됩니다.

서브넷팅 전에는 라우터 없이 약 1,667만 대의 컴퓨터 간에 통신이 가능했지만, 서브넷팅 후에는 서브넷이 서로 통신을 하기 위해선 라우터가 필요합니다.

이처럼 서브넷팅을 통해 호스트 부가 서브넷 부로 변경되면서 네트워크 부가 확장됩니다. 따라서 클래스 A의 IP 주소를 서브 넷팅하면 네트워크 부가 변경되는데 IP 주소만으로는 변경된 네트워크 부가 어디까지인지 알 수가 없습니다. 아래 <그림 8>과 같이 서브넷팅을 한다고 해서 IP 주소가 변경되지 않기 때문입니다.

IP 주소만으로 네트워크 부와 호스트 부의 경계를 알 수 있도록 만든 클래스가 서브넷팅으로 인해 그 의미를 잃게 된 것입니다.

따라서 IP 주소를 서브넷팅하는 경우 IP 주소와 별도로 어디까지가 네트워크 부이고 어디까지가 호스트 부인지 구별할 수 있는 식별자가 필요한데, 이 식별자를 서브넷 마스크라고 합니다.

서브넷 마스크

서브넷 마스크는 IP 주소의 네트워크 부와 호스트 부의 경계를 식별하기 위해 만든 숫자입니다. 서브넷 마스크는 IP 주소의 32비트에 대응한 32비트로 구성되어 있습니다. 즉, 서브넷 마스크는 IP 주소처럼 32개의 0 또는 1로 구성된 값입니다.

IP 주소의 비트가 네트워크 부이면 이 비트에 대응하는 서브넷 마스크의 비트는 1이 되고, IP 주소의 비트가 호스트 부이면 서브넷 마스크의 비트는 0이 됩니다. 이러한 방법으로 클래스에 구애받지 않고 IP 주소의 네트워크부를 식별할 수 있습니다.


서브넷 마스크 표기법

① 십진수 표기법

32비트로 표현된 서브넷 마스크도 IP 주소와 마찬가지로 보기 쉽게 전체 32비트를 8비트씩 4그룹으로 나누어, 각 그룹을 십진수로 변환하고, 그룹의 경계에 '.'을 넣은 방법으로 표기하고 있습니다.


② 프리픽스 표기법

서브넷 마스크를 슬래시(/)와 네트워크부 비트수로 나타내는 프리픽스(prefix) 표기법을 사용할 수 있습니다. 프리픽스로 표기한 서브넷 마스크는 IP 주소와 함께 묶어 표현합니다.

네트워크 부가 8비트인 클래스 A에서 8비트를 서브넷팅한 IP 주소를 서브넷 마스크로 정의

서브넷팅 후 IP 주소는 동일하지만 달라진 서브넷 마스크로 서브넷팅 후에는 앞에서 16비트까지가 네트워크 부인 것을 식별할 수 있게 됩니다.

서브넷 마스크의 유용성

서브넷 마스크를 사용하면 8비트 단위가 아닌 1비트 단위로 네트워크 부를 구성할 수 있기 때문에 더 세분화된 네트워크를 만들 수 있습니다.

예를 들어, 약 60개의 호스트를 연결하는 네트워크를 구축하는 경우 클래스 기준에서는 가장 적은 수의 호스트를 연결할 수 있는 클래스 C의 네트워크 부를 사용할 수밖에 없습니다. 다시 말해 60개의 호스트를 연결하는 하나의 네트워크를 만들기 위해 IP 주소 관리 기관에 IP 주소 할당 신청을 하면 주소 관리 기관에서는 C클래스의 네트워크 부 주소 하나를 할당하게 되고 신청자는 254(2의 8승 - 2) 개의 IP 주소를 사용할 수 있게 됩니다. 60개가 필요한 데 254개가 할당되니 나머지 IP 주소는 누구도 사용하지 못하는 주소가 됩니다.

만약 호스트 부에서 2비트를 빌려 서브넷팅 하면 클래스 C의 1개의 네트워크를 4개로 분할하고 각 네트워크 당 62(2의 6승 -2)의 IP 주소를 사용할 수 있게 됩니다. 따라서 60개의 IP 주소가 필요한 사람에게 서브넷 마스크가 255.255.255.192인 IP 주소의 네트워크 부 주소 하나를 할당하고, 나머지 3개의 네트워크 부 주소를 다른 사람에게 할당하는 방법으로 IP 주소를 낭비하지 않을 수 있습니다.

현재는 클래스와 상관없이 한 네트워크에 연결하고 싶은 호스트들의 규모에 맞게 네트워크 부와 호스트 부의 길이를 비트 단위로 유연하게 변경할 수 있는 서브넷 마스크를 사용하여 IP 주소를 할당합니다.

클래스 방식에서 네트워크 부를 8비트 단위로 IP 주소 32비트의 맨 앞에서부터 선택한 것처럼 서브넷 마스크 방식에서도 네트워크 부를 1비트 단위로 IP 주소 32비트의 맨 앞에서부터 차례로 선택합니다. 따라서 서브넷 마스크는 반드시 연속한 '1' 과 연속한 '0' 으로 구성됩니다. '1'과 '0'이 교대로 나타나는 서브넷 마스크는 없습니다. 이로 인해 서브넷 마스크의 십진수 표기법이 가질 수 있는 값은 다음과 같습니다.

NAT와 NAPT

인터넷이 보편화되면서 IP 주소의 부족 문제를 해결하고, 네트워크 보안을 강화하기 위해 다양한 기술이 개발되었습니다. 그 중에서 NAT(Network Address Translation)NAPT(Network Address Port Translation)는 네트워크에서 자주 사용되는 기술입니다.

개념

NAT와 NAPT는 현대 네트워크 환경에서 필수적인 기술입니다.

NAT는 내부 네트워크의 사설 IP 주소를 공인 IP 주소로 변환하여 인터넷과 통신할 수 있게 하는 기술로 IP 주소의 절약과 보안 향상에 기여합니다.

NAPT는 NAT의 확장 개념으로, 단순히 IP 주소를 변환하는 것에 더해 포트 번호까지 함께 변환합니다. 이를 통해 하나의 공인 IP 주소를 사용하여 다수의 내부 네트워크 장비들이 동시에 인터넷에 접속할 수 있습니다. 다수의 장치가 동시에 인터넷에 접속할 수 있게 합니다

NAT

특징

  • 사설 IP 주소를 외부에서 감출 수 있음.
  • IP 주소의 재사용이 가능해, 공인 IP 주소의 사용을 줄일 수 있음.
  • 네트워크 보안을 강화할 수 있음.

장점

  • 공인 IP 주소의 절약.
  • 내부 네트워크 구조를 외부에 노출시키지 않음으로써 보안 강화.

단점

  • 네트워크 연결에 있어 일부 프로토콜과의 호환성 문제가 발생할 수 있음.
  • 특정 애플리케이션에서 문제가 발생할 수 있음.

사용 사례

  • 소규모 가정용 네트워크나 중소기업에서 내부 네트워크 보호 및 IP 주소 절약을 위해 널리 사용됩니다.

NAPT

 

개념

  • NAPT는 NAT의 확장 개념으로, 단순히 IP 주소를 변환하는 것에 더해 포트 번호까지 함께 변환합니다. 이를 통해 하나의 공인 IP 주소를 사용하여 다수의 내부 네트워크 장비들이 동시에 인터넷에 접속할 수 있습니다.

특징

  • IP 주소와 포트 번호를 모두 변환함으로써, 여러 장치가 동시에 하나의 공인 IP 주소를 공유할 수 있음.
  • "포트 포워딩" 설정을 통해 외부에서 내부 네트워크의 특정 장치로 직접 접근할 수 있음.

장점

  • 단일 공인 IP 주소로 여러 장치의 인터넷 접속을 가능하게 함.
  • 내부 네트워크의 세밀한 트래픽 제어가 가능.

단점

  • 포트 번호가 한정되어 있어, 매우 많은 연결 시 포트 고갈 문제가 발생할 수 있음.
  • NAT와 마찬가지로, 특정 애플리케이션과의 호환성 문제 발생 가능.

사용 사례

  • 대규모 네트워크 환경에서 공인 IP 주소 절약과 내부 장치 보호를 위해 주로 사용됩니다.
  • 가정용 라우터에서도 NAPT는 일반적으로 기본 설정으로 제공됩니다.

 


참고자료

출처: https://better-together.tistory.com/118 [변계사 Sam의 테크 스타트업!:티스토리]

 

네트워크 통신이란

어원은 그물을 뜻하는 net과 work의 합성어이며 그물을 짜는 행위를 가리키는 명사에서 임의의 연결망을 지칭하는 용어로 그 범위가 확장된 단어이다. 또한 통신 장치 간에 데이터 및 정보를 교환하는 것을 의미한다. 정보를 교환하기 위해서는 프로토콜(Protocol)이라는 통신 규약에 따라 정보를 전송하고 공유한다.

네트워크 통신의 발달로 전세계 소식을 실시간으로 확인하고, 정보를 교류할 수 있게 되었으며, 사물 인터넷(IoT)기기의 발달과 보급으로 다양한 전자 기기들을 제어할 수 있게 되었다.

네트워크 통신의 역사

  • 1800년경
    • 유선 통신의 시작
    • 볼타가 최초로 전지를 발명하여 전선을 통해 신호를 보내는 방법을 연구하기 시작
    • 사무엘 모르스가 알파벳 문자에 대해 점과 대시를 사용해 신호를 보냈는데 이게 모스부호
  • 1864년
    • 무선 통신의 시작
    • 제임스 클럭 맥스웰이 전자기파가 대기 중에서 전파된다고 예측함
  •  1876년
    • 알렉산더 그레이엄 벨이 음성을 전달할 수 있는 전화기를 개발
  • 1888년
    • 하인리히 루돌프 헤르츠가 실험을 통해 라디오파를 주고받음으로써 전자기파가 대기 중에 있다고 입증
  • 1894년
    • 마르코니가 지상의 금속판에 연결된 수직 안테나를 사용해 신호 전달의 범위를 증가시킬 수 있음을 증명
    • 해당 실험으로 121km 까지 소식 교환이 가능하게 됨
  • 1958년
    • 컴퓨터 통신의 시작
    • 벨 텔레폰 연구소의 조지 스티비츠가 전화 교환회로를 산술기기로 발전시킨 모델-K 기기를 개발
    • 모델-K가 CNC (Complex Number Calculator) 로 발전해 더 정교한 산술 계산이 가능하게 됨.
    • CNC는 입력기가 본체와 따로 떨어져 전화선으로 데이터를 주고받을 수 있었는데 이런 원격 데이터 통신 방식은 이후 모뎀, 시분할 시스템, 컴퓨터 네트워크 기술로 발전
  • 1964년 
    • 군사 작전 수행을 위한 고성능 컴퓨터 개발을 목적으로 ARPA (Agency for Advanced Research Project Agency) 에 커맨드 컨트롤 리서치라는 부서를 설립
    •  J.C.R 리클라이더에 의해 커맨드컨트롤 리서치는 순수 컴퓨터 과학 연구를 위한 IPTO(Information Processing Techniques Office)로 재탄생
  • 1965년
    • 세계 최초의 컴퓨터 네트워크 개발에 착수, 이 네트워크가 ARPANET (Advanced Research Project Agency Network) 탄생으로 이루어진다.
    • 이후 최초의 장거리 컴퓨터 통신이 이루어짐. MIT 링컨 연구소의 TX-2가 캘리포니아 산타 모니카 SDC(System Development Corporation)의 Q-32 컴퓨터와 전화선으로 직접 통신함.
    • ARPA가 처음 시도한 장거리 컴퓨터 통신망이었다. 기존의 방대한 전화망을 이용해 컴퓨터 네트워크를 구축하려고 한 것이다.  그러나 너무 느리고 비싸고 비효율적이었다.
    • TX-2와 SDC Q-32 네트워크 구축을 제안한 심리학자 톰 마릴이 컴퓨터 간에 메시지를 전달하는 과정에서  메시지가 제대로 도착했는지 확인하는 방법을 가리켜 '기술적 은어' 라는 뜻으로 프로토콜이라고 부름
  • 1967년
    • ARPA가 ACM 모임에서 각 호스트를 IMP라는 특정 컴퓨터에 연결하고, IMP들을 서로 연결하는 ARPANET이라는 아이디어를 제안
    • IMP는 현재의 라우터 개념과 유사함.
  • 1969년
    • 4개의 노드를 네트워크로 구성하고 NCP라는 프로토콜을 호스트 간 통신에 사용
  • 1971년
    • 레이 톰린슨이 전자 메일 프로그램을 개발
    • 톰린슨이 전자 메일에 @기호를 넣었고, 지금도 이 방식에 따라 아이디 뒤에 @기호를 사용
  • 1972년
    • Vinton Cerf Bob Kanhn이 네트워크를 통해 패킷을 전송하는 중계 하드웨어 역할을 하는 게이트웨이를 개발
  • 1973년
    • 빈트 서프와 로버트 칸이 TCP/IP프로토콜과 인터넷 구조를 설계
    • 컴퓨터와 터미널로 구성되는 네트워크로 IBM의 SNA망이 최초
  • 1974년
    • 제록스가 이더넷을 개발
    • 이더넷은 네트워크 구조를 호스트-터미널 구조에서 오늘날과 같은 클라이언트-서버 구조로 전환하는데 큰 역할을 함
  • 1979년
    • 유즈넷 탄생
  • 1981년
    • TCP를 두 개의 프로토콜인 TCP와 IP로 나누고 이를 표준화
    • 유닉스 운영체제에 TCP/IP가 배포
    • TCP/IP는 ARPANET의 공식 프로토콜이 됨
  • 1983년
    • ARPANET에서 MILNET (군사용 네트워크)이 분리
    • 존 포스텔이 도메인 이름 시스템을 개발
  • 1984년
    • DNS가 구성되어 네트워크가 폭발적으로 확장되었다.
    • OSI(Open Systems Interconnection)모델, 컴퓨터 및 네트워크 모든 시스템을 상호 연동하여 사용할 수 있도록 표준화한 모델을 발표
  • 1989년
    • 버너스-리 라는 CERN(유럽핵물리연구소)의 물리학자가 웹 개념을 제안
  • 1990년
    • ARPANET이 해체되고 NSFNET이 만들어짐
    • 버너스-리라가 동 료인 로버트 카이유와 서로 다른 컴퓨터끼리 정보를 공유하고 서로 링크되어 찾기 쉽도록 하이퍼텍스트형태의 서비스를 도입
    • 웹 브라우저, WWW(World Wide Web)가 등장
  • 2007년
    • 휴대전화와 카메라 그리고 인터넷 기능이 하나로 통합된 iPhone(스마트폰)이 등장

OSI 4계층 / 7계층

OSI 7계층

국제 표준화 기구(ISO, International Organization Standardization)

  • 물리 계층(Physical Layer)
    • 가장 최하위 계층으로 비트 신호를 주고받는 계층
    • 물리적인 유무선 통신 매체(LAN, 케이블 등)을 통해 비트 스트림(BitStream)을 전송
  • 데이터 링크 계층(Data Link Layer)
    • 물리 계층에서는 단순히 데이터를 전달만 하기에 데이터 상에 문제가 발생하여도 알 수가 없기 때문에 같은 LAN에 속한 호스트끼리 올바르게 정보를 주고받기 위한 계층
    • 데이터 시작과 끝 부분에 제어 정보를 추가하여 에러를 확인하고 제어
    • 호스트를 식별할 수 있는 주소(MAC 주소) 등을 할당하여 네트워크 장비들을 식별
  • 네트워크 계층(Network Layer)
    • 네트워크 간 통신을 가능하게 하는 계층
    • 데이터를 전송하는 스위칭(Switching)기능과 데이터를 전송을 위한 최적의 경로를 결정하는 라우팅(Routing)기능을 제공
    • 네트워크 간 통신 과정에서 호스트를 식별할 수 있는 주소(IP 주소)를 이용해 다른 네트워크와 통신을 주고받기 위해 필요한 계층
  • 전송 계층(Transfer Layer)
    • 수신지와 목적지를 감독하면서 전체 데이터가 오류 없이 순서대로 도착하는 것을 보장하며 목적지까지 에러 제어, 흐름 제어 등을 수행하며 신뢰성 있는 데이터 전송을 담당
    • 대표적인 프로토콜로 TCP(Transport Protocol)와 UDP(User Datagram Protocol)가 있다
  • 세션 계층(Session Layer)
    • 응용 프로그램 간의 연결 상태를 의미하는 세션(Session)을 유지하거나 새롭게 생성하고 필요하다면 연결을 끊는 역할을 하며 관리하기 위한 계층
    • 파일을 전송하던 중에 전송이 중단되어 이어서 전송 해야 하는 경우 데이터를 동기화하고 통신 세션을 설정하고 유지하는 역할을 함.
  • 표현 계층(Presentation Layer)
    • 데이터를 변환, 인코딩, 압축, 암호화, 복호화 등 번역을 수행하여 상위 계층인 응용 계층이 이해할 수 있는 데이터로 가공하는 계층
    • 대표적인 예시로 JPEG, AVI 등이 있음
  • 응용 계층(Application Layer)
    • 사용자와 가장 밀접하게 맞닿아 있어 통신할 수 있는 여러 응용 네트워크 서비스를 제공한다.
    • 대표적인 예시로 웹 브라우저, FTP등이 있다

TCP/IP 모델 (OSI 4계층)

  • 네트워크 엑세스 계층(Network Access Layer)
    • 물리적인 연결 매체에 연결되어 패킷을 주고받는 작업을 담당
    • OSI 모델의 물리, 데이터 링크 계층과 유사
  • 인터넷 계층(Internet Layer)
    • IP(Internet Protocol)를 사용하여 목적지까지 데이터를 전달하는 기능을 담당
    • 인터넷 주소를 부여(Addressing)기능과 최적의 경로를 탐색하는 라우팅(Routing)기능을 제공
    • 인터넷 계층의 프로토콜로 IP, ARP, ICMP등이 있음
    • OSI 모델의 네트워크 계층과 유사
  • 전송 계층(Transport Layer)
    • 수신지와 목적지를 감독하면서 데이터가 오류 없이 도착하는 것을 담당
    • 전송 계층의 프로토콜로 연결형 서비스인 TCP, 비연결형 서비스 UDP가 있음
    • OSI 모델의 전송 계층과 유사
  • 응용 계층(Application Layer)
    • 사용자와 통신할 수 있는 응용 서비스를 제공
    •  웹 브라우저, FTP등이 있음
    • OSI 모델의 세션, 표현, 응용 계층을 합친 것과 유사

https://softeer.ai/practice/6275

 

Softeer - 현대자동차그룹 SW인재확보플랫폼

 

softeer.ai

풀이

로봇을 정해진 방향으로 이동시키고 회전시키는 구현 문제이다. 

신경 써야할 점은 로봇이 A 명령을 받았을 때 2칸을 움직인다는 것과 처음 시작을 어디에서 해야하는지 이다.

이동을 2칸씩 하기 때문에 로봇이 길에서 한 칸을 건너뛰어서 이동하는 경우가 생길 수 있어 도착하는 지점과 중간에 있는 지점을 확인해줘야합니다.

처음 시작은 로봇 경로의 도착 지점 혹은 시작 지점을 찾아야 하는데 해당 지점은 근처 #의 개수를 통해 찾아 주었습니다.

코드

#include <iostream>
#include <vector>
#define MAX 26
using namespace std;

int a, b, startX, startY, dir;
char board[MAX][MAX];
int visited[MAX][MAX] = {};
int dx[4] = {0, 1, 0, -1};
int dy[4] = {1, 0, -1, 0};
char dirs[] = {'>', 'v', '<', '^'};
vector<char> commands;

void init(){
  ios_base::sync_with_stdio(false); 
  cin.tie(NULL); cout.tie(NULL);
  cin >> a >> b;

  for(int i = 0; i < a; i++){
    for(int j = 0; j < b; j++){
      cin >> board[i][j];
    }
  }
}

bool isValid(int x, int y) {    
    return x >= 0 && x < a && y >= 0 && y < b && board[x][y] == '#' && !visited[x][y];
}

void start(){
  for(int i = 0; i < a; i++){
    for(int j = 0; j < b; j++){
      int cnt = 0;
      for (int k = 0; k < 4; k++) {
        int nx = i + dx[k];
        int ny = j + dy[k];
          if (board[i][j] == '#' && isValid(nx, ny)){
            cnt++;
            dir = k;

            if(cnt > 1)
              break;
        }
      }
      if(cnt == 1){
        startX = i;
        startY = j;
        return;
      }
    }
  }
}

void move(int x, int y, int dir){
  visited[x][y] = true;

  for (int i = 0; i < 4; i++) {
    int new_dir = (dir + i) % 4;
    int nx = x + dx[new_dir];
    int ny = y + dy[new_dir];

    if (isValid(nx, ny)) {
      visited[nx][ny] = true;

      if (new_dir != dir) {
        int turn = new_dir - dir;
        if (turn == 1 || turn == -3)
          commands.push_back('R');
        else if (turn == -1 || turn == 3)
          commands.push_back('L');
      }
      commands.push_back('A');
        nx += dx[new_dir];
        ny += dy[new_dir];
      move(nx, ny, new_dir);
    }
  }
}

int main() {
  init();
  start();

  move(startX, startY, dir);
  cout << startX + 1 << " " << startY + 1 << "\n";
  cout << dirs[dir] << '\n';
  
  for (char command : commands) {
    cout << command;
  }
  
  return 0;
}

성능

https://softeer.ai/practice/6247

 

Softeer - 현대자동차그룹 SW인재확보플랫폼

 

softeer.ai

풀이

처음엔 간단하게 중간값부터 포인터 두 개를 사용해서 조합을 만들려고 했다. 정렬해서 중간 값 좌우를 보면 간단히 조합의 개수를 셀 수 있는데다가 모든 조합을 구하는 것이 아니라 조합의 개수를 찾는 것이기 때문에 중간 값의 좌우 개수를 곱해주면 되기 때문이다.

다만 간과한 것이 중간 값이 다양하게 나올 때마다 중간에 있는 중간 값을 찾아줘야 하는데 이 경우 find 함수를 쓰게 되면 시간복잡도가 O(n) 이다. 거기에 중간 값이 q 번만큼 나오기 때문에 최종 시간 복잡도는
정렬 + (find * 중간값 input) = O(n log n + n * q) 가 된다. n의 최대는 5만, q의 최대는 20만이기 때문에 곱하면 100억이 된다...

그래서 시간 복잡도를 줄이기 위해 값들과 함께 인덱스를 미리 unordered_map에 저장하는 방식을 사용했습니다. 해당 방식을 사용하면 find와 다르게 주어지는 중간 값으로 바로 인덱스를 찾을 수 있어 시간복잡도가 O(1)이 됩니다.

코드

오답 코드(투 포인터)

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

int n, q;
long long temp, m;
vector<int> fuel;
int cnt;

int init(){
  ios::sync_with_stdio(false);
  cin.tie(0); cout.tie(0);

  cin >> n >> q;
  fuel.resize(n);
  for(int i = 0; i < n; i++){
    cin >> fuel[i];
  }

  sort(fuel.begin(), fuel.end());
}

void question(int m){
  int idx = find(fuel.begin(), fuel.end(), m) - fuel.begin();

  if (idx >= n || fuel[idx] != m){
    cout << 0 << "\n";
    return;
  }

  cout << idx * (n - idx - 1) << "\n";
}

int main(){
  init();
  for(int i = 0; i < q; i++){
    cin >> m;
    question(m);
  }

  return 0;
}

정답 코드

#include <iostream>
#include <vector>
#include <unordered_map>
#include <algorithm>

using namespace std;

int main() {
  ios::sync_with_stdio(false);
  cin.tie(0);
  
  int n, q;
  cin >> n >> q;
  
  vector<int> data(n);
  for (int i = 0; i < n; ++i) {
    cin >> data[i];
  }
  
  sort(data.begin(), data.end());

  unordered_map<int, int> index;
  for (int i = 0; i < n; ++i) {
    index[data[i]] = i;
  }

  while (q--) {
    int m;
    cin >> m;

    if (index.find(m) == index.end()) {
      cout << 0 << "\n";
    } else {
      int median_idx = index[m];
      if (m == data[0] || m == data[n - 1]) {
        cout << 0 << "\n";
      } else {
        int left_cnt = median_idx;
        int right_cnt = n - median_idx - 1;
        cout << left_cnt * right_cnt << "\n";
      }
    }
  }

  return 0;
}

 

성능

+ Recent posts