알고리즘 복잡도 표현 방법

 

1. 알고리즘 복잡도 계산이 필요한 이유

하나의 문제를 푸는 알고리즘은 다양할 수 있음

ex) 정수의 절대값 구하기

- 방법1 : 정수값을 제곱한 값에 다시 루트를 씌우기

- 방법2 : 정수가 음수인지 확인해서, 음수일 때만 -1을 곱하기

다양한 알고리즘 중 어느 알고리즘이 더 좋은지를 분석하기 위해, 복잡도를 정의하고 계산함

 

2. 알고리즘 복잡도 계산 항목

  • 시간 복잡도 : 알고리즘 실행 속도
  • 공간 복잡도 : 알고리즘이 사용하는 메모리 사이즈
가장 중요한 시간 복잡도를 꼭 이해하고 계산할 수 있어야 함

 

알고리즘 시간 복잡도의 주요 요소

반복문으로 계산

 

3. 알고리즘 성능 표기법

  • Big O (빅-오) 표기법 : O(N)
    • 알고리즘 최악의 실행 시간을 표기
    • 가장 많이/일반적으로 사용함
    • 아무리 최악의 상황이라도, 이정도의 성능은 보장한다는 의미이기 떄문
  • 오메가 표기법 : 오메가(N)
    • 오메가 표기법은 알고리즘 최상의 실행 시간을 표기
  • 세타 표기법 : 세타(N)
    • 오메가 표기법은 알고리즘 평균 실행 시간을 표기

 

대문자 O 표기법

  • 빅 오 표기법, Big-O 표기법 이라고도 부름
  • O(입력)
    • 입력 n에 따라 결정되는 시간 복잡도 함수
    • O(1), O(logn), O(n), O(nlogn), O(n^2), O(2^n), O(n!) 등으로 표기함
    • 입력 n의 크기에 따라 기하급수적으로 시간 복잡도는 늘어날 수 있음
    • O(1) < O(logn) < O(n) < O(nlogn) < O(n^2) < O(2^n) < O(n!)
  • 단순하게 입력 n에 따라 몇번 실행이 되는지를 계산하면 됨
    • 표현식에 가장 큰 영향을 미치는 n의 단위로 표기한다.
    • 상수회 : O(1)
    • n + 10 3n + 10 등 : O(n)
    • n^2 100n^2 + 100 등 : O(n^2)
  • ex) 만약 시간 복잡도 함수가 2n^2 + 3n -> 빅 오 표기법으로는 O(n^2)

 

4. 실제 알고리즘을 예로 각 알고리즘의 시간 복잡도와 빅 오 표기법 알아보기

  • 1부터 n까지의 합을 구하는 알고리즘1 
    • 시간 복잡도 : n 빅 오 표기법으로는 O(n)
public Integer sumAll(Integer n) {
	Integer total = 0;
    for (num = 1; num < n + 1; num++) {
    	total += num;
    }
    return total;
}
  • 1부터 n까지의 합을 구하는 알고리즘2
    • 시간 복잡도 : 1 빅 오 표기법으로는 O(1)
public Integer sumAll(Integer n) {
	return int(n * (n + 1) / 2)
}

 

'Algorithm > 자료구조' 카테고리의 다른 글

트리  (0) 2023.01.16
해쉬테이블  (0) 2023.01.03
링크드 리스트  (0) 2023.01.02
스택  (0) 2022.12.20
  (0) 2022.12.20

1. 링크드 리스트 (Linked List) 구조

- 연결 리스트라고도 함

- 배열은 순차적으로 연결된 공간에 데이터를 나열하는 데이터 구조

- 링크드 리스트는 떨어진 곳에 존재하는 데이터를 화살표로 연결해서 관리하는 데이터 구조

 

기본 구조와 용어

- 노드(Node) : 데이터 저장 단위 (데이터값, 포인터)로 구성

- 포인터(Pointer) : 각 노드 안에서, 다음이나 이전의 노드와의 연결 정보를 가지고 있는 공간

 

