프로그래머스 인공지능 데브코스 첫 날
꼭 참여하고 싶었고 어렵게 함께하게 된 만큼 열심히 할 생각이었는데, 첫 날부터 학습량이 만만치가 않다.
하지만 내가 개발자가 되기위해 공부한 기간이 얼마안되기 때문에 어려운 것이 당연하고 같이 참여하는 동기들보다 많이 부족하다고 스스로 느끼기 때문에 남들보다 두세배 더 열심히 해볼 생각이다.
이 공간에는 앞으로 그날 그날 배운 것들 중 내가 알고는 있었지만 정확하지 않았거나 배우면서 어렵다고 느끼는 것들 그리고 새롭게 알게된 것들을을 기록하면서 복습하고 정리하겠다.
자료구조(Data Structure)란 컴퓨터 과학에서 효율적인 접근 및 수정을 가능케하는 자료의 조직, 관리, 저장을 의미한다. 자세히는 데이터 값의 모임 또는 관계, 그리고 데이터에 적용가능한 함수나 명령으로 적절한 자료구조의 선택은 효율적인 알고리즘을 사용 할 수 있게한다.
< 위키피디아 참고>
수학과 컴퓨터 과학, 언어학 또는 관련 분야에서 어떠한 문제를 해결하기 위해 정해진 일련의 절차나 방법을 공식화한 형태로 표현한 것, 계산을 실행하기 위한 단계적 절차를 의미한다.
< 위키피디아 참고>
자료구조와 알고리즘의 차이가 딱 나누어 떨어지지않아 둘의 의미를 좀 혼동했다. 찾아보니 다른 사람들도 많이 둘의 구분에대해 어려워하는 것 같은데, 내가 이해한대로라면 자료구조는 데이터가 저장되는 형태 또는 표현을 의미하고 알고리즘은 그러한 자료구조를 어떤 순서, 규칙으로 어떻게 처리할 것인가를 말한다.
결국 자료구조와 알고리즘은 모두 문제를 효과적으로 해결하기위함이 목적이고 문제유형에 따라 적절한 자료구조와 알고리즘은 다르다. 여기서 효과적이란 시간(실행속도)과 공간(메모리) 자원을 최소한으로 사용하는 것을 의미한다.
L2 = sorted(L, reverse = True)
// 리스트 L을 내림차순으로 정렬, 리스트 자체가 변경되지 않으므로 별도 변수에 할당필요
L.sort(reverse = True)
// 마찬가지로 리스트 L을 내림차순으로 정렬하는데 위와 다르게 리스트 L자체가 변경된다
L = ['abcd', 'xyz', 'spam']
sorted(L, lambda x: len(x))
결과 : ['xyz','abcd', 'spam']
L=[{'name' : 'John', 'score': 82}, {'name' : 'Paul', 'score': 92}]
L.sort(key = lambda x: x['name'], reverse =True)
// name의 알파벳 내림차순 순서로 정렬됨
결과 [{'name': 'Paul', 'score': 92}, {'name': 'John', 'score': 82}]
L.sort(key = lambda x: x['score'], reverse=True)
// score 점수가 내림차순 순으로 정렬됨
결과 [{'name': 'Paul', 'score': 92}, {'name': 'John', 'score': 82}]
위에서 lamba 함수를 사용했는데 이는 함수를 간편하게 정의하는 방법이다. 단, def 함수와 같이 정의해서 계속사용하는 것이 아니라 일시적 사용을 위한 함수다.
간단한 예시를 들면
result = lambda a,b : a+b
print(result(4,5))
// 9
map() 함수와도 함께 사용가능하다.
마찬가지로 예시를 들면
L1 = [1,2,3]
L2 = [4,5,6]
result = list(map(lambda a,b : a+b, L1,L2)) ## 리스트 L1,L2의 원소를 받아 lambda함수 적용
print(result)
// [5,6,9]
def solution(L, x):
answer = 0
lower = 0 ## 탐색시작 인덱스
upper = len(L) - 1 ## 탐색종료 인덱스
if x not in L:
return -1
while lower <= upper:
middle = (lower + upper) // 2
## x가 중앙값일 경우 그 값의 인덱스 리턴
if L[middle] == x:
return L.index(x)
## x가 L의 중앙값보다 큰 경우 : 탐색시작 인덱스를 middle값으로
elif L[middle] < x:
lower = middle
## x가 L의 중앙값보다 작은 경우 : 탐색종료 인덱스를 middle값으로
else:
upper = middle
재귀 알고리즘 예시 (피보나치 수열)
def solution(x):
answer = 0
if x <= 1:
answer = x
else:
answer = solution(x-1) + solution(x-2)
return answer
여기서부터는 내가 처음 접하는 내용들이 대부분이라 이해하기가 점점 힘들기 시작했다.
매일매일의 학습량이 방대해 따라가기도 벅차지만 틈틈히 아래 내용들은 따로 찾아보고 공부해서
계속해서 내용을 보완하겠다.
연결리스트의 연산에는 다음과 같이 6가지가 있다.
- 임의의 K 번째 원소 참조
- 리스트 순회(traverse)
- 전체 리스트 길이얻어내기
- 리스트의 원소추가
- 리스트의 원소삭제
- 두 리스트 합치기
## Node라는 명의 클래스 정의
class Node:
## item이라는 인자를 받는 초기화 함수
def __init__(self, item):
## 객체생성 : 변수data를 item으로 초기화, 변수 next를 None으로 초기화
self.data = item
self.next = None
## LinkedList 클래스 정의
class LinkedList:
def __init__(self):
## 연결리스트 개수 초기화
self.nodeCount = 0
## 처음, 마지막 node 초기화
self.head = None
self.tail = None
## 노드 생성,초기화
class Node:
def __init__(self, item):
self.data = item
self.prev = None
self.next = None
## 양방향 연결리스트 생성, head와 tail에 None으로 초기화해준 것을 볼 수 있다.
class DoublyLinkedList:
def __init__(self):
self.nodeCount = 0 ## dummy node는 빼고센다
self.head = Node(None)
self.tail = Node(None)
self.head.prev = None
self.head.next = self.tail
self.tail.prev = self.head
self.tail.next = None
## 역순회
def reverse(self):
result = []
curr = self.tail
while curr.prev.prev:
curr = curr.prev
result.append(curr.data)
return result
## 원소 탐색, 인자를 위치 pos로 받았다.
def getAt(self, pos):
if pos < 0 or pos > self.nodeCount:
return None
## 탐색속도를위해 중간위치에서 왼쪽인지 오른쪽인지 확인
## 중간보다 뒤에있으면 tail에서부터 탐색
if pos > self.nodeCount // 2:
i = 0
curr = self.tail
while i < self.nodeCount - pos + 1:
curr = curr.prev
i += 1
## 중간 앞에있으면 head에서부터 탐색
else:
i = 0
curr = self.head
while i < pos:
curr = curr.next
i += 1
return curr
## 원소 삽입, 이전 node(prev)와 새로운 node를 인자로 받는다.
def insertAfter(self, prev, newNode):
next = prev.next
newNode.prev = prev
newNode.next = next
prev.next = newNode
next.prev = newNode
self.nodeCount += 1
return True
## 또다른 방법, 위치 pos를 인자로 받는다.
def insertAt(self, pos, newNode):
if pos < 1 or pos > self.nodeCount + 1:
return False
prev = self.getAt(pos - 1)
return self.insertAfter(prev, newNode)
## 다음 node를 인자로 받아 삽입도 가능하다.
def insertBefore(self, next, newNode):
prev = next.prev
newNode.prev = prev
newNode.next = next
prev.next = newNode
next.prev = newNode
self.nodeCount += 1
return True
## 이전 node를 인자로 받아 원소삭제
def popAfter(self, prev):
curr = prev.next
prev.next = curr.next
curr.next.prev = prev
curr.next = None
curr.prev = None
self.nodeCount -= 1 ## 요거 빼억지말자!
return curr.data
## 다음 node를 인자로 받아 원소삭제
def popBefore(self, next):
curr = next.prev
next.prev = curr.prev
curr.prev.next = next
curr.prev = None
curr.next = None
self.nodeCount -= 1
## 양방향 리스트의 결합, 결합할 양방향 리스트를 인자로받는다.
def concat(self, L):
if self.nodeCount != 1 or L.nodeCount != 0:
self.tail.prev.next = L.head.next
L.head.next.prev = self.tail.prev
self.tail = L.tail
self.nodeCount += L.nodeCount ## dummy인 head, tail은 제외한 값이다.
def solution(x):
return 0
제공하는 연산자
size() : 스택의 사이즈(원소의 개수)를 구함
isEmpty() : 스택이 비어있는지 판단, 비어있으면 self.size() == 0을 반환
push(x) : 원소 x를 스택에 추가
pop() : 가장 나중에 저장된 원소 제거 및 반환 self.data.pop(-1)
peek() : 가장 나중에 저장된 원소를 반환, 제거하지 않음
제공하는 연산자
size() : 큐의 사이즈(원소의 개수)를 구함
isEmpty() : 큐가 비어있는지 판단, 비어있으면 self.size() == 0을 반환
enqueue(x) : 원소 x를 큐에 추가
dequeue() : 가장 나중에 저장된 원소 제거 및 반환 self.data.pop(0)
peek() : 가장 나중에 저장된 원소를 반환, 제거하지 않음
class Node:
def __init__(self, item):
self.data = item
self.prev = None
self.next = None
class DoublyLinkedList:
def __init__(self):
self.nodeCount = 0
self.head = Node(None)
self.tail = Node(None)
self.head.prev = None
self.head.next = self.tail
self.tail.prev = self.head
self.tail.next = None
def __repr__(self):
if self.nodeCount == 0:
return 'LinkedList: empty'
s = ''
curr = self.head
while curr.next.next:
curr = curr.next
s += repr(curr.data)
if curr.next.next is not None:
s += ' -> '
return s
def getLength(self):
return self.nodeCount
def traverse(self):
result = []
curr = self.head
while curr.next.next:
curr = curr.next
result.append(curr.data)
return result
def reverse(self):
result = []
curr = self.tail
while curr.prev.prev:
curr = curr.prev
result.append(curr.data)
return result
def getAt(self, pos):
if pos < 0 or pos > self.nodeCount:
return None
if pos > self.nodeCount // 2:
i = 0
curr = self.tail
while i < self.nodeCount - pos + 1:
curr = curr.prev
i += 1
else:
i = 0
curr = self.head
while i < pos:
curr = curr.next
i += 1
return curr
def insertAfter(self, prev, newNode):
next = prev.next
newNode.prev = prev
newNode.next = next
prev.next = newNode
next.prev = newNode
self.nodeCount += 1
return True
def insertAt(self, pos, newNode):
if pos < 1 or pos > self.nodeCount + 1:
return False
prev = self.getAt(pos - 1)
return self.insertAfter(prev, newNode)
def popAfter(self, prev):
curr = prev.next
next = curr.next
prev.next = next
next.prev = prev
self.nodeCount -= 1
return curr.data
def popAt(self, pos):
if pos < 1 or pos > self.nodeCount:
raise IndexError('Index out of range')
prev = self.getAt(pos - 1)
return self.popAfter(prev)
def concat(self, L):
self.tail.prev.next = L.head.next
L.head.next.prev = self.tail.prev
self.tail = L.tail
self.nodeCount += L.nodeCount
class LinkedListQueue:
def __init__(self):
self.data = DoublyLinkedList()
def size(self):
return
self.data.getLength()
def isEmpty(self):
return
self.data.getLength() == 0
def enqueue(self, item):
node = Node(item)
## 아래 둘다 가능
## insertAt은 인자로 pos와 newNode를 받음
self.data.insertAt(self.data.getLength()+1, node)
## insertAfter는 인자로 prev와 newNode를 받음
self.data.insertAfter(self.data.tail.prev, node)
def dequeue(self):
return
self.data.popAt(1)
def peek(self):
return
self.data.getAt(1).data
def solution(x):
return 0