자료구조

maro·2024년 3월 12일

자료구조란

여러 개의 값들을 모아서 관리하는 데이터 타입.

  • 한 개의 변수는 한 개의 값 밖에는 가지지 못한다. 그러나 하나의 변수로 여러 개의 값 관리해야 할 경우가 있다.
  • 하나의 값이 여러개의 값들로 구성된 경우
    - 한명의 고객 정보의 경우 이름, 나이, 주소, 전화번호 등 여러개의 값이 모여서 하나의 값이 된다.
    - 한 반의 학생들의 이름들은 여러개의 이름들로 구성된다.

파이썬은 데이터를 모으는 방식에 따라 다음과 같이 4개의 타입을 제공한다.

  • List: 순서가 있으며 중복된 값들을 모으는 것을 허용하고 구성하는 값들(원소)을 변경할 수 있다.
  • Tuple: 순서가 있으며 중복된 값들을 모으는 것을 허용하는데 구성하는 값들을 변경할 수 없다.
  • Dictionary: key-value 형태로 값들을 저장해 관리한다.
  • Set: 중복을 허용하지 않고 값들의 순서가 없다.
    원소, 성분, 요소, element
  • 자료구조의 값들을 구성하는 개별 값들을 말한다.
  • len(자료구조) 함수
    - 자료구조 내의 원소의 개수를 반환한다.

List(리스트)

  • 값들을 순서대로 모아서 관리하는 자료구조. 원소(element)들을 순번을 이용해 식별한다.
    - 각각의 원소가 어떤 값인지를 index(순번)을 가지고 식별하기 때문에 순서가 있고 그 순서가 매우 중요하다. 즉 같은 값에 대해 순서가 바뀌면 안된다.
  • 각각의 원소들은 index를 이용해 식별한다.
    - index는 문자열과 마찮가지로 양수 index와 음수 index 두개가 각 값에 생긴다.
    - 양수 index는 앞에서부터 음수 index는 뒤에서 부터 값을 식별할 때 사용하는 것이 편리하다.
    - index를 가지고 각 원소값의 의미를 식별할 수 있으면 List나 Tuple을 사용한다.
  • 중복된 값들을 저장할 수 있다.
  • 각 원소들의 데이터 타입은 달라도 상관없다.
    - 보통은 같은 타입의 데이터를 모은다.
  • 리스트를 구성하는 원소들을 변경할 수 있다. (추가, 삭제, 변경이 가능)
    - 원소 변경 여부가 List와 Tuple의 차이이다.

List 생성 구문

[값, 값, 값, ..]

ages = [10, 20, 30, 11, 23, 32]
ages

[10, 20, 30, 11, 23, 32]

names = ["이순신", "홍길동", "유관순", "강감찬"]
names

['이순신', '홍길동', '유관순', '강감찬']

person_info = ["홍길동", 20, "서울", 182.1, True]
person_info

['홍길동', 20, '서울', 182.1, True]

Indexing과 Slicing을 이용한 원소(element) 조회 및 변경

Indexing

  • 하나의 원소를 조회하거나 변경할 때 사용
  • 리스트[index]
    - index의 원소를 조회
  • 리스트[index] = 값
    - index의 원소를 변경

Slicing

  • 범위로 조회하거나 그 범위의 값들을 변경한다.
  • 기본구문: 리스트[ 시작 index : 종료 index : 간격]
    - 시작 index ~ (종료 index – 1)
    - 간격을 지정하면 간격만큼 index를 증/감한다. (생략 시 1이 기본 간격)
  • 0번 index 부터 조회 할 경우 시작 index는 생략가능
    - 리스트 [ : 5] => 0 ~ 4 까지 조회
  • 마지막 index까지 (끝까지) 조회 할 경우 종료 index는 생략 가능
    - 리스트[2 : ] => 2번 index 에서 끝까지
  • 명시적으로 간격을 줄 경우
    - 리스트[ : : 3 ] => 0, 3, 6, 9.. index의 값 조회
    - 리스트[1 : 9 : 2] => 1, 3, 5, 7 index의 값 조회
  • 시작 index > 종료 index, 간격을 음수로 하면 역으로 반환한다.(Reverse)
    - 리스트[5: 1: -1] => 5, 4, 3, 2 index의 값 조회
    - 리스트[: : -1] => 마지막 index ~ 0번 index 까지 의미. Reverse 한다.