데이터 다음 데이터 주소 ---> 데이터 다음 데이터 주소 ---> 데이터 다음 데이터 주소

 

2. 간단한 링크드 리스트 예

- Node 구현

public class Node {
    private Integer data;
    public Node next;
    
    public Node(Integer data) {
    	this.data = data;
        this.next = null;
    }
    
    public Node(Integer data, Node next) {
    	this.data = data;
        this.next = next;
    }
    
    public Integer getData() {
        return this.data;
    }
}

 

 

- Node와 Node 연결하기 (포인터 활용)

Node node1 = new Node(1);
Node node2 = new Node(2);
node1.next = node2;
Node head = node1;

 

- 링크드 리스트로 데이터 추가하기

public void add(Integer data) {
    Node node = head;
    while (node.next != null) {
    	node = node.next;
    }
    node.next = new Node(data);
}

 

- 링크드 리스트 데이터 출력하기(검색하기)

Node node = head;
while (node.next != null) {
	System.out.println(node.getData());
    node = node.next;
}
System.out.println(node.getData());

 

3. 링크드 리스트의 장단점

- 장점

  • 미리 데이터 공간을 할당하지 않아도 됨 (배열은 미리 데이터 공간을 할당 해야 함)

- 단점

  • 연결을 위한 별도 데이터 공간이 필요하므로, 저장공간 효율이 높지 않음
  • 연결 정보를 찾는 시간이 필요하므로 접근 속도가 느림
  • 중간 데이터 삭제시, 앞뒤 데이터의 연결을 재구성해야 하는 부가적인 작업 필요

 

4. 링크드 리스트의 복잡한 기능1 (링크드 리스트 데이터 사이에 데이터를 추가)

- 링크드 리스트는 유지 관리에 부가적인 구현이 필요함

Node node1 = new Node(1);
Node node3 = new Node(3);
node1.next = node3;
Node head = node1;

Node node2 = new Node(2);
Node node = head;
Boolean search = true;

while (search) {
	if (node.getData() == 1) {
    	search = false;
    } else {
    	node = node.next;
    }
}

Node node_next = node.next;
node.next = node2;
node2.next = node_next;

 

5. 링크드 리스트의 복잡한 기능2 (특정 노드를 삭제)

- head 삭제

- 마지막 노드 삭제

- 중간 노드 삭제

 

6. 링크드 리스트 구현하기

public class Node {
    private Integer data;
    public Node next;
    
    public Node(Integer data) {
    	this.data = data;
        this.next = null;
    }
    
    public Node(Integer data, Node next) {
    	this.data = data;
        this.next = next;
    }
    
    public Integer getData() {
        return this.data;
    }
}

public class NodeMgmt() {
    private Node head;
	
	public void init(Integer data) {
    	head = new Node(data);
    }
	
	public void add(Integer data) {
    	if (head == null) {
        	head = new Node(data);
        } else {
        	Node node = head;
            while (node.next != null) {
            	node = node.next;
            }
            node.next = new Node(data);
        }
    }
    
    public void desc() {
    	Node node = head;
        while (node != null) {
        	System.out.println(node.getData());
            node = node.next;
        }
    }
    
    public void delete(Integer data) {
    	if (head == null) {
        	System.out.println("해당 값을 가진 노드가 없습니다.");
            return;
        }
        
        // 1. head 삭제
        if (head.getData() == data) {
            head = head.next;
        } else {
        	Node node = head;
            while (node.next != null) {
            	// 2. 중간, 마지막 노드 삭제
            	if (node.next.getData() == data) {
                	Node temp = node.next;
                    node.next = node.next.next; 
                    temp = null;
                    ruturn;
                } else {
                	node = node.next;
                }
            }
        }
    }
    
    public Node searchNode(Integer data) {
    	Node node = head;
        while (node != null) {
        	if (node.getData() == data) {
            	return node;
            } else {
            	node = node.next;
            }
        }
    }
}

 

7. 다양한 링크드 리스트 구조

더블 링크드 리스트 (Double Linked List) 기본 구조

- 이중 연결 리스트라고도 함

