문과 vs 이과
문과 : 일단 외우고 나서, 이해하기 시작
이과 : 일단 원리를 파악하고 나서, 원리를 적용해가며 이해하기 시작
- 프로그래밍은 작은 원리를 적용하는 방법을 익히고, 연습을 통해 익숙해져야함
자료 구조
용어 : 자료구조, 데이터구조, data structure
대량의 데이터를 효율적으로 관리할 수 있는 데이터의 구조를 의미
코드 상에서 효율적으로 데이터를 처리하기 위해, 데이터 특성에 따라, 체계적으로 데이터를 구조화해야함.
어떤 데이터 구조를 사용하느냐에 따라, 코드 효율이 달라짐
대표적인 자료구조 : 배열, 스택, 큐, 링크드 리스트, 해쉬 테이블, 힙...
알고리즘 : 어떤 문제를 풀기 위한 절차 / 방법
어떤 문제에 대해, 특정한 '입력'을 넣으면 원하는 '출력'을 얻을 수 있도록 만드는 프로그래밍
1. 배열은 왜 필요할까?
#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)
2. 파이썬에서의 배열
1차원 배열: 리스트로 구현시
data_list = [1, 2, 3, 4, 5]
data_list
2차원 배열: 리스트로 구현시
data_list = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
data_list
2차원 배열 => 대괄호가 2개
유사한 데이터를 연결된 데이터 공간에 넣을 수 있는 것 : 배열
최대 길이를 알지 못하면 추가/ 수정이 어렵다.
1. 큐 구조
2. 알아둘 용어
- Enqueue: 큐에 데이터를 넣는 기능
- Dequeue: 큐에서 데이터를 꺼내는 기능
3. 파이썬 queue 라이브러리 활용해서 큐 자료 구조 사용하기
일반적인 큐 외에 다양한 정책이 적용된 큐들이 있음
3.1. Queue()로 큐 만들기 (가장 일반적인 큐, FIFO(First-In, First-Out))
import queue
data_queue = queue.Queue()
3.2. LifoQueue()로 큐 만들기 (LIFO(Last-In, First-Out))
import queue
data_queue = queue.LifoQueue()
3.3. PriorityQueue()로 큐 만들기 : 데이터와 우선순위를 같이 넣어주는 큐 => 우선순위가 높은 데이터부터 출력
- tuple로 값을 넣어줌 (우선순위, data)
- 우선 순위의 수가 낮을 수록 우선 순위는 높은 것
어디에 큐가 많이 쓰일까?
큐의 경우에는 장단점 보다는 (특별히 언급되는 장단점이 없음), 큐의 활용 예로 프로세스 스케쥴링 방식을 함께 이해해두는 것이 좋음
Queue()로 큐 만들기 (가장 일반적인 큐, FIFO(First-In, First-Out))
import queue
data_queue = queue.Queue()
data_queue.put("funcoding")
data_queue.put(1)
data_queue.qsize() #2
data_queue.get() # 'funcoding'
리스트 변수로 큐를 다루는 enqueue, dequeue 기능 python으로 구현해보기
queue_list = list()
def enqueue(data):
queue_list.append(data)
def dequeue():
data = queue_list[0]
del queue_list[0]
return data
for index in range(10):
enqueue(index)
len(queue_list)
#10
dequeue()
#0 : first in first out!
- 데이터를 제한적으로 접근할 수 있는 구조
- 한쪽 끝에서만 자료를 넣거나 뺄 수 있는 구조 => 큐와 유사
- 가장 나중에 쌓은 데이터를 가장 먼저 빼낼 수 있는 데이터 구조
- 큐: FIFO 정책
- 스택: LIFO 정책
1. 스택 구조
- 스택은 LIFO(Last In, Fisrt Out) 또는 FILO(First In, Last Out) 데이터 관리 방식을 따름
- LIFO: 마지막에 넣은 데이터를 가장 먼저 추출하는 데이터 관리 정책
- FILO: 처음에 넣은 데이터를 가장 마지막에 추출하는 데이터 관리 정책
대표적인 스택의 활용
주요 기능
2. 스택 구조와 프로세스 스택
=> 다음 함수의 동작 방식이 스택과 유사
# 재귀 함수
def recursive(data):
if data < 0:
print ("ended")
else:
print(data)
recursive(data - 1)
print("returned", data)
recursive(4)
'''
#result
4
3
2
1
0
ended
returned 0
returned 1
returned 2
returned 3
returned 4
'''
3. 자료 구조 스택의 장단점
- 스택은 단순하고 빠른 성능을 위해 사용되므로, 보통 배열 구조를 활용해서 구현하는 것이 일반적임.
- 이 경우, 위에서 열거한 단점이 있을 수 있음
4. 파이썬 리스트 기능에서 제공하는 메서드로 스택 사용해보기
data_stack = list()
data_stack.append(1)
data_stack.append(2)
data_stack
# [1, 2]
data_stack.pop()
# 2
python을 통해 리스트 변수로 스텍을 다루는 pop, push 기능 구현해보기
stack_list = list()
def push(data):
stack_list.append(data)
def pop():
data = stack_list[-1]
del stack_list[-1]
return data
1. 링크드 리스트 (Linked List) 구조
연결 리스트라고도 함
배열은 순차적으로 연결된 공간에 데이터를 나열하는 데이터 구조 => 미리 공간의 크기를 설정하고 데이터를 입력해야하는 단점
링크드 리스트는 떨어진 곳에 존재하는 데이터를 화살표로 연결해서 관리하는 데이터 구조 => 미리 예약을 하지 않고 필요 시 데이터를 연결해서 추가
본래 C언어에서는 주요한 데이터 구조이지만, 파이썬은 리스트 타입이 링크드 리스트의 기능을 모두 지원
링크드 리스트 기본 구조와 용어
2. 간단한 링크드 리스트 예
Node 구현 : 보통 파이썬에서 링크드 리스트 구현시, 파이썬 클래스를 활용함
class Node:
def __init__(self, data, next=None):
self.data = data
self.next = next
def add(data):
node = head
while node.next: # node.next가 있으면..! => True
node = node.next
node.next = Node(data)
3. 링크드 리스트의 장단점 (전통적인 C언어에서의 배열과 링크드 리스트)
4. 링크드 리스트의 복잡한 기능1 (링크드 리스트 데이터 사이에 데이터를 추가)
node = head
while node.next:
print(node.data)
node = node.next
print (node.data)
"""
1
2
3
4
5
6
7
8
9
"""
코드를 입력하세요
node3 = Node(1.5) # 1.5라는 data를 1과 2 사이에 넣고 싶다!
node = head
search = True
while search:
if node.data == 1:
search = False
else:
node = node.next
node_next = node.next
node.next = node3
node3.next = node_next
node = head
while node.next:
print(node.data)
node = node.next
print (node.data)
"""
1
1.5
2
3
4
5
6
7
8
9
"""
5. 파이썬 객체지향 프로그래밍으로 링크드 리스트 구현하기
class Node:
def __init__(self, data, next=None):
self.data = data
self.next = next
class NodeMgmt: # 링크드 리스트를 관리하는 클래스
def __init__(self, data):
self.head = Node(data)
def add(self, data):
if self.head == '': # 방어 코드로, 보통은 else로 실행됨
self.head = Node(data) # data를 받아 node를 만들고, head 함수로 설정한다.
else:
node = self.head
while node.next:
node = node.next
node.next = Node(data)
def desc(self):
node = self.head
while node:
print (node.data)
node = node.next
6. 링크드 리스트의 복잡한 기능 2 (특정 노드를 삭제)
- head 삭제
- 마지막 노드 삭제
- 중간 노드 삭제
class Node:
def __init__(self, data, next=None):
self.data = data
self.next = next
class NodeMgmt:
def __init__(self, data):
self.head = Node(data)
def add(self, data):
if self.head == '':
self.head = Node(data)
else:
node = self.head
while node.next:
node = node.next
node.next = Node(data)
def desc(self):
node = self.head
while node:
print (node.data)
node = node.next
def delete(self, data):
if self.head == '':
print ("해당 값을 가진 노드가 없습니다.")
return
if self.head.data == data:
temp = self.head
self.head = self.head.next
del temp
else:
node = self.head
while node.next:
if node.next.data == data:
temp = node.next
node.next = node.next.next # 마지막 노드 삭제, 중간 노드 삭제 동시에 해결 가능한 코드
del temp
return
else:
node = node.next
노드 데이터가 특정 숫자인 노드를 찾는 함수를 만들기
def search_node(self, data):
node = self.head
while node:
if node.data == data:
return node
else:
node = node.next
7. 다양한 링크드 리스트 구조

