240806~240807
1. stack의 특성
- 박스처럼 자료를 순서대로 쌓는 방식(박스, 접시를 생각하자!)
- 스택에 저장된 자료는 선형 구조를 갖는다
- 선형 구조 : 자료 간 관계가 1대 1의 관계
- 비선형구조 : 자료 간의 관곅 1대 N의 관계 ex. 트리
- 스택에 자료를 삽입하거나 자료를 꺼낼 수 있다
- 마지막에 삽입한 자료를 가장 먼저 꺼낸다(후입선출, LIFO)
- 마지막 삽입된 원소의 위치를 top이라 부름
- 연산 : 삽입(push), 삭제(pop)
stack = []
#append : 추천하지 않는 방식 -> 시간이 O(1)로 빠르지만 동적으로 메모리 할당하다보니 시간이 오래 걸림
#stack : 아예 사이즈를 잡아놓고 시작을 권유함
stack.append(1) #push(1)
stack.append(2) #push(2)
stack.append(3) #psuh(3)
print(stack.pop())
print(stack.pop())
print(stack.pop())
#3
#2
#1
#포인터 방식 : 시간이 훨씬 빠름
stack_size =10
stack = [0] * stack_size
top = -1
top += 1 # push(1)
stack[top] = 1
top += 1 # push(2)
stack[top] = 2
top += 1 # push(3)
stack[top] = 3
top -= 1
print(stack[top+1])
print(stack[top])
top -= 1
print(stack[top])
top -= 1 #이거 없어도 1 잘 나옴 내가 생각한게 맞음0
# 3 2 1
- overflow(스택길이가 부족하다), underflow(스택이 비어있다) 개념
def my_pop():
if len(s) == 0:
#underflow
return
else: return s.pop()
#스택 응용 : function call
def f2(c,d):
return c-d
def f1(a,b):
c = a+b
d = 10
return f2(c,d)
#a,b먼저 선언
a = 10
b = 20
print(f1(a,b))
2. 재귀호출 💖(A형 취득에 중요)
- 필요한 함수가 자신과 같은 경우 자신을 다시 호출하는 구조
- 종료조건/ 유도조건(문제를 더 작은 부분으로) 으로 나뉨
- 그럼 프로그램의 크기를 줄이고 간단하게 작성이 가능함
-ex. factorial : 1부터 n까지의 모든 자연수를 곱함
-ex. 피보나치 : 0과 1로 시작하고, 이전의 두 수합을 다음 항으로 하는 수열
def fact(n):
if n == 1:
return 1
return n * fact(n-1)
print(fact(5))
#몇번 호출하는가
def f(n):
global cnt
cnt += 1
if n == 0:
return
else:
f(n-1)
cnt = 0
f(5)
print(cnt) #6
#크기 N인 배열 arr[i]에 접근
def f(i,N): # 현재 인덱스 i, N 크기
if i == N:
return
else:
print(arr[i])
f(i+1,N)
return #함수의 끝 구분위해 return #끝 retuen은 안써도 되긴함
arr = [1,2,3]
N = 3
print(f(0,N))
3.Memoization 메모이제이션 - top down
- 앞 예시에서 피보나치 수를 구하는 함수를 재귀함수로 구현시 엄청난 중복 호출이 존재한다
- 피보나치 수 : 첫째 및 둘째 항이 1이며 그 뒤의 모든 항은 바로 앞 두 항의 합인 수열
- 메모이제이션은 컴퓨터 프로그램 실행할 때 이전에 계산한 값을 메모리에 저장해서, 매번 다시 계산하지 않도록 하여 전체적 실행속도를 빠르게 하는 기술.
def fibo1(n): #훨씬 빠르게 수행
if n >= 2 and memo[n] == 0: #fibo1(n)이 계산된 적 없으면
memo[n] = fibo1(n-1) + fibo1(n-2) #아래 실행
return memo[n]
def fibo(n):
if n< 2:
return n
else:
return fibo(n-1) + fibo(n-2)
n = 7
memo = [0] * (n+1) #[0,1,1,2,3,5,8,13]
memo[0] = 0
memo[1] = 1
print(fibo1(n))
print(fibo(n))
print(memo)
4. 동적계획법(Dynamic Programming) - bottom up
- 그리디 알고리즘과 같이 최적화 문제 해결하는 알고리즘
- 큰 문제를 작은 문제로 나눌 수 있고, 작은 문제에서 구한 정답은 그것을 포함하는 큰 문제에서도 동일할 때 적용 가능
- ex. 피보나치(문제를 부분문제로 분할 가능)
- 메모이제이션을 재귀적 구조에 사용하는 것보다 반복적 구조로 DP를 구현하는 것이 성능면에서 효율적
-> 재귀적 구조는 내부에 시스템 호출 스택을 사용하는 오버헤드 발생
5. DFS(깊이우선탐색)
1) 그래프란?
- 비선형적(아침드라마의 인간관계 같은..연결관계 ^0^), 정점과(Node,Vertex) 간선(Edge, Link)으로 이루어진 자료구조
- 그래프 -> 컴퓨터 폴더 구조
- 그래프의 표현 :
-- 인접행렬(Adjacency Matrix) : 연결관계를 행렬로 나타냄, 연결되지 않은 노드들에 대한 정보 저장(메모리 과다)
-- 인접 리스트 : 각 노드별로 인접한 모든 노드를 리스트로 나타냄, 인접행렬에 비해 병렬처리가 어려움 O(N + E)
- 그래프 구조는 그래프로 표현된 모든 자료를 빠짐없이 검색하는 것이 중요함
-> 두가지 방법 : DFS(깊이 우선 탐색) / BFS(너비 우선 탐색)
2) DFS :
- 왜 쓰일까? : 경로 존재 확인(미로확인), 백트래킹(특정 조건 만족하는 모든 해 찾기)
- 구현방법 : 후입선출 방식으로 처리하는 자료 구조인 재귀 함수, 스택
-> 갈림길로 가다가, 더 이상 탐색할 길이 없을 때 가장 최근 갈림길로 돌아가, 다시 깊이 우선 탐색 반복해야하므로 후입선출 구조 스택 사용 ❤
V, E = 7, 8 #노드수, 간선 수
edges = list(map(int, "1 2 1 3 2 4 2 5 4 6 5 6 6 7 3 7".split())) #연결된 엣지정보
print(edges)
#[1, 2, 1, 3, 2, 4, 2, 5, 4, 6, 5, 6, 6, 7, 3, 7]
# 인접 리스트
# 세가지 방법으로 구현 가능
graph = [[] for _ in range(V+1)] #엣지에 0이 없어서 인덱스 0은 안씀
graph = {i+1:[] for i in range(V)}
from collections import defaultdict
graph = defaultdict(list)
for i in range(E):
v1,v2 = edges[2*i], edges[2*i+1] #edge 2개가 한 묶음 #1 2 #1 3
#무향 그래프(양방향으로 이동가능)
graph[v1].append(v2)
graph[v2].append(v1) #이 코드 없으면 유향그래프(단방향)
print(graph)
# for key,val in graph.items():
# graph[key] = sorted(val) #재귀방식으로 작은 번호 순으로 방문
# graph[key] = sorted(val, reverse=True) #스택 방식에서 작은 번호 순으로 방문
#[[], [2, 3], [1, 4, 5], [1, 7], [2, 6], [2, 6], [4, 5, 7], [6, 3]]
def dfs_recursive(cur_node):
# 방문한 노드 처리 로직
visited[cur_node] = 1
print(cur_node, end=' ')
# 현재 노드와 인접한 다음 노드 탐색
for next_node in graph[cur_node]:
#방문하지 않은 것만 탐색
if not visited[next_node]:
dfs_recursive(next_node)
visited = [0] * (V+1) ##??
dfs_recursive(1)
def dfs_stack(start_node):
stack = [start_node]
while stack : # 스택에 남은 것이 없을 때까지 반복
cur_node = stack.pop() #현재 방문 노드
#스택 중간 요소들을 빼주는 코드 #스택 : 마지막 요소를 고려
#방문한 적이 있는 경우 continue -> 다음 while문으로 이어짐
#최적화
if visited[cur_node] :
continue
#방문처리
visited[cur_node] = 1
print(cur_node,end=' ')
#현재 노드와 연결된 노드 중 방문하지 않은 노드만 스택에 추가
for next_node in graph[cur_node]:
# for next_node in sorted(graph[cur_node], reverse=True):
if not visited[next_node]:
stack.append(next_node)
print()
visited = [0] * (V+1) #노드 번호가 0부터 시작안하는 케이스라서
dfs_stack(1)
'SSAFY' 카테고리의 다른 글
[수업기록] 알고리즘 : stack(3) - 백트래킹(2) (0) | 2024.08.09 |
---|---|
[수업기록] 알고리즘 : stack(2)-문자열 계산식, 백트래킹 (0) | 2024.08.08 |
[수업기록] 알고리즘 : string (0) | 2024.08.02 |
[수업기록] 알고리즘 : list(검색, 선택 정렬) (0) | 2024.08.01 |
[수업기록] 알고리즘 : list(배열, 정렬, 순열-조합, 완전탐색, 그리디, 행렬) (0) | 2024.07.31 |