- 장점 : 양방향으로 연결되어 있어서 노드 탐색이 양쪽으로 모두 가능

이전 데이터 주소 데이터 다음 데이터 주소 --->
<---
이전 데이터 주소 데이터 다음 데이터 주소
public class Node {
    public Node prev;
    private Integer data;
    public Node next;
    
    public Node(Integer data) {
    	this.prev = null;
        this.data = data;
        this.next = null;
    }
}

public class  NodeMgmt() {
	private Node head;
    private Node tail;
    
	public void init(Integer data) {
    	head = new Node(data);
        tail = head;
    }
    
    public void insert(Integer data) {
    	if (head == null) {
        	head = new Node(data);
            tail = head;
        } else {
        	Node node = head;
        	while (node.next != null) {
            	node = node.next;
            }
            Node newNode = new Node(data);
            node.next = newNode;
            newNode.prev = node;
            tail = newNode;
        }
    }
	
    public void desc() {
    	Node node = head;
        whild (node != null) {
        	System.out.println(node.getData());
            node = node.next;
        }
    }
    
    public Node searchFromHead(Integer data) {
    	if (head == null) {
        	return null;
        }
    	Node node = head;
        while (node != null) {
        	if (node.getData() == data) {
            	return node;
            } else {
            	node = node.next;
            }
        }
        return null;
    }
    
    public Node searchFromTail(Integer data) {
    	if (head == null) {
        	return null;
        }
    	Node node = tail;
        while (node != null) {
        	if (node.getData() == data) {
            	return node;
            } else {
            	node = node.prev;
            }
        }
        return null;
    }
    
    // 노드 데이터가 특정 숫자인 노드 앞에 데이터를 추가하는 함수
    public Boolean insertBefore(Integer data) {
    	if (head == null) {
        	head = new Node(data);
            return true;
        } else {
        	Node node = tail;
            while (node.getData() != data) {
            	node = node.prev;
                if (node == null) {
                	return false;
                }
            }
            Node newNode = new Node(data);
            newNode.prev = node.prev;
            node.prev.next = newNode;
            newNode.prev = node.prev;
            newNode.next = node;
            node.prev = newNode;
            return true;
        }
    }
    
}

 

 

'Algorithm > 자료구조' 카테고리의 다른 글

해쉬테이블  (0) 2023.01.03
알고리즘 복잡도 표현 방법 : 시간 복잡도  (0) 2023.01.02
스택  (0) 2022.12.20
  (0) 2022.12.20
배열  (0) 2022.12.20

스택 (Stack)

- 데이터를 제한적으로 접근할 수 있는 구조

- 한쪽 끝에서만 자료를 넣거나 뺼 수 있는 구조

- 가장 나중에 쌓은 데이터를 가장 먼저 빼낼 수 있는 데이터 구조

- 큐: FIFO 정책

- 스택: LIFO 정책

 

1. 스택 구조

- 스택은 LIFO(Last-In, First-Out) 또는 FILO(First-In, Last-Out) 데이터 관리 방식을 따름

- 대표적인 스택의 활용: 컴퓨터 내부의 프로세스 구조의 함수 동작 방식

- 주요 기능

  • push(): 데이터를 스택에 넣기
  • pop(): 데이터를 스택에서 꺼내기

예) push(1) -> push(2) -> push(5) -> pop()

Stack
 
 
 
 
 
1

 

Stack
 
 
 
 
2
1

 

Stack
 
 
 
5
2
1
Stack
 
 
 
 
2
1

2. 스택 구조와 프로세스 스택

- 스택 구조는 프로세스 실행 구조의 가장 기본

- 함수 호출시 프로세스 실행 구조를 스택과 비교해서 이해 필요

 

3. 자료 구조 스택의 장단점

장점

- 구조가 단순해서 구현이 쉽다.

- 데이터 저장/읽기 속도가 빠르다.

단점 (일반적인 스택 구현시)

- 데이터 최대 갯수를 미리 정해야 한다.

- 저장 공간의 낭비가 발생할 수 있음, 미리 최대 갯수만큼 저장 공간을 확보해야 함

 