slicing을 이용한 값 변경

  • slicing 을 이용할 경우 slicing된 원소 개수와 동일한 개수의 값들을 대입한다.
    - 리스트[1:5] = 10,20,30,40 : index 1, 2, 3, 4의 값을 각각 10, 20, 30, 40 으로 변경
ages

[10, 20, 30, 11, 23, 32]

print(ages[0], ages[-6]) # 양수, 음수 index
print(ages[0], ages[3])

10 10
10 11

ages[0] = 50 # 변경
ages

[50, 20, 30, 11, 23, 32]

ages[1:5]

[20, 30, 11, 23]

ages[:5] # 시작 index 생략 -> 처음부터

[50, 20, 30, 11, 23]

ages[-3:] #종료 index 생략 -> 끝까지

[11, 23, 32]

ages[::2] # 범위 : 처음 ~ 끝, 간격 : 2

[50, 30, 23]

ages[::-1]

[32, 23, 11, 30, 20, 50]

ages[1:4] = 100, 200, 300 
ages

[50, 100, 200, 300, 23, 32]

List 연산자

리스트 + 리스트

  • 두 리스트의 원소들을 합친 리스트를 반환한다.
    리스트 * 정수
  • 같은 리스트의 원소들을 정수번 합친 리스트를 반환한다.
    in, not in 연산자
  • 값 in 리스트
    - 리스트의 원소로 값이 있으면 True, 없으면 False 반환
  • 값 not in 리스트
    - 리스트의 원소로 값이 없으면 True, 있으면 False 반환
    len(리스트)
  • 리스트 내의 원소수를 반환.
l1 = [1, 2, 3, 4]
l2 = [10, 20, 30, 40]
l3 = l1 + l2 + ages
l3

[1, 2, 3, 4, 10, 20, 30, 40, 50, 100, 200, 300, 23, 32]

l4 = l1 * 3
l4

[1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4]

30 in ages # ages의 원소 중에 30이 있는지

False

len(l3)

14

중첩 리스트 (Nested List)

  • List가 원소로 List를 가지는 것을 말한다.
    - List를 포함한 모든 자료구조 타입들도 다 값이므로 다른 자료구조의 원소로 들어갈 수 있다.
scores = [
            [100, 50, 70],
            [70, 100, 90],
            [100, 40, 80]
        ]
        scores[0][1]

50

scores[1][1] = 70
scores

[[100, 50, 70], [70, 70, 90], [100, 40, 80]]

List 주요 메소드

append(value) : value를 추가한다.
extend(List) : List의 원소들을 추가한다.
sort([reverse=False]) : 원소들을 오름차순 정렬한다. reverse=True로 하면 내림차순정렬 한다.
insert(index, 삽입할값) : 지정한 index에 '삽입할값'을 삽입한다.
remove(삭제할값) : '삭제할값' 값과 같은 원소를 삭제한다.
index(찾을값[, 시작index]) : '찾을값'의 index를 반환한다.
pop([index]) : index의 값을 반환하면서 삭제한다. index 생략하면 가장 마지막 값을 반환하며 삭제한다.
count(값) : '값'이 리스트의 원소로 몇개 있는지 반환한다.
clear() : 리스트 안의 모든 원소들을 삭제한다.

ages

[50, 100, 200, 300, 23, 32]

ages.append(45)
ages.append(22)
ages

[50, 100, 200, 300, 23, 32, 45, 22]

