Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |
Tags
- JavaScript
- 4기
- 모션비트
- 자바스크립트
- 코드트리
- HTML
- 크래프톤정글
- 티스토리챌린지
- TiL
- userprog
- 핀토스
- CSS
- 크래프톤 정글
- 자바
- 시스템콜
- 리액트
- 사이드프로젝트
- pintos
- Flutter
- Vue.js
- 스택
- 나만무
- corou
- 백준
- 알고리즘
- 오블완
- Java
- 소켓
- defee
- 큐
Archives
- Today
- Total
미새문지
크래프톤 정글 week03, day21 - 동적 계획법(Dynamic Programming), 연결리스트(Linked-List), 포인터(Pointer) 본문
크래프톤 정글/TIL
크래프톤 정글 week03, day21 - 동적 계획법(Dynamic Programming), 연결리스트(Linked-List), 포인터(Pointer)
문미새 2024. 2. 20. 00:57728x90
동적 계획법(Dynamic Programming)
- 복잡한 문제를 간단한 여러 개의 하위 문제로 분해하여 해결하는 알고리즘 설계 기법
- 큰 문제를 작은 문제로 나누어 해결하는 분할 정복과 비슷하지만, 다이나믹 프로그래밍은 각 하위 문제들이 서로 연관되어 있고, 작은 문제들의 해결을 통해 전체 문제를 해결한다.
- 동일한 하위 문제를 반복적으로 해결해야 할 경우, 해결 결과를 메모리에 저장해두고 이를 재사용함으로써 중복 계산을 피해 연산 시간을 단축시키는 것을 메모이제이션(Memoization)이라고 한다.
- 사용을 위한 두 가지 조건
- 중복 하위 문제 구조(Overlapping Subproblems)
- 큰 문제와 작은 문제를 같은 방법으로 해결할 수 있으며, 작은 문제의 답을 메모리에 저장하고 필요할 때마다 활용한다.
- 최적 부분 구조(Optimal Substructure)
- 문제의 최적의 해가 부분 문제의 최적의 해로부터 구성될 수 있어야 한다.
- 예시로 피보나치 수열, 0-1 배낭 문제, LCS 등이 있다.
- 중복 하위 문제 구조(Overlapping Subproblems)
- 예시)
- 피보나치 문제(재귀 방식)
def fibonacci(n):
if n <= 1:
return n
else:
return(fibonacci(n-1) + fibonacci(n-2))
- 피보나치 문제는 보통 재귀 형식으로 사용해서 구현을 하지만, 이 경우 입력값이 커지면 계산 시간이 너무 늘어나게 되어 다이나믹 프로그래밍을 이용해 효율적으로 짜는게 좋다
- 피보나치 문졔(DP 방식)
def fibonacci(n):
fib = [0, 1] + [0] * (n-1)
for i in range(2, n+1):
fib[i] = fib[i-1] + fib[i-2]
return fib[n]
- DP의 경우엔 리스트에 각 값을 저장해 재활용할 수 있어서 중복 계산을 방지하고 계산 시간을 줄일 수 있다. 이것이 DP의 핵심이다.
연결 리스트(Linked-List)
- 각 노드가 데이터와 포인터를 가지고 한 줄로 연결되어 있는 방식으로 데이터를 저장하는 자료 구조이다.
- 포인터는 다음 노드와의 연결을 담당
- 연결 리스트의 특징
- 동적 크기
- 연결 리스트는 노드를 동적으로 할당해 크기를 조절할 수 있다. 이로 인해 필요한 만큼만 메모리를 사용하게 된다.
- 삽입 및 삭제 용이
- 데이터의 삽입과 삭제가 배열에 비해 용이하다.
- 배열의 경우 데이터 삽입 및 삭제 시 해당 위치 뒤의 모든 데이터를 이동시켜야 하지만, 연결 리스트는 추가하려는 위치의 앞 노드와 뒤 노드를 연결하거나 끊는 것으로 간단히 해결이 가능하다.
- 동적 크기
- 단점
- 접근 속도
- 연결 리스트는 배열과 달리 인덱스를 통한 접근이 불가능하며, 특정 위치의 데이터를 찾기 위해선 처음부터 순차적으로 접근해야 한다.
- 메모리 사용
- 각 노드가 데이터 외에도 포인터를 저장하므로 추가적인 메모리를 사용하게 된다.
- 접근 속도
- c언어에서 연결 리스트를 구현하려면, struct를 사용하여 노드를 정의하고, malloc() 함수를 사용해 동적으로 노드를 할당 받아야 한다.
- 또한 노드를 연결하거나 삭제할 때 포인터를 직접 조작해야 한다. 이로 인해 c언어에서는 연결 리스트의 구현이 다른 언어에 비해 복잡하고 오류가 발생하기 쉽다.
- 리스트와 연결 리스트의 차이
차이점
|
리스트
|
연결 리스트
|
메모리 구조
|
배열 기반의 구조를 가지고 있어, 데이터들이 메모리 상에서 연속적으로 저장
|
각 노드가 데이터와 다음 노드를 가리키는 포인터를 가지고 있으며, 이 노드들이 연결되어 있는 구조. 즉, 메모리 상에서 불연속적으로 위치할 수 있다.
|
데이터 접근
|
인덱스를 통해 O(1)의 시간 복잡도로 데이터에 접근
|
특정 위치의 데이터에 접근하려면 처음부터 순차적으로 접근해야 하므로 O(n)의 시간 복잡도를 가진다.
|
데이터 삽입 및 삭제
|
데이터를 삽입하거나 삭제할 때는 해당 위치 이후의 모든 데이터를 이동시켜야 하므로 시간 복잡도가 O(n)이다.
|
데이터를 삽입하거나 삭제할 때 해당 노드의 포인터만 변경하면 되서, 찾는데 걸리는 시간을 제외하면 O(1)의 시간 복잡도를 가진다.
|
메모리 할당
|
크기 변경이 빈번하게 일어나면 메모리 재할당과 데이터 복사로 인한 비효율이 발생할 수 있다.
|
필요할 때마다 노드를 동적으로 생성하여 메모리를 할당하므로 이러한 문제가 없다.
|
포인터
- 메모리의 주소를 저장하는 변수
- 어떤 값이 저장된 메모리 위치를 가리키는 역할
- 포인터를 선언할 때는 자료형 뒤에 별표(*)를 붙여서 선언한다.
- 예를 들어 int *p; 라는 선언은 p가 정수형 값을 가진 메모리의 주소를 저장하는 포인터임을 의미한다.
- 포인터를 사용하면 메모리의 특정 위치에 직접 접근이 가능해, 배열이나 함수, 동적 메모리 할당 등 다양한 곳에서 활용된다.
- 포인터를 사용하는 이유
- 동적 메모리 할당
- malloc(), calloc() 등의 함수를 사용하여 런타임 중에 메모리를 동적으로 할당하고, 포인터를 통해 이 메모리에 접근할 수 있다.
- 배열과 문자열
- 배열 이름은 배열의 첫 번째 원소를 가리키는 포인터로 사용되며, 문자열은 문자 배열의 포인터로 표현된다.
- 함수의 인자 전달
- 포인터를 통해 함수의 인자를 전달하면, 원래 변수가 저장된 메모리 위치를 직접 접근해 값을 변경할 수 있다. 이를 통해 함수에서 여러 개의 값을 반환하거나, 큰 데이터 구조를 효율적으로 전달 가능하다.
- 동적 메모리 할당
학습 시간 : 14시 ~ 25시
728x90
'크래프톤 정글 > TIL' 카테고리의 다른 글
크래프톤 정글 week03, day23 - 퀴즈 복습, 알고리즘 문제 (1) | 2024.02.20 |
---|---|
크래프톤 정글 week03, day22 - 알고리즘 문제 (1) | 2024.02.20 |
크래프톤 정글 week03, day20 - 어셈블리어, 오퍼랜드, 점프 인스트럭션, 프로시저 (1) | 2024.02.20 |
크래프톤 정글 week03, day19 - LCS 문자열 방식, 부분 수열 방식 (1) | 2024.02.20 |
크래프톤 정글 week03, day18 - 그리디 알고리즘, 분할 정복 (1) | 2024.02.20 |