스택은 단순하고 빠른 성능을 위해 사용대므로, 보통 배열 구조를 활용해서 구현하는 것이 일반적임 이 경우 위의 단점이 있을 수 있음

 

4. JAVA 스택 클래스

import java.util.Stack;

Stack<Integer> integerStack = new Stack<>();
Stack<String> stringStack = new Stack<>();

 

값 추가, 삭제, 출력

Stack<Integer> stack = new Stack<>();

stack.push(1);		// stack에 값 1 추가
stack.push(2);		// stack에 값 2 추가

stack.pop();		// stack에 값 제거
stack.clear();		// stack에 전체 값 제거 (초기화)

stack.push(1);		// stack에 값 1 추가
stack.peek();		// stack의 가장 상단의 값 출력
stack.size();		// stackd의 크기 출력 : 1
stack.empty();		// stack이 비어있는지 체크 (비어있다면 true)
stack.contains(1);	// stack에 1이 있는지 체크 (있다면 true)

'Algorithm > 자료구조' 카테고리의 다른 글

해쉬테이블  (0) 2023.01.03
알고리즘 복잡도 표현 방법 : 시간 복잡도  (0) 2023.01.02
링크드 리스트  (0) 2023.01.02
  (0) 2022.12.20
배열  (0) 2022.12.20

큐 (Queue)

 

1. 큐 구조

- 줄을 서는 행위와 유사

- 가장 먼저 넣은 데이터를 가장 먼저 꺼낼 수 있는 구조

- 음식점에서 가장 먼저 줄을 선 사람이 제일 먼저 음식점에 입장하는 것과 동일

- FIFO(First-In, First-Out) 또는 LILO(Last-In, Last-Out) 방식으로 스택과 꺼내는 순서과 반대

 

Input               Output
          8 5 4  

 

Input               Output
            8 5 4

 

Input               Output
              8 5

 

Input               Output
                8

 

2. 알아둘 용어

Enqueue: 큐에 데이터를 넣는 기능

Dequeue: 큐에서 데이터를 꺼내는 기능

 

3. 파생 종류

LifoQueue (Last-In, First-Out)

PriorityQueue (우선순위 큐)

 

4. JAVA  큐 라이브러리

import java.util.LinkedList;
import java.util.Queue;
import java.util.PriorityQueue;
import java.util.Collections;

Queue<Integer> integerQueue = new LinkedList<>();
Queue<String> stringQueue = new LinkedList<>();

// 낮은 숫자 우선 순위 큐
PriorityQueue<Integer> priorityQueueLowest = new PriorityQueue<>();

// 높은 숫자 우선 순위 큐
PriorityQueue<Integer> priorityQueueHighest = new PriorityQueue<>(Collections.reverseOrder());

 

값 추가, 삭제, 출력

Queue<Integer> stack = new LinkedList<>();

queue.add(1);		// queue에 값 1 추가
queue.offer(2);		// queue에 값 2 추가

queue.poll();       	// queue에 첫번째 값을 반환하고 제거 비어있다면 null
queue.remove();		// queue에 첫번째 값 제거
queue.clear();		// queue 초기화

queue.offer(1);		// queue에 값 1 추가
queue.offer(2);		// queue에 값 2 추가
queue.peek();		// queue의 첫번째 값 출력
queue.size();		// queue의 크기 출력

 

어디에 큐가 많이 쓰일까?

멀티 태스킹을 위한 프로세스 스케쥴링 방식을 구현하기 위해 많이 사용됨

큐의 경우에는 장단점 보다는 (특별히 언급되는 장단점이 없음), 큐의 활용 예로 프로세스 스케쥴링 방식을 함께 이해해두는 것이 좋음

 

'Algorithm > 자료구조' 카테고리의 다른 글

해쉬테이블  (0) 2023.01.03
알고리즘 복잡도 표현 방법 : 시간 복잡도  (0) 2023.01.02
링크드 리스트  (0) 2023.01.02
스택  (0) 2022.12.20
배열  (0) 2022.12.20

꼭 알아둬야 할 자료구조: 배열 (Array)