ages.extend([1, 2, 3, 4]) # 여러 개 추가할 때
ages

[50, 100, 200, 300, 23, 32, 45, 22, 1, 2, 3, 4]

ages.sort() # 오름차순으로 정렬
ages

[1, 2, 3, 4, 22, 23, 32, 45, 50, 100, 200, 300]

ages.sort(reverse = True) # 내림차순 정렬
ages

[300, 200, 100, 50, 45, 32, 23, 22, 4, 3, 2, 1]

sorted(ages) # 리스트.sort() : 리스트 자체를 변경(정렬)
             # sorted(자료구조) : 정렬한 결과를 새로운 리스트로 반환

[1, 2, 3, 4, 22, 23, 32, 45, 50, 100, 200, 300]

#삭제
ages.remove(300) # remove(삭제할 값)
ages

[200, 100, 50, 45, 32, 23, 22, 4, 3, 2, 1]

ages.pop(1)

50

v = ages.pop(-1)
ages

[200, 45, 32, 23, 22, 4, 3]

v

2

ages.clear()
ages

[]

ages.extend([1, 2, 3, 1, 2, 3])
ages

[1, 2, 3, 1, 2, 3]

#조회
ages.index(2) # 원소 2의 index를 반환

1

ages.index(2,3) # 원소 2를 찾는데 index 3에서부터 찾아라

4

ages.count(1)

2

Tuple(튜플)

  • List와 같이 순서대로 원소들을 관리한다. 단 저장된 원소를 변경할 수 없다.
  • Tuple 은 각 위치(Index) 마다 정해진 의미가 있고 그 값이 한번 설정되면 바뀌지 않는 경우에 사용한다.
    - Tuple은 값의 변경되지 않으므로 안전하다.

Tuple 생성

  • (value, value, value, ...)
  • 소괄호를 생략할 수 있다.
  • 원소가 하나인 Tuple 표현식
    - (value,) 또는 value,
    - 값 뒤에 , 를 붙여준다. ,를 붙이지 않으면 ( )가 연산자 우선순위 괄호가 된다.
t1 = (1, 10, 5, 70)
t1

(1, 10, 5, 70)

t2 = 1, 2, 3, 10, 20, 100, 5, 17, 20 # 튜플은 괄호를 생략할 수 있다
t2

(1, 2, 3, 10, 20, 100, 5, 17, 20)

t3 = 10, # 값 뒤에 ','를 붙이면 원소가 한 개인 튜플
t3

(10,)

t4 = ("홍길동",) # 쉼표를 안 붙히면 그냥 문자열로 인식
t4

('홍길동',)

Indexing과 Slicing을 이용한 원소(element) 조회

  • 리스트와 동일하다.
  • 단 튜플은 조회만 가능하고 원소를 변경할 수 없다.
t2[1], t2[-1], t2[5]

(2, 20, 100)

t2[2: 7: 2]

(3, 20, 5)

t2[:7]

(1, 2, 3, 10, 20, 100, 5)

t2[:5]

(1, 2, 3, 10, 20)

t2[::-1]

(20, 17, 5, 100, 20, 10, 3, 2, 1)

t1[0] = 20  # 변경할 수 없다.

TypeError Traceback (most recent call last)
Cell In[12], line 1
----> 1 t1[0] = 20
TypeError: 'tuple' object does not support item assignment

Tuple 연산자

tuple + tuple

  • 두 tuple의 원소들을 합친 tuple을 반환한다.
    tuple * 정수
  • 같은 tuple의 원소들을 정수번 합친 tuple를 반환한다.
    in, not in 연산자
  • 값 in tuple : tuple의 원소로 값이 있으면 True, 없으면 False 반환
  • 값 not in tuple : tuple의 원소로 값이 없으면 True, 있으면 False 반환
    len(tuple)
  • tuple의 원소 개수 반환

Tuple 주요 메소드