#double_linked_list
# self.head : list의 가장 첫번째 노드
# self.tail : list의 가장 마지막 노드
class Node:
def __init__(self, data, prev=None, next=None):
self.prev = prev
self.data = data
self.next = next
class NodeMgmt:
def __init__(self, data):
self.head = Node(data)
self.tail = self.head
def insert(self, data): # 리스트의 마지막에 data를 추가하는 메서드
if self.head == None:
self.head = Node(data)
self.tail = self.head
else:
node = self.head
while node.next: # 가장 마지막 노드에 도착하면 node.next = None으로 while문이 False가 되어 반복문이 멈춤
node = node.next
new = Node(data)
node.next = new
new.prev = node
self.tail = new
def desc(self):
node = self.head
while node:
print (node.data)
node = node.next
노드 데이터가 특정 숫자인 노드 앞에 데이터를 추가하는 함수를 만들기
class Node:
def __init__(self, data, prev=None, next=None):
self.prev = prev
self.data = data
self.next = next
class NodeMgmt:
def __init__(self, data):
self.head = Node(data)
self.tail = self.head
def insert(self, data):
if self.head == None:
self.head = Node(data)
self.tail = self.head
else:
node = self.head
while node.next:
node = node.next
new = Node(data)
node.next = new
new.prev = node
self.tail = new
def desc(self):
node = self.head
while node:
print (node.data)
node = node.next
def search_from_head(self, data):
if self.head == None:
return False
node = self.head
while node:
if node.data == data:
return node
else:
node = node.next
return False
def search_from_tail(self, data):
if self.head == None:
return False
node = self.tail
while node:
if node.data == data:
return node
else:
node = node.prev
return False
def insert_before(self, data, before_data):
if self.head == None:
self.head = Node(data)
return True # True : 값이 제대로 추가되었음을 의미
else:
node = self.tail
while node.data != before_data: # 반복문을 거쳐 나왔다는 것은 해당 data를 갖는 Node를 찾았다는 것이고, 해당 노드 전에 data를 갖는 노드를 생성한다.
node = node.prev
if node == None:
return False
new = Node(data)
before_new = node.prev
before_new.next = new
new.prev = before_new
new.next = node
node.prev = new
return True
노드 데이터가 특정 숫자인 노드 뒤에 데이터를 추가하는 함수를 만들기
class Node:
def __init__(self, data, prev=None, next=None):
self.prev = prev
self.data = data
self.next = next
class NodeMgmt:
def __init__(self, data):
self.head = Node(data)
self.tail = self.head
def insert_before(self, data, before_data):
if self.head == None:
self.head = Node(data)
return True
else:
node = self.tail
while node.data != before_data:
node = node.prev
if node == None:
return False
new = Node(data)
before_new = node.prev
before_new.next = new
new.next = node
return True
def insert_after(self, data, after_data):
if self.head == None:
self.head = Node(data)
return True
else:
node = self.head
while node.data != after_data:
node = node.next
if node == None:
return False
new = Node(data)
after_new = node.next
new.next = after_new
new.prev = node
node.next = new
if new.next == None:
self.tail = new
return True
def insert(self, data):
if self.head == None:
self.head = Node(data)
else:
node = self.head
while node.next:
node = node.next
new = Node(data)
node.next = new
new.prev = node
self.tail = new
def desc(self):
node = self.head
while node:
print (node.data)
node = node.next