[인프런 워밍업클럽 CS 2기] 1주차 발자국

[인프런 워밍업클럽 CS 2기] 1주차 발자국

자료구조와 알고리즘

  • 자료구조 : 데이터를 효율적으로 저장하고 관리하는 방법. 배열, 연결리스트, 스택, 큐, 트리, 그래프 등이 있다.

     

  • 알고리즘 : 어떤 문제를 해결하기 위해 정해진 일련의 절차나 규칙을 의미하며, 주어진 입력에 대해 결과를 도출하는 과정이다.

     

  • 시간 복잡도 : 알고리즘이 수행되는 데 걸리는 시간을 입력 크기에 대한 함수로 표현한 것.

     

    주로 O(1), O(n), O(n²) 등의 표기법으로 나타낸다.

     

  • 배열 : 고정된 크기의 연속된 메모리 공간에 데이터를 저장하는 자료구조. 인덱스를 통해 빠르게 접근할 수 있지만 크기가 고정되어 있으며 중간에 데이터를 삽입하거나 삭제하는 데는 비효율적이다.

     

    • 시간 복잡도: 접근 O(1), 삽입/삭제 O(n)

       

     

    연결 리스트(Linked List) :

  • 각 노드가 데이터와 다음 노드의 주소를 가지는 동적 자료구조. 삽입과 삭제가 용이하지만,

    특정 위치에 접근하려면 순차적으로 접근해야 하므로 시간이 더 걸린다.

    • 시간 복잡도: 접근 O(n), 삽입/삭제 O(1)

export class Node{
  constructor(data, next = null){
    this.data = data;
    this.next = next;
  }
}

// * 연결 리스트 추상 자료형
// 1. 모든 데이터 출력 -> printAll();
// 2. 모든 데이터 제거 -> clear();
// 3. 원하는 인덱스에 데이터를 삽입 -> insertAt(index, data);
// 4. 마지막 데이터 뒤에 데이터를 삽입 -> insertLast(data);
// 5. 원하는 인덱스에 데이터를 삭제 -> deleteAt(index);
// 6. 마지막 데이터를 제거 -> deleteLast();
// 7. 원하는 인덱스에 있는 데이터 읽기 -> getNodeAt(index);

export class LinkedList {
  constructor(){
    // 연결 리스트의 시작 Node를 가리킨다.
    this.head = null;

    // 총 저장된 Node의 수
    this.count = 0;
  }

  /**
   * 원하는 인덱스에 데이터를 삽입
   * @param {number} index - 삽입할 위치 index
   * @param {any} data - 삽입할 데이터
   */
  insertAt(index, data){    
    // 예외 처리
    if(index > this.count || index < 0) throw new Error('범위를 넘어갔습니다.');
    
    // 새로운 Node 생성
    let newNode = new Node(data); 
   
    if(index === 0){ 
      // 리스트의 가장 앞부분에 삽입할 경우

      // 1. 생성된 Node의 next가 head를 가리키도록 설정
      newNode.next = this.head; 
      // 2. 생성된 Node를 head로 설정
      this.head = newNode; 

    }else{ 
      // 앞부분 삽입을 제외한 원하는 인덱스에 삽입할 경우
      
      // head에서 부터 시작하기 때문에 head로 초기화
      let currentNode = this.head; 

      // 목표 인덱스 바로 전까지 next를 이용해 currentNode를 이동시킨다.
      for(let i=0; i<index -1; i++){
        currentNode = currentNode.next;  
      }

      // 1. 새로운 Node가 currentNode의 next Node를 가리키도록 설정
      newNode.next = currentNode.next;
      // 2. currentNode가 새로운 Node를 가리키도록 설정
      currentNode.next = newNode
    }

    // 새로운 Node가 삽입되었으니 count를 증가
    this.count++;
  }

  /**
   * 모든 데이터 출력
   * @returns {string} - 모든 Node의 data를 하나의 문자열에 담아 반환
   */
  printAll(){
    let currentNode = this.head;
    let text = '['

    while(currentNode){
      text += currentNode.data
      currentNode = currentNode.next;

      if(currentNode) text += ', ';
    }
    text += ']';
    console.log(text);    
    return text;
  }

  /**
   * 모든 데이터 제거
   */
  clear(){
    this.head = null; // head 초기화
    this.count = 0; // count 초기화
  }

  /**
   * 마지막 데이터 뒤에 데이터를 삽입
   * @param {any} data - 삽입할 데이터
   */
  insertLast(data){
    this.insertAt(this.count, data);
  }