index(찾을값 [, 시작index]) : '찾을값'이 몇번 index인지 반환한다.
count(값) : 원소로 '값'이 몇개 있는지 반환한다.

Dictionary

값을 키(key)-값(value) 쌍으로 묶어서 저장하는 자료구조이다.

  • 리스트나 튜플의 index의 역할을 하는 key를 직접 지정한다.
  • 서로 의미가 다른 값들을 하나로 묶을 때 그 값의 의미를 key로 가질 수 있는 dictionary를 사용한다.
    - cf) 값의 의미가 같을 경우 List나 Tuple을 사용한다.
  • key-value 쌍으로 묶은 데이터 한개를 item 또는 entry라고 한다.
  • key는 중복을 허용하지 않고 value는 중복을 허용한다.

Dictionary 생성

구문
1. { 키 : 값, 키 : 값, 키 : 값 }
2. dict(key=value, key=value) 함수 이용

  • 키(key)는 불변(Immutable)의 값들만 사용 가능하다. (숫자, 문자열, 튜플) 일반적으로 문자열을 사용한다.
  • dict() 함수를 사용할 경우 key는 변수로 정의한다
# 사과: 10, 귤: 20, 수박: 5
# l = [10, 20, 5]
# l[0]
f1 = {"사과":10, "귤":20, "수박": 5}
f1

{'사과': 10, '귤': 20, '수박': 5}

f2 = dict(사과=10, 귤=20, 수박=5)
f2

{'사과': 10, '귤': 20, '수박': 5}

Dictionary 원소 조회 및 변경

조회: index에 key값을 식별자로 지정한다.

  • dictionary[ key ]
  • 없는 키로 조회 시 KeyError 발생
    변경
  • dictionary[ key ] = 값
  • 있는 key값에 값을 대입하면 변경이고 없는 key 일 경우는 새로운 item을 추가하는 것이다.
f1['사과']
f1['수박']

5

f1['복숭아'] # 없는 것 조회시 KeyError

KeyError Traceback (most recent call last)
Cell In[23], line 1
----> 1 f1['복숭아']
KeyError: '복숭아'

#변경 - 있는 key에 대입
f1['사과'] = 50
f1

{'사과': 50, '귤': 20, '수박': 5}

#추가 - 없는 key 에 대입
f1['복숭아'] = 100
f1

{'사과': 50, '귤': 20, '수박': 5, '복숭아': 100}

Dictionary 연산자

in, not in 연산자

  • 값 in dictionary
    - dictionary의 Key로 값이 있으면 True, 없으면 False 반환
  • 값 not in dictionary
    - dictionary의 Key로 값이 없으면 True, 있으면 False 반환

len(dictionary)

  • dictionary의 Item의 개수 반환

Dictionary 주요 메소드

get(key[, 기본값]) : key의 item의 값을 반환한다. 단 key가 없을 경우 None또는 기본값을 반환한다.
pop(key) : key의 item의 값을 반환하면서 dictionary에서 삭제한다. 없는 key일 경우 KeyError발생
clear() : dictionary의 모든 item들을 삭제한다.
del dict[key] : key의 item을 제거한다.
items() : item의 key, value를 튜플로 묶어 모아 반환한다.
keys() : key값들만 모아 반환한다.
values() : value값들만 모아 반환한다.

f1['사과'], f1.get("사과")

(50, 50)

# f1['딸기']  #없는 key 조회 -> 에러
f1.get("딸기") # 없는 key 조회 -> default 값을 반환(None)
f1.get("딸기", "없음") # (key, 없으면반환할값)

'없음'

v = f1.pop("귤") # 삭제하면서 value를 반환.
v

20

f1

{'수박': 5, '복숭아': 100}

v = f1.items()   
v
#  [ (key, value), (key, value) ]

dict_items([('수박', 5), ('복숭아', 100)])

v = list(v)
v[1]

('복숭아', 100)