- 데이터를 나열하고, 각 데이터를 인덱스에 대응하도록 구성한 데이터 구조

- 파이썬에서는 리스트 타입이 배열 기능을 제공하고 있음

 

1. 배열이 왜 필요할까?

- 같은 종류의 데이터를 효율적으로 관리하기 위해 사용

- 같은 종류의 데이터를 순차적으로 저장

 

데이터 S T R I N G
인덱스(index) 0 1 2 3 4 5

 

배열의 장점:

- 빠른 접근 가능

배열의 단점:

- 추가/삭제가 쉽지 않음

 

2. 파이썬과 C 언어의 배열 예제

#include <stdio.h>

int main(int argc, char * argv[]) {
	char country[3] = "US";
    printf("%c%c\n", country[0], country[1]);
    printf("%s\n", country);
    return 0;
}
country = 'US'
print (country)

'Algorithm > 자료구조' 카테고리의 다른 글

해쉬테이블  (0) 2023.01.03
알고리즘 복잡도 표현 방법 : 시간 복잡도  (0) 2023.01.02
링크드 리스트  (0) 2023.01.02
스택  (0) 2022.12.20
  (0) 2022.12.20

Brute-force : 무차별 대입

가능한 경우의 수를 모두 찾아서 답을 찾는 알고리즘

컴퓨터의 빠른 계산 속도를 이용하는 방법

 

완전탐색 방법

1. Brute Force : for문과 if문을 이용해 처음부터 끝까지 탐색하는 방법

2. 비트마스크

3. 순열

4. 백트래킹

5. BFS

6. DFS

7. 재귀함수

 

관련문제

재귀, 순열

  - 프로그래머스 완전탐색 소수찾기 programmers.co.kr/learn/courses/30/lessons/42839

중첩 for문, 재귀

  - https://www.acmicpc.net/problem/2309 백준 일곱 난쟁이
  - https://www.acmicpc.net/problem/7568 백준 덩치
  - https://www.acmicpc.net/problem/6603 백준 로또
  - https://www.acmicpc.net/problem/1065 백준 한수
  - https://www.acmicpc.net/problem/1107 백준 리모컨

 

1. 선언

1
2
3
4
5
6
7
import java.util.PriorityQueue; //import
 
//int형 priorityQueue 선언 (최소힙)
PriorityQueue<Integer> priorityQueue = new PriorityQueue<>();
 
//int형 priorityQueue 선언 (최대힙)
PriorityQueue<Integer> priorityQueue = new PriorityQueue<>(Collections.reverseOrder());
cs

2. 값 추가

1
priorityQueue.offer(3);
cs

3. 값 삭제

1
priorityQueue.poll(); // priorityQueue에 첫번째 값을 반환하고 제거 비어있다면 null
cs

4. 값 출력

1
priorityQueue.peek(); //Priority Queue에서 우선순위가 가장 높은 값 출력
cs

'Algorithm > PROGRAMMERS' 카테고리의 다른 글

[완전탐색] 완전탐색이란  (0) 2020.11.24
[힙(Hesp)] 더 맵게 JAVA  (0) 2020.11.10
[해시] HashMap 자주 쓰는 함수 정리  (0) 2020.11.08
[해시] 완주하지 못한 선수 JAVA  (0) 2020.11.08

처음에 배열로 시도를 했으나 효율성 테스트에서 통과하지 못하고

PriorityQueue를 사용해서 통과했다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import java.util.*;
 
class Solution {
    public int solution(int[] scoville, int K) {
        int count = 0//섞은 횟수
        PriorityQueue<Integer> minHeap = new PriorityQueue<>(); //최소힙
        
        for(int s : scoville) {
            minHeap.offer(s);
        }
        
        while(minHeap.peek() < K) {
            if(minHeap.size() < 2) { //스코빌 지수가 K보다 작은데 원소가 하나일 경우
                return -1;
            }
            count++;
            minHeap.offer(minHeap.poll() + (minHeap.poll() * 2));
        }
       
        return count;
    }
    
}
cs

 

+ Recent posts