  /**
   * 원하는 인덱스에 데이터를 삭제
   * @param {number} index - 제거할 위치 index
   * @return {{data: number, next: {data: number, next: any} | null}} - 제거된 노드의 데이터와 그 다음 노드에 대한 정보.
   */
  deleteAt(index){
    // 예외 처리
    if(index >= this.count || index < 0) throw new Error('제거할 수 없습니다.');

    let currentNode = this.head;

    if(index === 0){
      // 리스트의 가장 앞부분에 제거할 경우

      // 반환하기 위해 삭제된 Node를 저장
      const deleteNode = this.head;
      // head를 다음 Node인 head.next로 설정
      this.head = this.head.next;
      // 제거했으므로 count를 내린다.
      this.count--;

      // 삭제한 Node 반환
      return deleteNode;

    }else{
      // 리스트의 가장 앞부분을 제외한 위치를 제거할 경우

      // 제거할 Node 이전 Node까지 순회해 제거할 Node의 이전 Node를 가리키게 한다.
      for(let i=0; i<index -1; i++){
        currentNode = currentNode.next;
      }

      // currentNode.next가 참조하고 있는 다음 Node 즉, 제거해야 할 Node를 저장
      let deleteNode = currentNode.next; 
      
      // 제거할 Node의 next Node를 가리키게 한다.
      currentNode.next = currentNode.next.next;

      // 제거했으므로 count를 내린다.
      this.count--;

      // 삭제한 Node 반환
      return deleteNode;      
    }
  }

  /**
   * 마지막 데이터를 제거
   * @return {{data: number, next: {data: number, next: any} | null}} - 제거된 노드의 데이터와 그 다음 노드에 대한 정보.
   */
  deleteLast(){
    return this.deleteAt(this.count - 1);
  }

  /**
   * 원하는 인덱스에 있는 데이터 읽기 
   * @param {number} index - 읽고자하는 Node의 index
   * @return {{data: number, next: any}} - 해당 Node 반환
   */
  getNodeAt(index){
    // 예외 처리
    if(index >= this.count || index < 0) throw new Error('범위를 넘어갔습니다.');

    let currentNode = this.head;

    // currentNode가 해당 index까지 순회한다.
    for(let i=0; i< index ; i++){
      currentNode = currentNode.next;
    }

    // 최종적으로 이동한 Node를 반환
    return currentNode;
  }
}

 

 

스택(Stack) :

  • 후입선출(LIFO: Last In, First Out) 방식의 자료구조. 마지막에 추가된 요소가 먼저 제거된다. 함수 호출, 되돌리기 기능 등에서 사용된다.

    • 시간 복잡도: 접근/삽입/삭제 O(1)

import { LinkedList } from './../LinkedList/LinikedList.mjs';

// * 스택의 추상자료형
// 1. 데이터 삽입 -> push();
// 2. 데이터 제거 -> pop();
// 3. top에 있는 데이터 참조 -> peek();
// 4. 스택이 비었는지 체크 -> isEmpty();

export class Stack{
  constructor(){
    this.list = new LinkedList();
  }

  push(data){
    this.list.insertAt(0, data);
  }

  pop(){
    try {      
      return this.list.deleteAt(0);
    } catch (error) {
      return null;
    }
  }

  peek(){
    return this.list.getNodeAt(0);
  }

  isEmpty(){
    return this.list.count === 0;
  }
}

 

 

큐 (Queue):

  • 선입선출(FIFO: First In, First Out) 방식의 자료구조. 먼저 들어온 데이터가 먼저 처리된다. 대기열, 주문 처리 등 순차적으로 처리해야 할 때 사용된다.

    • 시간 복잡도: 접근/삽입/삭제 O(1)

       

import { DoublyLinkedList } from "./DoublyLinkedList.mjs";
// * 기존에 구현한 Node에서 이전 Node도 가리킬 수 있도록 Node를 수정
// * 기존의 LinkedLst를 연결 리스트의 끝을 가리킬 수 있도록 수정 -> DoublyLinkedList

// * 큐의 추상자료형
// 1. 데이터 삽입 -> enqueue();
// 2. 데이터 제거 -> dequeue();
// 3. 데이터 참조 -> front();
// 4. 큐가 비었는지 확인 -> isEmpty();

class Queue{
    constructor(){
        this.list = new DoublyLinkedList();
    }

    enqueue(data){
        this.list.insertAt(0, data);
    }

    dequeue(){
        try{
            return this.list.deleteLast();
        } catch(e){
            return null;
        }
    }

    front(){
        return this.list.tail;
    }

    isEmpty(){
        return (this.list.count == 0);
    }
}

export {Queue};

 


운영체제

  • 폴링 방식 : 주기적으로 상태를 확인하는 방식.

  • 인터럽트 : 외부나 내부에서 발생하는 사건이 CPU의 주의를 끌어 현재 작업을 중단하고 즉시 그 사건을 처리하도록 하는 메커니즘.

  • 프로그램과 프로세스 :

    • 프로그램: 실행되지 않은 상태의 정적 코드. 하드디스크 등에 저장된 명령어 집합.

       

    • 프로세스: 실행 중인 프로그램. 운영체제에서 메모리와 CPU 등의 자원을 할당받아 실행되는 단위.

  • PCB : 프로세스 제어 블록으로, 운영체제가 프로세스를 관리하기 위한 정보(프로세스 상태, 프로그램 카운터, 메모리 정보 등)를 담고 있는 구조체.

  • 프로세스 : 실행 중인 프로그램으로, CPU와 메모리 자원을 할당받아 동작하며 독립적인 실행 단위를 형성한다.

  • 컨텍스트 스위칭: 하나의 프로세스나 스레드가 CPU에서 실행되다가 다른 프로세스로 전환될 때, 현재 프로세스의 상태를 저장하고 새로운 프로세스의 상태를 복구하는 과정.

     

     

     

     


     

댓글을 작성해보세요.

채널톡 아이콘