keys = f1.keys()   # key값들만 모아서 반환
keys

dict_keys(['수박', '복숭아'])

values = f1.values() # value 들만 모아서 반환
values

dict_values([5, 100])

set

Set은 중복되는 값을 허용하지 않고 순서를 신경 쓰지 않는다.

  • 원소를 식별할 수 있는 식별자가 없기 때문에 Set은 indexing과 slicing을 지원하지 않는다

Set 생성

구문

  • {값, 값, 값 }

빈 Dictionary 만들기

  • info = {}
  • 괄호만 사용하면 빈 set이 아니라 빈 dictionary를 생성하는 것임.
s1 = {1, 2, 3, 4, 5, 1, 1, 2, 2, 3, 3, 4, 4, 4, 5, 1, 3, }
s1

{1, 2, 3, 4, 5}

info = {} #dict
type(info)  # type(값) : 값의 data type을 알려준다.

dict

Set 연산자

in, not in 연산자

  • 값 in Set
    - Set의 원소로 값이 있으면 True, 없으면 False 반환
  • 값 not in Set
    - Set의 원소로 값이 없으면 True, 있으면 False 반환
    len(Set)
  • Set의 원소의 개수 반환
    집합연산자

Set의 주요 메소드

add(값) 집합에 값 추가
update(자료구조) 자료구조내의 원소들을 모두 집합에 추가
pop() 원소를 반환하고 Set에서 삭제한다.
remove(값) 값을 찾아서 Set에서 삭제한다.

Set의 집합연산 연산자 및 메소드

합집합

  • 집합A | 집합B
  • 집합A.union(집합B)

교집합

  • 집합A & 집합B
  • 집합A.intersection(집합B)

차집합

  • 집합A - 집합B
  • 집합A.difference(집합B)
s1 = {1, 2, 3, 4, 5}
s2 = {4, 5, 6, 7, 8, 9}
s1 | s2
s1.union(s2)
s1 & s2
s1.intersection(s2)
s1 - s2
s1.difference(s2)

{1, 2, 3, 4, 5, 6, 7, 8, 9}
{4, 5}
{1, 2, 3}

자료구조 변환 함수

list(자료구조)

  • 대상 자료구조/Iterable을 List로 변환한다.
    tuple(자료구조)
  • 대상 자료구조/Iterable을 Tuple로 변환
    set(자료구조)
  • 대상 자료구조/Iterable을 Set으로 변환
  • 다른 자료구조의 원소 중 중복을 빼고 조회할 때 set()를 이용해 Set으로 변환한다.
    Dictionary로 변환하는 함수는 없다.
  • dict(key=value, ..) 는 딕셔너리 생성하는 함수이다.
    변경 대상이 Dictionary 일 경우에는 key값들만 모아서 변환한다.
    Iterable
  • 반복가능한 객체.
  • 다음 값 달라는 요청받으면 값을 제공한다. 제공할 값을 다 줄 때까지 요청을 받을때 마다 순차적으로 하나씩 제공한다.
    - Iterable이 제공하는 값을 반복문을 이용해 조회할 경우 for in문을 사용한다.
  • 대표적으로 자료구조, 문자열 등이 있다.
l = [1, 2, 3, 1, 2, 3, 1, 2, 3]
t = tuple(l)
print(t)
type(l), type(t)

(1, 2, 3, 1, 2, 3, 1, 2, 3)
(list, tuple)

s = set(l)
s, type(s)

({1, 2, 3}, set)

s2 = set(t)
s2

{1, 2, 3}

d = {"name":"이순신", "age":20, "address":"서울"}
list(d)  
tuple(d)
set(d)

{'address', 'age', 'name'}

list("abcdefghij")

['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j']

d = list("월화수목금토일")
d[3]+"요일"

'목요일'

실습

# 문제 1 ~ 7
jumsu = [100, 90, 100, 80, 70, 100, 80, 90, 95, 85] 
# 위 리스트는 학생번호 1번 ~ 10번까지 10명의 시험 점수이다. 
#(1)  7번의 점수를 출력하세요 
jumsu[6]

80

#(2)  1번부터 5번까지의 점수를 출력하세요.
jumsu[:5]

[100, 90, 100, 80, 70]

#(3)  4, 5, 6, 7번의 점수를 출력하세요.
jumsu[3:7]

[80, 70, 100, 80]

jumsu

[100, 90, 100, 80, 70, 100, 80, 90, 95, 85]

#(4) 짝수번째 점수를 출력하세요.
jumsu[1::2]

[90, 80, 100, 90, 85]

#(5) 홀수번째 점수를 출력하세요.
jumsu[::2]

[100, 100, 70, 80, 95]

#(6) 9번의 점수를 20으로 변경하고 전체 출력하세요.
print(jumsu)
jumsu[8] = 20
print(jumsu)

[100, 90, 100, 80, 70, 100, 80, 90, 95, 85][100, 90, 100, 80, 70, 100, 80, 90, 20, 85]

#(7) 중복된 점수는 제거하고 하나씩만 나오도록 출력하세요.
list(set(jumsu))

[100, 70, 80, 20, 85, 90]

# 문제 8 ~ 9
fruits = ["복숭아", "수박", "딸기"]
#(8) fruits 리스트에 마지막 원소로 "사과", "귤"을 추가하세요.
# fruits.append("사과")
# fruits.append("귤")
fruits.extend(["사과", "귤"])
fruits

['복숭아', '수박', '딸기', '사과', '귤']

#(9) fruits 리스트에서 "복숭아"를 제거하세요.
i = fruits.index("복숭아")
fruits.pop(i)   #del fruits[i]
fruits

['수박', '딸기', '사과', '귤']

#문제 10 ~ 15
#(10)본인의 이름, 나이, email주소, 취미, 결혼유무를 사전(딕셔너리)으로 생성. 
#취미는 2개 이상의 값을 넣는다..
info = {
    "이름":"홍길동",
    "나이":20,
    "email주소":"honggd@a.com",
    "취미":["독서", "영화감상", "게임"], 
    "결혼여부":False
}
#(11) 위 딕셔너리에서 이름과 email주소를 조회해서 출력하세요.
print(f"이름: {info['이름']}\nEmail주소: {info['email주소']}")

이름: 홍길동
Email주소: honggd@a.com

#(12) 위 딕셔너리에서 취미중 두번째 취미를 조회해서 출력하세요.
info['취미'][1]

'영화감상'

#(13) 위 딕셔너리에 몸무게와 키 항목을 추가하세요.
info['키'] = 172.15
info['몸무게'] = 72.6
info

{'이름': '홍길동',
'나이': 20,
'email주소': 'honggd@a.com',
'취미': ['독서', '영화감상', '게임'],
'결혼여부': False,
'키': 172.15,
'몸무게': 72.6}

#(14) 위 딕셔너리에서 email 주소를 다른 값으로 변경하세요.
info['email주소'] = "honggd@naver.com"
info

{'이름': '홍길동',
'나이': 20,
'email주소': 'honggd@naver.com',
'취미': ['독서', '영화감상', '게임'],
'결혼여부': False,
'키': 172.15,
'몸무게': 72.6}

#(15) 위 딕셔너리에서 나이를 제거하세요.
info.pop("나이")
info

{'이름': '홍길동',
'email주소': 'honggd@naver.com',
'취미': ['독서', '영화감상', '게임'],
'결혼여부': False,
'키': 172.15,
'몸무게': 72.6}

del info['결혼여부']
info

{'이름': '홍길동',
'email주소': 'honggd@naver.com',
'취미': ['독서', '영화감상', '게임'],
'키': 172.15,
'몸무게': 72.6}

profile
공부 & 프로젝트 & 개발 블로그

0개의 댓글