├── chap01 ├── 2digits1.py ├── 2digits2.py ├── 2digits3.py ├── alternative1.py ├── alternative1a.py ├── alternative2.py ├── alternative2a.py ├── branch1.py ├── branch2.py ├── branch2a.py ├── for.py ├── for_else.py ├── input1.py ├── input2.py ├── judge_sign.py ├── max3.py ├── max3_func.py ├── median3.py ├── median3a.py ├── multiplication_table.py ├── object_function.py ├── print_stars1.py ├── print_stars2.py ├── rectangle.py ├── skip1.py ├── skip2.py ├── sum.py ├── sum1ton_for.py ├── sum1ton_positive.py ├── sum1ton_while.py ├── sum1ton_while2.py ├── sum_gauss.py ├── sum_verbose1.py ├── sum_verbose2.py ├── triangle_lb.py ├── triangle_lb2.py └── triangle_rb.py ├── chap02 ├── card_conv.py ├── card_conv_verbose.py ├── list1.py ├── list2.py ├── list3.py ├── list4.py ├── list_element.py ├── list_tuple.py ├── max.py ├── max_of_test.py ├── max_of_test_input.py ├── max_of_test_randint.py ├── pass_list.py ├── prime1.py ├── prime2.py ├── prime3.py ├── prime3a.py ├── reverse.py ├── reverse2.py ├── sum_1ton.py ├── total.py ├── tuple1.py ├── tuple2.py ├── tuple3.py └── tuple4.py ├── chap03 ├── bsearch.py ├── bsearch_ve.py ├── bsearch_verbose.py ├── chained_hash.py ├── chained_hash_test.py ├── open_hash.py ├── open_hash_test.py ├── ssearch_for.py ├── ssearch_sentinel.py ├── ssearch_test1.py ├── ssearch_test2.py ├── ssearch_ve.py └── ssearch_while.py ├── chap04 ├── fixed_queue.py ├── fixed_queue_test.py ├── fixed_stack.py ├── fixed_stack_test.py ├── last_elements.py ├── stack.py └── stack_test.py ├── chap05 ├── 8queen.py ├── 8queen2.py ├── 8queen_b.py ├── 8queen_bb.py ├── factorial.py ├── factorial_ve.py ├── gcd.py ├── hanoi.py ├── recur1.py ├── recur1a.py ├── recur1b.py ├── recur2.py └── stack.py ├── chap06 ├── binary_insertion_sort.py ├── binary_insort.py ├── bubble_sort1.py ├── bubble_sort1_verbose.py ├── bubble_sort2.py ├── bubble_sort2_verbose.py ├── bubble_sort3.py ├── bubble_sort3_verbose.py ├── counting_sort.py ├── heap_sort.py ├── heapq_heap_sort.py ├── heapq_merge.py ├── heapq_merge_sort.py ├── insertion_sort.py ├── merge.py ├── merge_sort.py ├── partition.py ├── quick_sort1.py ├── quick_sort1_non_recur.py ├── quick_sort1_verbose.py ├── quick_sort2.py ├── selection_sort.py ├── shaker_sort.py ├── shaker_sort_verbose.py ├── shell_sort1.py ├── shell_sort2.py ├── sorted_sort.py └── stack.py ├── chap07 ├── bf_match.py ├── bm_match.py ├── find.py ├── index.py └── kmp_match.py ├── chap08 ├── array_list.py ├── array_list_test.py ├── double_list.py ├── double_list_test.py ├── linked_list.py └── linked_list_test.py └── chap09 ├── bst.py ├── bst2.py ├── bst2_test.py └── bst_test.py /chap01/2digits1.py: -------------------------------------------------------------------------------- 1 | # [Do it! 실습 1C-3] 2자리 양수(10 ~ 99) 입력받기 2 | 3 | print('2자리 양수를 입력하세요.') 4 | 5 | while True: 6 | no = int(input('값을 입력하세요.: ')) 7 | if no >= 10 and no <= 99: 8 | break 9 | 10 | print(f'입력받은 양수는 {no}입니다.') -------------------------------------------------------------------------------- /chap01/2digits2.py: -------------------------------------------------------------------------------- 1 | # 2자리 양수(10 ~ 99) 입력받기 2 2 | 3 | print('2자리 양수를 입력하세요.') 4 | 5 | while True: 6 | no = int(input('값을 입력하세요.: ')) 7 | if 10 <= no <= 99: 8 | break 9 | 10 | print(f'입력받은 양수는 {no}입니다.') -------------------------------------------------------------------------------- /chap01/2digits3.py: -------------------------------------------------------------------------------- 1 | # 2자리 양수(10~99) 입력받기 3 2 | 3 | print('2자리 양수를 입력하세요.') 4 | 5 | while True: 6 | no = int(input('값을 입력하세요.: ')) 7 | if not(no < 10 or no > 99): 8 | break 9 | 10 | print(f'입력받은 양수는 {no}입니다.') -------------------------------------------------------------------------------- /chap01/alternative1.py: -------------------------------------------------------------------------------- 1 | # [Do it! 실습 1-12] +와 -를 번갈아 출력하기 1 2 | 3 | print('+와 -를 번갈아 출력합니다.') 4 | n = int(input('몇 개를 출력할까요?: ')) 5 | 6 | for i in range(n): # 반복 n번 7 | if i % 2: 8 | print('-', end='') # 홀수인 경우 - 출력 9 | else: 10 | print('+', end='') # 짝수인 경우 + 출력 11 | 12 | print() -------------------------------------------------------------------------------- /chap01/alternative1a.py: -------------------------------------------------------------------------------- 1 | # [Do it! 실습 1-12] +와 -를 번갈아 출력하기 1(for 문 수정) 2 | 3 | print('+와 -를 번갈아 출력합니다.') 4 | n = int(input('몇 개를 출력할까요?: ')) 5 | 6 | for i in range(1, n + 1): 7 | if i % 2: # 홀수 8 | print('+', end='') 9 | else: # 짝수 10 | print('-', end='') 11 | 12 | print() -------------------------------------------------------------------------------- /chap01/alternative2.py: -------------------------------------------------------------------------------- 1 | # [Do it! 실습 1-13] +와 -를 번갈아 출력하기 2 2 | 3 | print('+와 -를 번갈아 출력합니다.') 4 | n = int(input('몇 개를 출력할까요?: ')) 5 | 6 | for _ in range(n // 2): 7 | print('+-', end='') # n // 2개의 +-를 출력 8 | 9 | if n % 2: 10 | print('+', end='') # n이 홀수일 때만 +를 출력 11 | 12 | print() -------------------------------------------------------------------------------- /chap01/alternative2a.py: -------------------------------------------------------------------------------- 1 | # [Do it! 실습 1-13] +와 -를 번갈아 출력하기 2(range()함수 수정) 2 | 3 | print('+와 -를 번갈아 출력합니다.') 4 | n = int(input('몇 개를 출력할까요?: ')) 5 | 6 | for _ in range(1, n // 2 + 1): 7 | print('+-', end='') # n // 2개의 +-를 출력 8 | 9 | if n % 2: 10 | print('+', end='') # n이 홀수일 때만 +를 출력 -------------------------------------------------------------------------------- /chap01/branch1.py: -------------------------------------------------------------------------------- 1 | # [Do it! 실습 1-4] 3개로 분기하는 조건문 2 | 3 | n = int(input('정수를 입력하세요.: ')) 4 | 5 | if n == 1: 6 | print('A') 7 | elif n == 2: 8 | print('B') 9 | else: 10 | print('C') -------------------------------------------------------------------------------- /chap01/branch2.py: -------------------------------------------------------------------------------- 1 | # [Do it! 실습 1-5] 4개로 분기하는 조건문 2 | 3 | n = int(input('정수를 입력하세요.: ')) 4 | 5 | if n == 1: 6 | print('A') 7 | elif n == 2: 8 | print('B') 9 | elif n == 3: 10 | print('C') -------------------------------------------------------------------------------- /chap01/branch2a.py: -------------------------------------------------------------------------------- 1 | # [Do it! 실습 1-6] 실습 1-5의 원래 모습 2 | 3 | n = int(input('정수를 입력하세요.: ')) 4 | 5 | if n == 1: 6 | print('A') 7 | elif n == 2: 8 | print('B') 9 | elif n == 3: 10 | print('C') 11 | else : 12 | pass -------------------------------------------------------------------------------- /chap01/for.py: -------------------------------------------------------------------------------- 1 | # [Do it! 실습 1C-5] 1부터 100까지 반복하여 출력 2 | 3 | for i in range(1, 101): 4 | print(f'i = {i:3} id(i) = {id(i)}') -------------------------------------------------------------------------------- /chap01/for_else.py: -------------------------------------------------------------------------------- 1 | # [Do it! 실습 1-18] 10~99 사이의 난수 n개 생성하기(13이 나오면 중단) 2 | 3 | import random 4 | 5 | n = int(input('난수의 개수를 입력하세요.: ')) 6 | 7 | for _ in range(n): 8 | r = random.randint(10, 99) 9 | print(r, end=' ') 10 | if r == 13: 11 | print('\n프로그램을 중단합니다.') 12 | break 13 | else : 14 | print('\n난수 생성을 종료합니다.') -------------------------------------------------------------------------------- /chap01/input1.py: -------------------------------------------------------------------------------- 1 | # [Do it! 실습 1C-1] 이름을 입력받아 인사하기 2 | 3 | print('이름을 입력하세요.: ', end = '') 4 | name = input() 5 | print(f'안녕하세요? {name} 님.') -------------------------------------------------------------------------------- /chap01/input2.py: -------------------------------------------------------------------------------- 1 | # 이름을 입력 받아 인사합니다(실습 1C-1 수정). 2 | 3 | name = input( '이름을 입력하세요.: ') 4 | print(f'안녕하세요? {name} 님.') -------------------------------------------------------------------------------- /chap01/judge_sign.py: -------------------------------------------------------------------------------- 1 | # [Do it! 실습 1-3] 입력받은 정숫값의 부호(양수, 음수, 0) 출력하기 2 | 3 | n = int(input('정수를 입력하세요.: ')) 4 | 5 | if n > 0: 6 | print('이 수는 양수입니다.') 7 | elif n < 0: 8 | print('이 수는 음수입니다.') 9 | else: 10 | print('이 수는 0입니다.') -------------------------------------------------------------------------------- /chap01/max3.py: -------------------------------------------------------------------------------- 1 | # [Do it! 실습 1-1] 세 정수를 입력받아 최댓값 구하기 2 | 3 | print('세 정수의 최댓값을 구합니다.') 4 | a = int(input('정수 a의 값을 입력하세요.: ')) 5 | b = int(input('정수 b의 값을 입력하세요.: ')) 6 | c = int(input('정수 c의 값을 입력하세요.: ')) 7 | 8 | maximum = a 9 | if b > maximum: maximum = b 10 | if c > maximum: maximum = c 11 | 12 | print(f'최댓값은 {maximum}입니다.') -------------------------------------------------------------------------------- /chap01/max3_func.py: -------------------------------------------------------------------------------- 1 | # [Do it! 실습 1-2] # 세 정수의 최댓값을 구하기 2 | 3 | def max3(a, b, c): 4 | """a, b, c의 최댓값을 구하여 반환""" 5 | maximum = a 6 | if b > maximum: maximum = b 7 | if c > maximum: maximum = c 8 | return maximum # 최댓값 반환 9 | 10 | print(f'max3(3, 2, 1) = {max3(3, 2, 1)}') # [A] a > b > c 11 | print(f'max3(3, 2, 2) = {max3(3, 2, 2)}') # [B] a > b = c 12 | print(f'max3(3, 1, 2) = {max3(3, 1, 2)}') # [C] a > c > b 13 | print(f'max3(3, 2, 3) = {max3(3, 2, 3)}') # [D] a = c > b 14 | print(f'max3(2, 1, 3) = {max3(2, 1, 3)}') # [E] c > a > b 15 | print(f'max3(3, 3, 2) = {max3(3, 3, 2)}') # [F] a = b > c 16 | print(f'max3(3, 3, 3) = {max3(3, 3, 3)}') # [G] a = b = c 17 | print(f'max3(2, 2, 3) = {max3(2, 2, 3)}') # [H] c > a = b 18 | print(f'max3(2, 3, 1) = {max3(2, 3, 1)}') # [I] b > a > c 19 | print(f'max3(2, 3, 2) = {max3(2, 3, 2)}') # [J] b > a = c 20 | print(f'max3(1, 3, 2) = {max3(1, 3, 2)}') # [K] b > c > a 21 | print(f'max3(2, 3, 3) = {max3(2, 3, 3)}') # [L] b = c > a 22 | print(f'max3(1, 2, 3) = {max3(1, 2, 3)}') # [M] c > b > a -------------------------------------------------------------------------------- /chap01/median3.py: -------------------------------------------------------------------------------- 1 | # [Do it! 실습 1C-2] 세 정숫값을 입력받아 중앙값을 구하기 1 2 | 3 | def med3(a, b, c): 4 | """a, b, c의 중앙값을 구하여 반환""" 5 | if a >= b: 6 | if b >= c: 7 | return b 8 | elif a <= c: 9 | return a 10 | else: 11 | return c 12 | elif a > c: 13 | return a 14 | elif b > c: 15 | return c 16 | else: 17 | return b 18 | 19 | print('세 정수의 중앙값을 구합니다.') 20 | a = int(input('정수 a의 값을 입력하세요.: ')) 21 | b = int(input('정수 b의 값을 입력하세요.: ')) 22 | c = int(input('정수 c의 값을 입력하세요.: ')) 23 | 24 | print(f'중앙값은 {med3(a, b, c)}입니다.') -------------------------------------------------------------------------------- /chap01/median3a.py: -------------------------------------------------------------------------------- 1 | # 세 정숫값을 입력받아 중앙값을 구하기 2 2 | 3 | def med3(a, b, c): 4 | """a, b, c의 중앙값을 구하여 반환(다른 방법)""" 5 | if (b >= a and c <= a) or (b <= a and c >= a): 6 | return a 7 | elif (a > b and c < b) or (a < b and c > b): 8 | return b 9 | return c 10 | 11 | print('세 정수의 중앙값을 구합니다.') 12 | a = int(input('정수 a의 값을 입력하세요.: ')) 13 | b = int(input('정수 b의 값을 입력하세요.: ')) 14 | c = int(input('정수 c의 값을 입력하세요.: ')) 15 | 16 | print(f'중앙값은 {med3(a, b, c)}입니다.') -------------------------------------------------------------------------------- /chap01/multiplication_table.py: -------------------------------------------------------------------------------- 1 | # [Do it! 실습 1-21] 구구단 곱셈표 출력하기 2 | 3 | print('-' * 27) 4 | for i in range(1, 10): # 행 루프 5 | for j in range(1, 10): # 열 루프 6 | print(f'{i * j : 3}', end='') 7 | print() # 행 변경 8 | print('-' * 27) -------------------------------------------------------------------------------- /chap01/object_function.py: -------------------------------------------------------------------------------- 1 | # [Do it! 실습 1C-4] 함수의 내부·외부에서 정의한 변수와 객체의 식별 번호를 출력 2 | 3 | n = 1 # 전역 변수(함수 내부·외부에서 사용) 4 | def put_id(): 5 | x = 1 # 지역 변수(함수 내부에서만 사용) 6 | print(f'id(x) = {id(x)}') 7 | 8 | print(f'id(1) = {id(1)}') 9 | print(f'id(n) = {id(n)}') 10 | put_id() -------------------------------------------------------------------------------- /chap01/print_stars1.py: -------------------------------------------------------------------------------- 1 | # [Do it! 실습 1-14] *를 n개 출력하되 w개마다 줄바꿈하기 1 2 | 3 | print('*를 출력합니다.') 4 | n = int(input('몇 개를 출력할까요? : ')) 5 | w = int(input('몇 개마다 줄바꿈할까요? : ')) 6 | 7 | for i in range(n): # n번 반복 8 | print('*', end='') 9 | if i % w == w - 1: # n번 판단 10 | print() # 줄바꿈 11 | 12 | if n % w: 13 | print() # 줄바꿈 -------------------------------------------------------------------------------- /chap01/print_stars2.py: -------------------------------------------------------------------------------- 1 | # *를 n개 출력하되 w개마다 줄바꿈하기 2 2 | 3 | print('*를 출력합니다.') 4 | n = int(input('몇 개를 출력할까요?: ')) 5 | w = int(input('몇 개마다 줄바꿈할까요?: ')) 6 | 7 | for _ in range(n // w): # 반복 n // w번 반복 8 | print('*' * w) 9 | 10 | rest = n % w 11 | if rest: 12 | print('*' * rest) # if 문 1번 판단 -------------------------------------------------------------------------------- /chap01/rectangle.py: -------------------------------------------------------------------------------- 1 | # [Do it! 실습 1-17] 가로 세로가 정수이고 넓이가 area인 직사각형에서 변의 길이를 나열하기 2 | 3 | area = int(input('직사각형의 넓이를 입력하세요.: ')) 4 | 5 | for i in range(1, area + 1): # 1부터 사각형의 넓이 계산 6 | if i * i > area: break 7 | if area % i: continue 8 | print(f'{i} × {area // i}') -------------------------------------------------------------------------------- /chap01/skip1.py: -------------------------------------------------------------------------------- 1 | # [Do it! 실습 1-19] 1~12까지 8을 건너뛰고 출력하기 1 2 | 3 | for i in range(1, 13): 4 | if i == 8: 5 | continue 6 | print(i, end=' ') 7 | 8 | print() -------------------------------------------------------------------------------- /chap01/skip2.py: -------------------------------------------------------------------------------- 1 | # [Do it! 실습 1-20] 1부터 12까지 8을 건너 뛰고 출력하기 2 2 | 3 | for i in list(range(1, 8)) + list(range(9, 13)): 4 | print(i, end=' ') 5 | print() -------------------------------------------------------------------------------- /chap01/sum.py: -------------------------------------------------------------------------------- 1 | # [Do it! 실습 1-9] a부터 b까지 정수의 합 구하기(for 문) 2 | 3 | print('a부터 b까지의 합을 구합니다.') 4 | a = int(input('정수 a를 입력하세요.: ')) 5 | b = int(input('정수 b를 입력하세요.: ')) 6 | 7 | if a > b: 8 | a, b = b, a # a와 b를 오름차순으로 정렬 9 | 10 | sum = 0 11 | for i in range(a, b + 1): 12 | sum += i # sum에 i를 더함 13 | 14 | print(f'{a}부터 {b}까지 정수의 합은 {sum}입니다.') -------------------------------------------------------------------------------- /chap01/sum1ton_for.py: -------------------------------------------------------------------------------- 1 | # [Do it! 실습 1-8] 1부터 n까지의 합 구하기 2(for 문) 2 | 3 | print('1부터 n까지의 합을 구합니다.') 4 | n = int(input('n값을 입력하세요.: ')) 5 | 6 | sum = 0 7 | for i in range(1, n + 1): 8 | sum += i # sum에 i를 더함 9 | 10 | print(f'1부터 {n}까지 정수의 합은 {sum}입니다.') -------------------------------------------------------------------------------- /chap01/sum1ton_positive.py: -------------------------------------------------------------------------------- 1 | # [Do it! 실습 1-16] 1부터 n까지 정수의 합 구하기(n값은 양수만 입력받음) 2 | 3 | print('1부터 n까지 정수의 합을 구합니다.') 4 | 5 | while True: 6 | n = int(input('n값을 입력하세요.: ')) 7 | if n > 0: 8 | break # n이 0보다 커질 때까지 반복 9 | 10 | sum = 0 11 | i = 1 12 | 13 | for i in range(1, n + 1): 14 | sum += i # sum에 i를 더함 15 | i += 1 # i에 1을 더함 16 | 17 | print(f'1부터 {n}까지 정수의 합은 {sum}입니다.') -------------------------------------------------------------------------------- /chap01/sum1ton_while.py: -------------------------------------------------------------------------------- 1 | # [Do it! 실습 1-7] 1부터 n까지 정수의 합 구하기 1(while 문) 2 | 3 | print('1부터 n까지 정수의 합을 구합니다.') 4 | n = int(input('n값을 입력하세요.: ')) 5 | 6 | sum = 0 7 | i = 1 8 | 9 | while i <= n: # i가 n보다 작거나 같은 동안 반복 10 | sum += i # sum에 i를 더함 11 | i += 1 # i에 1을 더함 12 | 13 | print(f'1부터 {n}까지 정수의 합은 {sum}입니다.') -------------------------------------------------------------------------------- /chap01/sum1ton_while2.py: -------------------------------------------------------------------------------- 1 | # 1부터 n까지 정수의 합 구하기 1(while 문) 2 | 3 | print('1부터 n까지 정수의 합을 구합니다.') 4 | n = int(input('n값을 입력하세요. : ')) 5 | 6 | sum = 0 7 | i = 1 8 | 9 | while i <= n: # i가 n보다 작거나 같은 동안 반복 10 | sum += i # sum에 i를 더함 11 | i += 1 # i에 1을 더함 12 | 13 | print(f'1부터 {n}까지 정수의 합은 {sum}입니다.') 14 | print(f'i값은 {i}입니다.') -------------------------------------------------------------------------------- /chap01/sum_gauss.py: -------------------------------------------------------------------------------- 1 | # 1부터 n까지의 합 구하기 3(가우스 덧셈 방법) 2 | 3 | print('1부터 n까지의 합을 구합니다.') 4 | n = int(input('n값을 입력하세요.: ')) 5 | 6 | sum = n * (n + 1) // 2 7 | 8 | print(f'1부터 {n}까지의 합은 {sum}입니다.') -------------------------------------------------------------------------------- /chap01/sum_verbose1.py: -------------------------------------------------------------------------------- 1 | # [Do it! 실습 1-10] a부터 b까지 정수의 합 구하기 1 2 | 3 | print('a부터 b까지의 합을 구합니다.') 4 | a = int(input('정수 a를 입력하세요.: ')) 5 | b = int(input('정수 b를 입력하세요.: ')) 6 | 7 | if a > b: 8 | a, b = b, a 9 | 10 | sum = 0 11 | for i in range(a, b + 1): # b - a번 반복 12 | if i < b: # i가 b보다 작으면 합을 구하는 과정을 출력 13 | print(f'{i} + ', end='') 14 | else: # i가 b보다 크거나 같으면 최종값 출력을 위해 i =를 출력 15 | print(f'{i} = ', end='') 16 | sum += i # sum에 i를 더함 17 | 18 | print(sum) -------------------------------------------------------------------------------- /chap01/sum_verbose2.py: -------------------------------------------------------------------------------- 1 | # [Do it! 실습 1-11] a부터 b까지 정수의 합 구하기 2 2 | 3 | print('a부터 b까지 정수의 합을 구합니다.') 4 | a = int(input('정수 a를 입력하세요.: ')) 5 | b = int(input('정수 b를 입력하세요.: ')) 6 | 7 | if a > b : 8 | a, b = b, a 9 | 10 | sum = 0 11 | for i in range(a, b): 12 | print(f'{i} + ', end='') 13 | sum += i # sum에 i를 더함 14 | 15 | print(f'{b} = ', end ='') 16 | sum += b # sum에 b를 더함 17 | 18 | print(sum) -------------------------------------------------------------------------------- /chap01/triangle_lb.py: -------------------------------------------------------------------------------- 1 | # [Do it! 실습 1-22] 왼쪽 아래가 직각인 이등변 삼각형으로 * 출력하기 2 | 3 | print('왼쪽 아래가 직각인 이등변 삼각형을 출력합니다.') 4 | n = int(input('짧은 변의 길이를 입력하세요.: ')) 5 | 6 | for i in range(n): # 행 루프 7 | for j in range(i + 1): # 열 루프 8 | print('*', end='') 9 | print() -------------------------------------------------------------------------------- /chap01/triangle_lb2.py: -------------------------------------------------------------------------------- 1 | # 왼쪽 아래가 직각인 이등변 삼각형으로 * 출력하기(언더스코어 사용) 2 | 3 | print('왼쪽 아래가 직각인 이등변 삼각형을 출력합니다.') 4 | n = int(input('짧은 변의 길이를 입력하세요.: ')) 5 | 6 | for i in range(n): # 행 루프 7 | for _ in range(i + 1): # 열 루프 8 | print('*', end='') 9 | print() -------------------------------------------------------------------------------- /chap01/triangle_rb.py: -------------------------------------------------------------------------------- 1 | # [Do it! 실습 1-23] 오른쪽 아래가 직각인 이등변 삼각형으로 * 출력하기 2 | 3 | print('오른쪽 아래가 직각인 이등변 삼각형을 출력합니다.') 4 | n = int(input('짧은 변 길이를 입력하세요.: ')) 5 | 6 | for i in range(n): # 행 루프 7 | for _ in range(n - i - 1): # 열 루프(공백을 출력) 8 | print(' ', end='') 9 | for _ in range(i + 1): 10 | print('*', end='') # 열 루프(*을 출력) 11 | print() -------------------------------------------------------------------------------- /chap02/card_conv.py: -------------------------------------------------------------------------------- 1 | # Do it! 실습 2-7 [A] 10진수 정수값을 입력받아 2~36진수로 변환하여 출력하기 2 | 3 | def card_conv(x: int, r: int) -> str: 4 | """정수 x를 r 진수로 변환한 뒤 그 수를 나타내는 문자열을 반환""" 5 | 6 | d = '' # 변환 뒤 문자열 7 | dchar = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ' 8 | 9 | while x > 0: 10 | d += dchar [x % r] # 해당하는 문자를 꺼내 결합 11 | x //= r 12 | 13 | return d[::-1] # 역순으로 반환 14 | 15 | # Do it! 실습 2-7 [B] 16 | 17 | if __name__ == '__main__': 18 | print('10진수를 n진수로 변환합니다.') 19 | 20 | while True: 21 | while True : # 음이 아닌 정수를 입력받음 22 | no = int(input('변환할 값으로 음이 아닌 정수를 입력하세요.: ')) 23 | if no > 0: 24 | break 25 | 26 | while True : # 2~36진수의 정수값을 입력받음 27 | cd = int(input('어떤 진수로 변환할까요?: ')) 28 | if 2 <= cd <= 36: 29 | break 30 | 31 | print(f'{cd}진수로는 {card_conv(no, cd)}입니다.') 32 | 33 | retry = input( "한 번 더 변환할까요?(Y ... 예/N ... 아니오): ") 34 | if retry in {'N', 'n'}: 35 | break -------------------------------------------------------------------------------- /chap02/card_conv_verbose.py: -------------------------------------------------------------------------------- 1 | # 10진수 정수값을 입력받아 2~36진수로 변환하여 출력하기(실습 2-7 수정) 2 | 3 | def card_conv(x: int, r: int) -> str: 4 | """정수 x를 r 진수로 변환한 뒤 그 수를 나타내는 문자열을 반환""" 5 | 6 | d = '' # 변환 뒤 문자열 7 | dchar = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ' 8 | n = len(str(x)) # 변환하기 전의 자릿수 9 | 10 | print(f'{r:2} | {x:{n}d}') 11 | while x > 0: 12 | print(' +' + (n + 2) * '-') 13 | if x // r: 14 | print(f'{r:2} | {x // r:{n}d} … {x % r}') 15 | else: 16 | print(f' {x // r:{n}d} … {x % r}') 17 | d += dchar [x % r] # 해당하는 문자를 꺼내 결합 18 | x //= r 19 | 20 | return d[::-1] # 역순으로 반환 21 | 22 | # 이하 Do it! 실습 2-7 [B]와 동일 23 | 24 | if __name__ == '__main__': 25 | print('10진수를 n진수로 변환합니다.') 26 | 27 | while True: 28 | while True : # 음이 아닌 정수를 입력받음 29 | no = int(input('변환할 값으로 음이 아닌 정수를 입력하세요.: ')) 30 | if no > 0: 31 | break 32 | 33 | while True : # 2~36진수의 정수값을 입력받음 34 | cd = int(input('어떤 진수로 변환할까요?: ')) 35 | if 2 <= cd <= 36: 36 | break 37 | 38 | print(f'{cd}진수로는 {card_conv(no, cd)}입니다.') 39 | 40 | retry = input( "한 번 더 변환할까요?(Y … 예/N … 아니오) : ") 41 | if retry in {'N', 'n'}: 42 | break 43 | -------------------------------------------------------------------------------- /chap02/list1.py: -------------------------------------------------------------------------------- 1 | # [Do it! 실습 2C-1] 리스트의 모든 원소를 ​​스캔하기(원소 수를 미리 파악) 2 | 3 | x = ['John', 'George', 'Paul', 'Ringo'] 4 | 5 | for i in range(len(x)): 6 | print(f'x[{i}] = {x[i]}') -------------------------------------------------------------------------------- /chap02/list2.py: -------------------------------------------------------------------------------- 1 | # [Do it! 실습 2C-2] 리스트의 모든 원소를 ​​enumerate 함수로 스캔하기 2 | 3 | x = ['John', 'George', 'Paul', 'Ringo'] 4 | 5 | for i, name in enumerate(x): 6 | print(f'x[{i}] = {name}') -------------------------------------------------------------------------------- /chap02/list3.py: -------------------------------------------------------------------------------- 1 | # [Do it! 실습 2C-3] 리스트의 모든 요소를 ​​enumerate 함수로 스캔(1부터 카운트) 2 | 3 | x = ['John', 'George', 'Paul', 'Ringo'] 4 | 5 | for i, name in enumerate(x, 1): 6 | print(f'{i}번째 = {name}') -------------------------------------------------------------------------------- /chap02/list4.py: -------------------------------------------------------------------------------- 1 | # [Do it! 실습 2C-4] 리스트의 모든 원소를 ​​스캔하기(인덱스 값을 사용하지 않음) 2 | 3 | x = ['John', 'George', 'Paul', 'Ringo'] 4 | 5 | for i in x: 6 | print(i) -------------------------------------------------------------------------------- /chap02/list_element.py: -------------------------------------------------------------------------------- 1 | # [Do it! 실습 2C-7] 자료형을 정하지 않은 리스트 원소 확인하기 2 | 3 | x = [15, 64, 7, 3.14, [32, 55], 'ABC'] 4 | for i in range(len(x)): 5 | print(f'x[{i}] = {x[i]}') -------------------------------------------------------------------------------- /chap02/list_tuple.py: -------------------------------------------------------------------------------- 1 | # 리스트와 튜플 생성 2 | 3 | list01 = [] # [] 빈 리스트 생성 4 | list02 = [1, 2, 3] # [1, 2, 3] 5 | list03 = ['A', 'B', 'C', ] # ['A', 'B', 'C'] 맨 마지막 원소에 쉼표를 써도 됨 6 | 7 | list04 = list() # [] 빈 리스트 생성 8 | list05 = list('ABC') # ['A', 'B', 'C'] 각각의 문자로부터 원소 생성 9 | list06 = list([1, 2, 3]) # [1, 2, 3] 리스트로부터 원소 생성 10 | list07 = list((1, 2, 3)) # [1, 2, 3] 튜플로부터 원소 생성 11 | list08 = list({1, 2, 3}) # [1, 2, 3] 집합으로부터 원소 생성 12 | 13 | list09 = list(range(7)) # [0, 1, 2, 3, 4, 5, 6] 14 | list10 = list(range(3, 8)) # [3, 4, 5, 6, 7] 15 | list11 = list(range(3, 13, 2)) # [3, 5, 7, 9, 11] 16 | 17 | # 원소 수가 5이고 모든 원소값이 없는 리스트를 생성 18 | list12 = [None] * 5 # [None, None, None, None, None] 19 | 20 | tuple01 = () # () 21 | tuple02 = 1, # (1,) 22 | tuple03 = (1,) # (1,) 23 | tuple04 = 1, 2, 3 # (1, 2, 3) 24 | tuple05 = 1, 2, 3, # (1, 2, 3) 25 | tuple06 = (1, 2, 3) # (1, 2, 3) 26 | tuple07 = (1, 2, 3, ) # (1, 2, 3) 27 | tuple08 = 'A', 'B', 'C', # ('A', 'B', 'C') 28 | 29 | tuple09 = tuple() # () 빈 튜플 생성 30 | tuple10 = tuple('ABC') # ('A', 'B', 'C') 문자열의 각 문자로부터 원소를 생성 31 | tuple11 = tuple([1, 2, 3]) # (1, 2, 3) 리스트로부터 원소 생성 32 | tuple12 = tuple({1, 2, 3}) # (1, 2, 3) 집합으로부터 원소 생성 33 | 34 | tuple13 = tuple(range(7)) # (0, 1, 2, 3, 4, 5, 6) 35 | tuple14 = tuple(range(3, 8)) # (3, 4, 5, 6, 7) 36 | tuple15 = tuple(range(3, 13, 2)) # (3, 5, 7, 9, 11) 37 | 38 | print('list01 =', list01) 39 | print('list02 =', list02) 40 | print('list03 =', list03) 41 | print('list04 =', list04) 42 | print('list05 =', list05) 43 | print('list06 =', list06) 44 | print('list07 =', list07) 45 | print('list08 =', list08) 46 | print('list09 =', list09) 47 | print('list10 =', list10) 48 | print('list11 =', list11) 49 | print('list12 =', list12) 50 | 51 | print('tupe01 =', tuple01) 52 | print('tupe02 =', tuple02) 53 | print('tupe03 =', tuple03) 54 | print('tupe04 =', tuple04) 55 | print('tupe05 =', tuple05) 56 | print('tupe06 =', tuple06) 57 | print('tupe07 =', tuple07) 58 | print('tupe08 =', tuple08) 59 | print('tupe09 =', tuple09) 60 | print('tupe10 =', tuple10) 61 | print('tupe11 =', tuple11) 62 | print('tupe12 =', tuple12) 63 | print('tupe13 =', tuple13) 64 | print('tupe14 =', tuple14) 65 | print('tupe15 =', tuple15) -------------------------------------------------------------------------------- /chap02/max.py: -------------------------------------------------------------------------------- 1 | # [Do it! 실습 2-2] 시퀀스 원소의 최댓값 출력하기 2 | 3 | from typing import Any, Sequence 4 | 5 | def max_of(a: Sequence) -> Any: 6 | """시퀀스형 a 요소의 최댓값을 반환""" 7 | maximum = a[0] 8 | for i in range(1, len(a)): 9 | if a[i] > maximum: 10 | maximum = a[i] 11 | return maximum 12 | 13 | if __name__ == '__main__': 14 | print('배열의 최댓값을 구합니다.') 15 | num = int(input('원소 수를 입력하세요 : ')) 16 | x = [None] * num # 원소 수가 num인 리스트를 생성 17 | 18 | for i in range(num): 19 | x[i] = int(input(f'x[{i}]를 입력하세요.: ')) 20 | 21 | print(f'최댓값은 {max_of(x)}입니다.') -------------------------------------------------------------------------------- /chap02/max_of_test.py: -------------------------------------------------------------------------------- 1 | # [Do it! 실습 2-5] 배열 요소의 최댓값을 구해서 출력하기(튜플, 문자열, 문자열 리스트) 2 | 3 | from max import max_of 4 | 5 | t = (4, 7, 5.6, 2, 3.14, 1) 6 | s = 'string' 7 | a = ['DTS', 'AAC', 'FLAC'] 8 | 9 | print(f'{t}의 최댓값은 {max_of(t)}입니다.') 10 | print(f'{s}의 최댓값은 {max_of(s)}입니다.') 11 | print(f'{a}의 최댓값은 {max_of(a)}입니다.') -------------------------------------------------------------------------------- /chap02/max_of_test_input.py: -------------------------------------------------------------------------------- 1 | # [Do it! 실습 2-3] 배열 원소의 최댓값을 구해서 출력하기(원솟값을 입력받음) 2 | 3 | from max import max_of 4 | 5 | print('배열의 최댓값을 구합니다.') 6 | print('주의: "End"를 입력하면 종료합니다.') 7 | 8 | number = 0 9 | x = [] # 빈 리스트 10 | 11 | while True: 12 | s = input(f'x[{number}]를 입력하세요.: ') 13 | if s == 'End': 14 | break 15 | x.append(int(s)) # 배열의 끝에 추가 16 | number += 1 17 | 18 | print(f'{number}개를 입력했습니다.') 19 | print(f'최댓값은 {max_of(x)}입니다.') -------------------------------------------------------------------------------- /chap02/max_of_test_randint.py: -------------------------------------------------------------------------------- 1 | # [Do it! 실습 2-4] 배열 원소의 최댓값을 구해서 출력하기(원솟값을 난수로 생성) 2 | 3 | import random 4 | from max import max_of 5 | 6 | print('난수의 최댓값을 구합니다.') 7 | num = int(input('난수의 개수를 입력하세요.: ')) 8 | lo = int(input('난수의 최솟값을 입력하세요.: ')) 9 | hi = int(input('난수의 최댓값을 입력하세요.: ')) 10 | x = [None] * num # 원소 수 num인 리스트를 생성 11 | 12 | for i in range(num): 13 | x[i] = random.randint(lo, hi) 14 | 15 | print(f'{(x)}') 16 | print(f'이 가운데 최댓값은 {max_of(x)}입니다.') -------------------------------------------------------------------------------- /chap02/pass_list.py: -------------------------------------------------------------------------------- 1 | # [Do it! 실습 2C-6] 리스트의 모든 원솟값을 업데이트하기 2 | 3 | def change(lst, idx, val): 4 | """lst[idx]의 값을 val로 업데이트""" 5 | lst [idx] = val 6 | 7 | x = [11, 22, 33, 44, 55] 8 | print('x =', x) 9 | 10 | index = int(input('업데이트할 인덱스를 선택하세요.: ')) 11 | value = int(input('새로운 값을 입력하세요.: ')) 12 | 13 | change(x, index, value) 14 | print(f'x = {x}') -------------------------------------------------------------------------------- /chap02/prime1.py: -------------------------------------------------------------------------------- 1 | # [Do it! 실습 2-8] 1,000 이하의 소수를 나열하기 2 | 3 | counter = 0 # 나눗셈 횟수 4 | 5 | for n in range(2, 1001): 6 | for i in range(2, n): 7 | counter += 1 8 | if n % i == 0 : # 나누어 떨어지면 소수가 아님 9 | break # 반복은 더 이상 불필요하여 중단 10 | else: # 끝까지 나누어 떨어지지 않으면 다음을 수행 11 | print(n) 12 | print(f'나눗셈을 실행한 횟수: {counter}') -------------------------------------------------------------------------------- /chap02/prime2.py: -------------------------------------------------------------------------------- 1 | # [Do it! 실습 2-9] 1,000 이하의 소수를 나열하기(알고리즘 개선 1) 2 | 3 | counter = 0 # 나눗셈 횟수 4 | ptr = 0 # 이미 찾은 소수의 개수 5 | prime = [None] * 500 # 소수를 저장하는 배열 6 | 7 | prime[ptr] = 2 # 2는 소수이므로 초깃값으로 지정 8 | ptr += 1 9 | 10 | for n in range(3, 1001, 2): # 홀수만을 대상으로 설정 11 | for i in range(1, ptr): # 이미 찾은 소수로 나눔 12 | counter += 1 13 | if n % prime[i] == 0: # 나누어 떨어지면 소수가 아님 14 | break # 반복 중단 15 | else: # 끝까지 나누어 떨어지지 않았다면 16 | prime[ptr] = n # 소수로 배열에 등록 17 | ptr += 1 18 | 19 | for i in range(ptr): # ptr의 소수를 출력 20 | print(prime[i]) 21 | print(f'나눗셈을 실행한 횟수: {counter}') -------------------------------------------------------------------------------- /chap02/prime3.py: -------------------------------------------------------------------------------- 1 | # [Do it! 실습 2-10] 1,000 이하의 소수를 나열하기(알고리즘 개선 2) 2 | 3 | counter = 0 # 곱셈과 나눗셈을 합한 횟수 4 | ptr = 0 # 이미 찾은 소수의 개수 5 | prime = [None] * 500 # 소수를 저장하는 배열 6 | 7 | prime[ptr] = 2 # 2는 소수 8 | ptr += 1 9 | 10 | prime[ptr] = 3 # 3은 소수 11 | ptr += 1 12 | 13 | for n in range(5, 1001, 2): # 홀수만을 대상으로 설정 14 | i = 1 15 | while prime[i] * prime[i] <= n: 16 | counter += 2 17 | if n % prime[i] == 0: # 나누어 떨어지므로 소수가 아님 18 | break # 반복 중단 19 | i += 1 20 | else: # 끝까지 나누어 떨어지지 않았다면 21 | prime[ptr] = n # 소수로 배열에 등록 22 | ptr += 1 23 | counter += 1 24 | 25 | for i in range(ptr): # ptr개의 소수를 출력 26 | print(prime[i]) 27 | print(f'곱셈과 나눗셈을 실행한 횟수: {counter}') -------------------------------------------------------------------------------- /chap02/prime3a.py: -------------------------------------------------------------------------------- 1 | # [Do it! 실습 2-10] 1,000 이하의 소수를 나열하기(알고리즘 개선 2) - 배열의 원솟수를 미리 결정하지 않음 2 | 3 | counter = 0 # 곱셈과 나눗셈을 합한 횟수 4 | prime = [2, 3] # 소수를 저장하는 배열 5 | 6 | for n in range(5, 1001, 2): # 홀수만을 대상으로 설정 7 | i = 1 8 | while prime[i] * prime[i] <= n: 9 | counter += 2 10 | if n % prime[i] == 0: # 나누어 떨어지므로 소수가 아님 11 | break # 반복 중단 12 | i += 1 13 | else: # 끝까지 나누어 떨어지지 않았다면 14 | prime += [n] # 소수로 배열에 등록 15 | counter += 1 16 | 17 | for i in prime: # 소수를 출력 18 | print(i) 19 | print(f'곱셈과 나눗셈을 실행한 횟수: {counter}') -------------------------------------------------------------------------------- /chap02/reverse.py: -------------------------------------------------------------------------------- 1 | # [Do it! 실습 2-6] 뮤터블 시퀀스 원소를 역순으로 정렬 2 | 3 | from typing import Any, MutableSequence 4 | 5 | def reverse_array(a: MutableSequence) -> None: 6 | """뮤터블 시퀀스형 a의 원소를 역순으로 정렬""" 7 | n = len(a) 8 | for i in range(n // 2): 9 | a[i], a[n - i - 1] = a[n - i - 1], a[i] 10 | 11 | if __name__ == '__main__': 12 | print('배열 원소를 역순으로 정렬합니다.') 13 | nx = int(input('원소 수를 입력하세요.: ')) 14 | x = [None] * nx # 원소 수가 nx인 리스트를 생성 15 | 16 | for i in range(nx): 17 | x[i] = int(input(f'x[{i}] : ')) 18 | 19 | reverse_array(x) # x를 역순으로 정렬 20 | 21 | print('배열 원소를 역순으로 정렬했습니다.') 22 | for i in range(nx): 23 | print(f'x[{i}] = {x[i]}') -------------------------------------------------------------------------------- /chap02/reverse2.py: -------------------------------------------------------------------------------- 1 | # 뮤터블 시퀀스 원소를 역순으로 정렬(n을 사용하지 않음) 2 | 3 | from typing import Any, MutableSequence 4 | 5 | def reverse_array(a: MutableSequence) -> None: 6 | """뮤터블 시퀀스형 a의 원소를 역순으로 정렬""" 7 | for i in range(len(a) // 2): 8 | a[i], a[len(a) - i - 1] = a[len(a) - i - 1], a[i] 9 | 10 | if __name__ == '__main__': 11 | print('배열 원소를 역순으로 정렬합니다.') 12 | nx = int(input('원소 수를 입력하세요.: ')) 13 | x = [None] * nx # 원소 수가 nx인 리스트를 생성 14 | 15 | for i in range(nx): 16 | x[i] = int(input(f'x[{i}]:')) 17 | 18 | reverse_array(x) # x를 역순으로 정렬 19 | 20 | print('배열 원소를 역순으로 정렬했습니다.') 21 | for i in range(nx): 22 | print(f'x[{i}]={x[i]}') -------------------------------------------------------------------------------- /chap02/sum_1ton.py: -------------------------------------------------------------------------------- 1 | # [Do it! 실습 2C-5] 1부터 n까지 정수의 합 구하기 2 | 3 | def sum_1ton(n): 4 | """1부터 n까지 정수의 합""" 5 | s = 0 6 | while n > 0: 7 | s += n 8 | n -= 1 9 | return s 10 | 11 | x = int(input('x의 값을 입력하세요.: ')) 12 | print(f'1부터 {x}까지 합은 {sum_1ton(x)}입니다.') -------------------------------------------------------------------------------- /chap02/total.py: -------------------------------------------------------------------------------- 1 | #[Do it! 실습 2-1] 학생 5명의 점수를 입력받아 합계와 평균을 출력하기 2 | 3 | print('학생 그룹 점수의 합계와 평균을 구합니다.') 4 | 5 | score1 = int(input('1번 학생의 점수를 입력하세요.: ')) 6 | score2 = int(input('2번 학생의 점수를 입력하세요.: ')) 7 | score3 = int(input('3번 학생의 점수를 입력하세요.: ')) 8 | score4 = int(input('4번 학생의 점수를 입력하세요.: ')) 9 | score5 = int(input('5번 학생의 점수를 입력하세요.: ')) 10 | 11 | total = 0 12 | total += score1 13 | total += score2 14 | total += score3 15 | total += score4 16 | total += score5 17 | 18 | print(f'합계는 {total}점입니다.') 19 | print(f'평균은 {total / 5}점입니다.') -------------------------------------------------------------------------------- /chap02/tuple1.py: -------------------------------------------------------------------------------- 1 | # 튜플의 모든 원소를 ​​스캔하기(원소 수를 미리 파악) 2 | 3 | x = ('John', 'George', 'Paul', 'Ringo') 4 | 5 | for i in range(len(x)): 6 | print(f'x[{i}] = {x[i]}') -------------------------------------------------------------------------------- /chap02/tuple2.py: -------------------------------------------------------------------------------- 1 | # 튜플의 모든 원소를 ​​enumerate 함수로 스캔하기 2 | 3 | x = ('John', 'George', 'Paul', 'Ringo') 4 | 5 | for i, name in enumerate(x): 6 | print(f'x[{i}] = {name}') -------------------------------------------------------------------------------- /chap02/tuple3.py: -------------------------------------------------------------------------------- 1 | # 튜플의 모든 원소를 ​​enumerate 함수로 스캔하기(1부터 카운트) 2 | 3 | x = ('John', 'George', 'Paul', 'Ringo') 4 | 5 | for i, name in enumerate(x, 1): 6 | print(f'{i} 번째 = {name}') -------------------------------------------------------------------------------- /chap02/tuple4.py: -------------------------------------------------------------------------------- 1 | # 튜플의 모든 원소를 ​​스캔하기(인덱스 값을 사용하지 않음) 2 | 3 | x = ('John', 'George', 'Paul', 'Ringo') 4 | 5 | for i in x: 6 | print(i) -------------------------------------------------------------------------------- /chap03/bsearch.py: -------------------------------------------------------------------------------- 1 | # [Do it! 실습 3-3] 이진 검색 알고리즘 2 | 3 | from typing import Any, Sequence 4 | 5 | def bin_search(a: Sequence, key: Any) -> int: 6 | """시퀀스 a에서 key와 일치하는 원소를 이진 검색""" 7 | pl = 0 # 검색 범위 맨 앞 원소의 인덱스 8 | pr = len(a) - 1 # 검색 범위 맨 끝 원소의 인덱스 9 | 10 | while True: 11 | pc = (pl + pr) // 2 # 중앙 원소의 인덱스 12 | if a[pc] == key: 13 | return pc # 검색 성공 14 | elif a[pc] < key: 15 | pl = pc + 1 # 검색 범위를 뒤쪽의 절반으로 좁힘 16 | else: 17 | pr = pc - 1 # 검색 범위를 앞쪽의 절반으로 좁힘 18 | if pl > pr: 19 | break 20 | return -1 # 검색 실패 21 | 22 | if __name__ == '__main__': 23 | num = int(input('원소 수를 입력하세요.: ')) 24 | x = [None] * num # 원소 수가 num인 배열을 생성 25 | 26 | print('배열 데이터를 오름차순으로 입력하세요.') 27 | 28 | x[0] = int(input('x[0]: ')) 29 | 30 | for i in range(1, num): 31 | while True: 32 | x[i] = int(input(f'x[{i}]: ')) 33 | if x[i] >= x[i - 1]: 34 | break 35 | 36 | ky = int(input('검색할 값을 입력하세요.: ')) # 검색할 ky를 입력 37 | 38 | idx = bin_search(x, ky) # ky와 같은 값의 원소를 x에서 검색 39 | 40 | if idx < 0: 41 | print('검색값을 갖는 원소가 존재하지 않습니다.') 42 | else: 43 | print(f'검색값은 x[{idx}]에 있습니다.') -------------------------------------------------------------------------------- /chap03/bsearch_ve.py: -------------------------------------------------------------------------------- 1 | # 이진 검색 알고리즘(검색에 실패할 때 ValueError를 내보냄) 2 | 3 | from typing import Any, Sequence 4 | 5 | def bin_search(a: Sequence, key: Any) -> int: 6 | """시퀀스 a에서 key와 일치하는 요소를 이진 검색""" 7 | pl = 0 # 검색 범위 맨 앞 요소의 인덱스 8 | pr = len(a) - 1 # 검색 범위 맨 끝 요소의 인덱스 9 | 10 | while True: 11 | pc = (pl + pr) // 2 # 중앙 요소의 인덱스 12 | if a[pc] == key: 13 | return pc # 검색 성공 14 | elif a[pc] < key: 15 | pl = pc + 1 # 검색 범위를 뒤쪽 절반으로 좁힘 16 | else: 17 | pr = pc - 1 # 검색 범위를 앞쪽 절반으로 좁힘 18 | if pl > pr: 19 | break 20 | raise ValueError # 검색 실패 21 | 22 | if __name__ == '__main__': 23 | num = int(input('원소 수를 입력하세요.: ')) 24 | x = [None] * num # 원소 수 num인 배열을 생성 25 | 26 | print('오름차순으로 입력하세요.') 27 | 28 | x[0] = int(input('x[0] : ')) 29 | 30 | for i in range(1, num): 31 | while True: 32 | x[i] = int(input(f'x[{i}] : ')) 33 | if x[i] >= x[i - 1]: 34 | break 35 | 36 | ky = int(input('검색할 값을 입력하세요.: ')) # 키 ky를 입력받음 37 | 38 | try: 39 | idx = bin_search(x, ky) # ky와 같은 값의 요소를 x에서 검색 40 | except ValueError: 41 | print('검색값을 갖는 요소가 존재하지 않습니다.') 42 | else: 43 | print(f'검색값은 x[{idx}]에 있습니다.') -------------------------------------------------------------------------------- /chap03/bsearch_verbose.py: -------------------------------------------------------------------------------- 1 | # [Do it! 실습 3C-3] 이진 검색 알고리즘의 실행 과정을 출력 2 | 3 | from typing import Any, Sequence 4 | 5 | def bin_search(a: Sequence, key: Any) -> int: 6 | """시퀀스 a에서 key와 일치하는 원소를 이진 검색(실행 과정을 출력)""" 7 | pl = 0 # 검색 범위 맨 앞 원소의 인덱스 8 | pr = len(a) - 1 # 검색 범위 맨 끝 원소의 인덱스 9 | 10 | print(' |', end='') 11 | for i in range(len(a)): 12 | print(f'{i : 4}', end='') 13 | print() 14 | print('---+' + (4 * len(a) + 2) * '-') 15 | 16 | while True: 17 | pc = (pl + pr) // 2 # 중앙 원소의 인덱스 18 | 19 | print(' |', end='') 20 | if pl != pc: # pl 원소 위에 <-를 출력 21 | print((pl * 4 + 1) * ' ' + '<-' + ((pc - pl) * 4) * ' ' + '+', end='') 22 | else: 23 | print((pc * 4 + 1) * ' ' + '<+', end='') 24 | if pc != pr: # pr 원소 위에 ->를 출력 25 | print(((pr - pc) * 4 - 2) * ' ' + '->') 26 | else: 27 | print('->') 28 | print(f'{pc:3}|', end='') 29 | for i in range(len(a)): 30 | print(f'{a[i]:4}', end='') 31 | print('\n |') 32 | 33 | if a[pc] == key: 34 | return pc # 검색 성공 35 | elif a[pc] < key: 36 | pl = pc + 1 # 검색 범위를 뒤쪽의 절반으로 좁힘 37 | else: 38 | pr = pc - 1 # 검색 범위를 앞쪽의 절반으로 좁힘 39 | if pl > pr: 40 | break 41 | return -1 # 검색 실패 42 | 43 | if __name__ == '__main__': 44 | num = int(input('원소 수를 입력하세요.: ')) 45 | x = [None] * num # 원소 수가 num인 배열을 생성 46 | 47 | print('배열 데이터를 오름차순으로 입력하세요.') 48 | 49 | x[0] = int(input('x[0]: ')) 50 | 51 | for i in range(1, num): 52 | while True: 53 | x[i] = int(input(f'x[{i}]: ')) 54 | if x[i] >= x[i - 1]: 55 | break 56 | 57 | ky = int(input('검색할 값을 입력하세요.: ')) # 검색할 ky를 입력 58 | 59 | idx = bin_search(x, ky) # ky와 같은 값의 원소를 x에서 검색 60 | 61 | if idx == -1: 62 | print('검색값을 갖는 원소가 존재하지 않습니다.') 63 | else: 64 | print(f'검색값은 x[{idx}]에 있습니다.') -------------------------------------------------------------------------------- /chap03/chained_hash.py: -------------------------------------------------------------------------------- 1 | # 체인법으로 해시 함수 구현하기 2 | # Do it! 실습 3-5 [A] 3 | from __future__ import annotations 4 | from typing import Any, Type 5 | import hashlib 6 | 7 | class Node: 8 | """해시를 구성하는 노드""" 9 | 10 | def __init__(self, key: Any, value: Any, next: Node) -> None: 11 | """초기화""" 12 | self.key = key # 키 13 | self.value = value # 값 14 | self.next = next # 뒤쪽 노드를 참조 15 | 16 | # Do it! 실습 3-5 [B] 17 | class ChainedHash: 18 | """체인법을 해시 클래스 구현""" 19 | 20 | def __init__(self, capacity: int) -> None: 21 | """초기화""" 22 | self.capacity = capacity # 해시 테이블의 크기를 지정 23 | self.table = [None] * self.capacity # 해시 테이블(리스트)을 선언 24 | 25 | def hash_value(self, key: Any) -> int: 26 | """해시값을 구함""" 27 | if isinstance(key, int): 28 | return key % self.capacity 29 | return(int(hashlib.sha256(str(key).encode()).hexdigest(), 16) % self.capacity) 30 | 31 | # Do it! 실습 3-5[C] 32 | def search(self, key: Any) -> Any: 33 | """키가 key인 원소를 검색하여 값을 반환""" 34 | hash = self.hash_value(key) # 검색하는 키의 해시값 35 | p = self.table[hash] # 노드를 노드 36 | 37 | while p is not None: 38 | if p.key == key: 39 | return p.value # 검색 성공 40 | p = p.next # 뒤쪽 노드를 주목 41 | 42 | return None # 검색 실패 43 | 44 | def add(self, key: Any, value: Any) -> bool: 45 | """키가 key이고 값이 value인 원소를 삽입""" 46 | hash = self.hash_value(key) # 삽입하는 키의 해시값 47 | p = self.table[hash] # 주목하는 노드 48 | 49 | while p is not None: 50 | if p.key == key: 51 | return False # 삽입 실패 52 | p = p.next # 뒤쪽 노드에 주목 53 | 54 | temp = Node(key, value, self.table[hash]) 55 | self.table[hash] = temp # 노드를 삽입 56 | return True # 삽입 성공 57 | 58 | # Do it! 실습 3-5[D] 59 | def remove(self, key: Any) -> bool: 60 | """키가 key인 원소를 삭제""" 61 | hash = self.hash_value(key) # 삭제할 키의 해시값 62 | p = self.table[hash] # 주목하고 있는 노드 63 | pp = None # 바로 앞 주목 노드 64 | 65 | while p is not None: 66 | if p.key == key: # key를 발견하면 아래를 실행 67 | if pp is None: 68 | self.table[hash] = p.next 69 | else: 70 | pp.next = p.next 71 | return True # key 삭제 성공 72 | pp = p 73 | p = p.next # 뒤쪽 노드에 주목 74 | return False # 삭제 실패(key가 존재하지 않음) 75 | 76 | def dump(self) -> None: 77 | """해시 테이블을 덤프""" 78 | for i in range(self.capacity): 79 | p = self.table[i] 80 | print(i, end='') 81 | while p is not None: 82 | print(f' → {p.key} ({p.value})', end='') # 해시 테이블에 있는 키와 값을 출력 83 | p = p.next 84 | print() -------------------------------------------------------------------------------- /chap03/chained_hash_test.py: -------------------------------------------------------------------------------- 1 | # [Do it! 실습 3-6] 체인법을 구현하는 해시 클래스 ChainedHash의 사용 2 | 3 | from enum import Enum 4 | from chained_hash import ChainedHash 5 | 6 | Menu = Enum('Menu', ['추가', '삭제', '검색', '덤프', '종료']) # 메뉴를 선언 7 | 8 | def select_menu() -> Menu: 9 | """메뉴 선택""" 10 | s = [f'({m.value}){m.name}' for m in Menu] 11 | while True: 12 | print(*s, sep = ' ', end='') 13 | n = int(input(': ')) 14 | if 1 <= n <= len(Menu): 15 | return Menu(n) 16 | 17 | hash = ChainedHash(13) # 크기가 13인 해시 테이블을 생성 18 | 19 | while True: 20 | menu = select_menu() # 메뉴 선택 21 | 22 | if menu == Menu.추가: # 추가 23 | key = int(input('추가할 키를 입력하세요.: ')) 24 | val = input('추가할 값을 입력하세요.: ') 25 | if not hash.add(key, val): 26 | print('추가를 실패했습니다!') 27 | 28 | elif menu == Menu.삭제: # 삭제 29 | key = int(input('삭제할 키를 입력하세요.: ')) 30 | if not hash.remove(key): 31 | print('삭제를 실패했습니다!') 32 | 33 | elif menu == Menu.검색: # 검색 34 | key = int(input('검색할 키를 입력하세요.: ')) 35 | t = hash.search(key) 36 | if t is not None: 37 | print(f'검색한 키를 갖는 값은 {t}입니다.') 38 | else: 39 | print('검색할 데이터가 없습니다.') 40 | 41 | elif menu == Menu.덤프: # 덤프 42 | hash.dump() 43 | 44 | else: # 종료 45 | break -------------------------------------------------------------------------------- /chap03/open_hash.py: -------------------------------------------------------------------------------- 1 | # Do it! 실습 3-7 오픈 주소법으로 해시함수 구현하기 2 | 3 | from __future__ import annotations 4 | from typing import Any, Type 5 | from enum import Enum 6 | import hashlib 7 | 8 | # 버킷의 속성 9 | class Status(Enum): 10 | OCCUPIED = 0 # 데이터를 저장 11 | EMPTY = 1 # 비어 있음 12 | DELETED = 2 # 삭제 완료 13 | 14 | class Bucket: 15 | """해시를 구성하는 버킷""" 16 | 17 | def __init__(self, key: Any = None, value: Any = None, 18 | stat: Status = Status.EMPTY) -> None: 19 | """초기화""" 20 | self.key = key # 키 21 | self.value = value # 값 22 | self.stat = stat # 속성 23 | 24 | def set(self, key: Any, value: Any, stat: Status) -> None: 25 | """모든 필드에 값을 설정""" 26 | self.key = key # 키 27 | self.value = value # 값 28 | self.stat = stat # 속성 29 | 30 | def set_status(self, stat: Status) -> None: 31 | """속성을 설정""" 32 | self.stat = stat 33 | 34 | class OpenHash: 35 | """오픈 주소법을 구현하는 해시 클래스""" 36 | 37 | def __init__(self, capacity: int) -> None: 38 | """초기화""" 39 | self.capacity = capacity # 해시 테이블의 크기를 지정 40 | self.table = [Bucket()] * self.capacity # 해시 테이블 41 | 42 | def hash_value(self, key: Any) -> int: 43 | """해시값을 구함""" 44 | if isinstance(key, int): 45 | return key % self.capacity 46 | return(int(hashlib.md5(str(key).encode()).hexdigest(), 16) 47 | % self.capacity) 48 | 49 | def rehash_value(self, key: Any) -> int: 50 | """재해시값을 구함""" 51 | return(self.hash_value(key) + 1) % self.capacity 52 | 53 | def search_node(self, key: Any) -> Any: 54 | """키가 key인 버킷을 검색""" 55 | hash = self.hash_value(key) # 검색하는 키의 해시값 56 | p = self.table[hash] # 버킷을 주목 57 | 58 | for i in range(self.capacity): 59 | if p.stat == Status.EMPTY: 60 | break 61 | elif p.stat == Status.OCCUPIED and p.key == key: 62 | return p 63 | hash = self.rehash_value(hash) # 재해시 64 | p = self.table[hash] 65 | return None 66 | 67 | def search(self, key: Any) -> Any: 68 | """키가 key인 갖는 원소를 검색하여 값을 반환""" 69 | p = self.search_node(key) 70 | if p is not None: 71 | return p.value # 검색 성공 72 | else: 73 | return None # 검색 실패 74 | 75 | def add(self, key: Any, value: Any) -> bool: 76 | """키가 key이고 값이 value인 요소를 추가""" 77 | if self.search(key) is not None: 78 | return False # 이미 등록된 키 79 | 80 | hash = self.hash_value(key) # 추가하는 키의 해시값 81 | p = self.table[hash] # 버킷을 주목 82 | for i in range(self.capacity): 83 | if p.stat == Status.EMPTY or p.stat == Status.DELETED: 84 | self.table[hash] = Bucket(key, value, Status.OCCUPIED) 85 | return True 86 | hash = self.rehash_value(hash) # 재해시 87 | p = self.table[hash] 88 | return False # 해시 테이블이 가득 참 89 | 90 | def remove(self, key: Any) -> int: 91 | """키가 key인 갖는 요소를 삭제""" 92 | p = self.search_node(key) # 버킷을 주목 93 | if p is None: 94 | return False # 이 키는 등록되어 있지 않음 95 | p.set_status(Status.DELETED) 96 | return True 97 | 98 | def dump(self) -> None: 99 | """해시 테이블을 덤프""" 100 | for i in range(self.capacity): 101 | print(f'{i:2} ', end='') 102 | if self.table[i].stat == Status.OCCUPIED: 103 | print(f'{self.table[i].key} ({self.table[i].value})') 104 | elif self.table[i].stat == Status.EMPTY: 105 | print('-- 미등록 --') 106 | elif self.table[i] .stat == Status.DELETED: 107 | print('-- 삭제 완료 --') -------------------------------------------------------------------------------- /chap03/open_hash_test.py: -------------------------------------------------------------------------------- 1 | # [Do it! 실습 3-8] 오픈 주소법을 구현하는 해시 클래스 OpenHash 사용 2 | 3 | from enum import Enum 4 | from open_hash import OpenHash 5 | 6 | Menu = Enum('Menu', ['추가', '삭제', '검색', '덤프', '종료']) 7 | 8 | def select_menu() -> Menu: 9 | """메뉴 선택""" 10 | s = [f'({m.value}){m.name}' for m in Menu] 11 | while True: 12 | print(*s, sep = ' ', end='') 13 | n = int(input(': ')) 14 | if 1 <= n <= len(Menu): 15 | return Menu(n) 16 | 17 | hash = OpenHash(13) # 크기가 13인 해시 테이블 생성 18 | 19 | while True: 20 | menu = select_menu() # 메뉴 선택 21 | 22 | if menu == Menu.추가: # 추가 23 | key = int(input('추가할 키를 입력하세요.: ')) 24 | val = input('추가할 값을 입력하세요.: ') 25 | if not hash.add(key, val): 26 | print('추가를 실패했습니다!') 27 | 28 | elif menu == Menu.삭제: # 삭제 29 | key = int(input('삭제할 키를 입력하세요.: ')) 30 | if not hash.remove(key): 31 | print('삭제를 실패했습니다!') 32 | 33 | elif menu == Menu.검색: # 검색 34 | key = int(input('검색할 키를 입력하세요.: ')) 35 | t = hash.search(key) 36 | if t is not None: 37 | print(f'검색한 키를 갖는 값은 {t}입니다.') 38 | else: 39 | print('검색할 데이터가 없습니다.') 40 | 41 | elif menu == Menu.덤프: # 덤프 42 | hash.dump() 43 | 44 | else: # 종료 45 | break -------------------------------------------------------------------------------- /chap03/ssearch_for.py: -------------------------------------------------------------------------------- 1 | # [Do it! 실습 3-2] for 문으로 작성한 선형 검색 알고리즘 2 | 3 | from typing import Any, Sequence 4 | 5 | def seq_search(a: Sequence, key: Any) -> int: 6 | """시퀀스 a에서 key값이 같은 요소를 선형 검색(for 문)""" 7 | for i in range(len(a)): 8 | if a[i] == key: 9 | return i # 검색 성공(인덱스를 반환) 10 | return -1 # 검색 실패(-1을 반환) 11 | 12 | if __name__ == '__main__': 13 | num = int(input('원소 수를 입력하세요.: ')) # num 값을 입력 14 | x = [None] * num # 원소 수가 num인 배열을 생성 15 | 16 | for i in range(num): 17 | x[i] = int(input(f'x[{i}]: ')) 18 | 19 | ky = int(input('검색할 값을 입력하세요.: ')) # 검색할 키 ky를 입력받음 20 | 21 | idx = seq_search(x, ky) # ky와 값이 같은 요소를 x에서 검색 22 | 23 | if idx == -1: 24 | print('검색 값을 갖는 요소가 존재하지 않습니다.') 25 | else: 26 | print(f'검색 값은 x[{idx}]에 있습니다.') -------------------------------------------------------------------------------- /chap03/ssearch_sentinel.py: -------------------------------------------------------------------------------- 1 | # [Do it! 실습 3-3] 선형 검색 알고리즘(실습 3-1)을 보초법으로 수정 2 | 3 | from typing import Any, Sequence 4 | import copy 5 | 6 | def seq_search(seq: Sequence, key: Any) -> int: 7 | """시퀀스 seq에서 key와 일치하는 원소를 선형 검색(보초법)""" 8 | a = copy.deepcopy(seq) # seq를 복사 9 | a.append(key) # 보초 key를 추가 10 | 11 | i = 0 12 | while True: 13 | if a[i] == key: 14 | break # 검색에 성공하면 while 문을 종료 15 | i += 1 16 | return -1 if i == len(seq) else i 17 | 18 | if __name__ == '__main__': 19 | num = int(input('원소 수를 입력하세요.: ')) # num 값을 입력 20 | x = [None] * num # 원소 수가 num인 배열을 생성 21 | 22 | for i in range(num): 23 | x[i] = int(input(f'x[{i}]: ')) 24 | 25 | ky = int(input('검색할 값을 입력하세요.: ')) # 검색할 키 ky를 입력받기 26 | 27 | idx = seq_search(x, ky) # ky값과 같은 원소를 x에서 검색 28 | 29 | if idx == -1: 30 | print('검색값을 갖는 원소가 존재하지 않습니다.') 31 | else: 32 | print(f'검색값은 x[{idx}]에 있습니다.') -------------------------------------------------------------------------------- /chap03/ssearch_test1.py: -------------------------------------------------------------------------------- 1 | # [Do it! 실습 3C-1] seq_search() 함수를 사용하여 실수 검색하기 2 | from ssearch_while import seq_search 3 | 4 | print('실수를 검색합니다.') 5 | print('주의: "End"를 입력하면 종료합니다.') 6 | 7 | number = 0 8 | x = [] # 빈 리스트 x를 생성 9 | 10 | while True: 11 | s = input(f'x[{number}]: ') 12 | if s == 'End': 13 | break 14 | x.append(float(s)) # 배열 마지막에 원소를 추가 15 | number += 1 16 | 17 | ky = float(input('검색할 값을 입력하세요.: ')) # 검색할 키 ky를 입력 18 | 19 | idx = seq_search(x, ky) # ky와 같은 값의 원소를 x에서 검색 20 | if idx == -1: 21 | print('검색값을 갖는 원소가 존재하지 않습니다.') 22 | else: 23 | print(f'검색값은 x[{idx}]에 있습니다.') -------------------------------------------------------------------------------- /chap03/ssearch_test2.py: -------------------------------------------------------------------------------- 1 | # [Do it! 실습 3C-2] seq_search() 함수를 사용하여 특정 인덱스 검색하기 2 | 3 | from ssearch_while import seq_search 4 | 5 | t = (4, 7, 5.6, 2, 3.14, 1) 6 | s = 'string' 7 | a = ['DTS', 'AAC', 'FLAC'] 8 | 9 | print(f'{t}에서 5.6의 인덱스는 {seq_search(t, 5.6)}입니다.') 10 | print(f'{s}에서 "n"의 인덱스는 {seq_search(s, "n")}입니다.') 11 | print(f'{a}에서 "DTS"의 인덱스는 {seq_search(a, "DTS")}입니다.') -------------------------------------------------------------------------------- /chap03/ssearch_ve.py: -------------------------------------------------------------------------------- 1 | # 선형 검색 알고리즘(검색에 실패하면 ValueError를 보냄) 2 | 3 | from typing import Any, Sequence 4 | 5 | def seq_search(a: Sequence, key: Any) -> int: 6 | """시퀀스 a에서 key와 값이 같은 요소를 선형 검색(for 문)""" 7 | for i in range(len(a)): 8 | if a[i] == key: 9 | return i # 검색 성공(첨자를 반환) 10 | raise ValueError # 검색 실패 11 | 12 | if __name__ == '__main__': 13 | num = int(input('원소 수를 입력하세요.: ')) 14 | x = [None] * num # 원소 수가 num인 배열을 생성 15 | 16 | for i in range(num): 17 | x[i] = int(input(f'x[{i}]: ')) 18 | 19 | ky = int(input('검색할 값을 입력하세요.: ')) # 키 ky를 입력받음 20 | 21 | try: 22 | idx = seq_search(x, ky) # ky와 값이 같은 요소를 x에서 검색 23 | except ValueError: 24 | print('검색 값을 갖는 요소가 존재하지 않습니다.') 25 | else: 26 | print(f'검색 값은 x[{idx}]에 있습니다.') -------------------------------------------------------------------------------- /chap03/ssearch_while.py: -------------------------------------------------------------------------------- 1 | # [Do it! 실습 3-1] while 문으로 작성한 선형 검색 알고리즘 2 | 3 | from typing import Any, Sequence 4 | 5 | def seq_search(a: Sequence, key: Any) -> int: 6 | """시퀀스 a에서 key값이 같은 원소를 선형 검색(while 문)""" 7 | i = 0 8 | 9 | while True: 10 | if i == len(a): 11 | return -1 # 검색에 실패하여 -1을 반환 12 | if a[i] == key: 13 | return i # 검색에 성공하여 현재 조사한 배열의 인덱스를 반환 14 | i += 1 15 | 16 | if __name__ == '__main__': 17 | num = int(input('원소 수를 입력하세요.: ')) # num 값을 입력 18 | x = [None] * num # 원소 수가 num인 배열을 생성 19 | 20 | for i in range(num): 21 | x[i] = int(input(f'x[{i}]: ')) 22 | 23 | ky = int(input('검색할 값을 입력하세요.: ')) # 검색할 키 ky를 입력받기 24 | 25 | idx = seq_search(x, ky) # ky와 같은 원소를 x에서 검색 26 | 27 | if idx == -1: 28 | print('검색값을 갖는 원소가 존재하지 않습니다.') 29 | else: 30 | print(f'검색값은 x[{idx}]에 있습니다.') -------------------------------------------------------------------------------- /chap04/fixed_queue.py: -------------------------------------------------------------------------------- 1 | # 고정 길이 큐 클래스 FixedQueue 구현하기 2 | # Do it! 실습 4-3 [A] 3 | from typing import Any 4 | 5 | class FixedQueue: 6 | 7 | class Empty(Exception): 8 | """비어 있는 FixedQueue에 대해 deque 또는 peek를 호출할 때 내보내는 예외처리""" 9 | pass 10 | 11 | class Full(Exception): 12 | """가득 찬 FixedQueue에 enque를 호출할 때 내보내는 예외처리""" 13 | pass 14 | 15 | def __init__(self, capacity: int) -> None: 16 | """초기화 선언""" 17 | self.no = 0 # 현재 데이터 개수 18 | self.front = 0 # 맨앞 원소 커서 19 | self.rear = 0 # 맨끝 원소 커서 20 | self.capacity = capacity # 큐의 크기 21 | self.que = [None] * capacity # 큐의 본체 22 | 23 | def __len__(self) -> int: 24 | """큐에 있는 모든 데이터 개수를 반환""" 25 | return self.no 26 | 27 | def is_empty(self) -> bool: 28 | """큐가 비어 있는지 판단""" 29 | return self.no <= 0 30 | 31 | def is_full(self) -> bool: 32 | """큐가 가득 찼는지 판단""" 33 | return self.no >= self.capacity 34 | 35 | # Do it! 실습 4-3 [B] 36 | def enque(self, x: Any) -> None: 37 | """데이터 x를 인큐""" 38 | if self.is_full(): 39 | raise FixedQueue.Full # 큐가 가득 찬 경우 예외처리를 발생 40 | self.que[self.rear] = x 41 | self.rear += 1 42 | self.no += 1 43 | if self.rear == self.capacity: 44 | self.rear = 0 45 | 46 | # Do it! 실습 4-3 [C] 47 | def deque(self) -> Any: 48 | """데이터를 디큐합니다""" 49 | if self.is_empty(): 50 | raise FixedQueue.Empty # 큐가 비어 있는 경우 예외처리를 발생 51 | x = self.que[self.front] 52 | self.front += 1 53 | self.no -= 1 54 | if self.front == self.capacity: 55 | self.front = 0 56 | return x 57 | 58 | # Do it! 실습 4-3 [D] 59 | def peek(self) -> Any: 60 | """데이터를 피크합니다(맨 앞 데이터를 들여다 봄)""" 61 | if self.is_empty(): 62 | raise FixedQueue.Empty # 큐가 비어 있으면 예외처리를 발생 63 | return self.que[self.front] 64 | 65 | def find(self, value: Any) -> Any: 66 | """큐에서 value를 찾아 인덱스를 반환하고 없으면 -1을 반환합니다""" 67 | for i in range(self.no): 68 | idx = (i + self.front) % self.capacity 69 | if self.que[idx] == value: # 검색 성공 70 | return idx 71 | return -1 # 검색 실패 72 | 73 | def count(self, value: Any) -> bool: 74 | """큐에 포함되어 있는 value의 개수를 반환합니다""" 75 | c = 0 76 | for i in range(self.no): # 큐 데이터를 선형 검색 77 | idx = (i + self.front) % self.capacity 78 | if self.que[idx] == value: # 검색 성공 79 | c += 1 # 들어있음 80 | return c 81 | 82 | def __contains__(self, value: Any) -> bool: 83 | """큐에 value가 포함되어 있는지 판단합니다""" 84 | return self.count(value) 85 | 86 | def clear(self) -> None: 87 | """큐의 모든 데이터를 비웁니다""" 88 | self.no = self.front = self.rear = 0 89 | 90 | def dump(self) -> None: 91 | """모든 데이터를 맨 앞에서 맨 끝 순서로 출력합니다""" 92 | if self.is_empty(): # 큐가 비어 있으면 예외처리를 발생 93 | print('큐가 비어 있습니다.') 94 | else: 95 | for i in range(self.no): 96 | print(self.que[(i + self.front) % self.capacity], end=' ') 97 | print() -------------------------------------------------------------------------------- /chap04/fixed_queue_test.py: -------------------------------------------------------------------------------- 1 | # [Do it! 실습 4-4] 고정 길이 큐 클래스(FixedQueue)를 사용하기 2 | 3 | from enum import Enum 4 | from fixed_queue import FixedQueue 5 | 6 | Menu = Enum('Menu', ['인큐', '디큐', '피크', '검색', '덤프', '종료']) 7 | 8 | def select_menu() -> Menu: 9 | """메뉴 선택""" 10 | s = [f'({m.value}){m.name}' for m in Menu] 11 | while True: 12 | print(*s, sep=' ', end='') 13 | n = int(input(': ')) 14 | if 1 <= n <= len(Menu): 15 | return Menu(n) 16 | 17 | q = FixedQueue(64) # 최대 64개를 인큐할 수 있는 큐 생성(고정 길이) 18 | 19 | while True: 20 | print(f'현재 데이터 개수: {len(q)} / {q.capacity}') 21 | menu = select_menu() # 메뉴 선택 22 | 23 | if menu == Menu.인큐: # 인큐 24 | x = int(input('인큐할 데이터를 입력하세요.: ')) 25 | try: 26 | q.enque(x) 27 | except FixedQueue.Full: 28 | print('큐가 가득 찼습니다.') 29 | 30 | elif menu == Menu.디큐: # 디큐 31 | try: 32 | x = q.deque() 33 | print(f'디큐한 데이터는 {x}입니다.') 34 | except FixedQueue.Empty: 35 | print('큐가 비어 있습니다.') 36 | 37 | elif menu == Menu.피크: # 피크 38 | try: 39 | x = q.peek() 40 | print(f'피크한 데이터는 {x}입니다.') 41 | except FixedQueue.Empty: 42 | print('큐가 비었습니다.') 43 | 44 | elif menu == Menu.검색: # 검색 45 | x = int(input('검색할 값을 입력하세요.: ')) 46 | if x in q: 47 | print(f'{q.count(x)}개 포함되고, 맨 앞의 위치는 {q.find(x)}입니다.') 48 | else: 49 | print('검색값을 찾을 수 없습니다.') 50 | 51 | elif menu == Menu.덤프: # 덤프 52 | q.dump() 53 | else: 54 | break -------------------------------------------------------------------------------- /chap04/fixed_stack.py: -------------------------------------------------------------------------------- 1 | # 고정 길이 스택 클래스 FixedStack 구현하기 2 | # Do it! 실습 4-1 [A] 3 | from typing import Any 4 | 5 | class FixedStack: 6 | """고정 길이 스택 클래스""" 7 | 8 | class Empty(Exception): 9 | """비어 있는 FixedStack에 pop 또는 peek를 호출할 때 내보내는 예외 처리""" 10 | pass 11 | 12 | class Full(Exception): 13 | """가득 찬 FixedStack에 push를 호출할 때 내보내는 예외 처리""" 14 | pass 15 | 16 | def __init__(self, capacity: int = 256) -> None: 17 | """초기화""" 18 | self.stk = [None] * capacity # 스택 본체 19 | self.capacity = capacity # 스택의 크기 20 | self.ptr = 0 # 스택 포인터 21 | 22 | def __len__(self) -> int: 23 | """스택에 쌓여있는 데이터 개수를 반환""" 24 | return self.ptr 25 | 26 | def is_empty(self) -> bool: 27 | """스택이 비어 있는가?""" 28 | return self.ptr <= 0 29 | 30 | def is_full(self) -> bool: 31 | """스택은 가득 찼는가?""" 32 | return self.ptr >= self.capacity 33 | 34 | # Do it! 실습 4-1 [B] 35 | def push(self, value: Any) -> None: 36 | """스택에 value를 푸시""" 37 | if self.is_full(): # 스택이 가득 참 38 | raise FixedStack.Full 39 | self.stk[self.ptr] = value 40 | self.ptr += 1 41 | 42 | def pop(self) -> Any: 43 | """스택에서 데이터를 팝(꼭대기 데이터를 꺼냄)""" 44 | if self.is_empty(): # 스택이 비어 있음 45 | raise FixedStack.Empty 46 | self.ptr -= 1 47 | return self.stk[self.ptr] 48 | 49 | def peek(self) -> Any: 50 | """스택에서 데이터를 피크(꼭대기 데이터를 들여다 봄)""" 51 | if self.is_empty(): # 스택이 비어 있음 52 | raise FixedStack.Empty 53 | return self.stk[self.ptr - 1] 54 | 55 | def clear(self) -> None: 56 | """스택을 비움(모든 데이터를 삭제)""" 57 | self.ptr = 0 58 | 59 | # Do it! 실습 4-1 [C] 60 | def find(self, value: Any) -> Any: 61 | """스택에서 value를 찾아 첨자(없으면 -1)를 반환""" 62 | for i in range(self.ptr - 1, -1, -1): # 꼭대기 쪽부터 선형 검색 63 | if self.stk[i] == value: 64 | return i # 검색 성공 65 | return -1 # 검색 실패 66 | 67 | def count(self, value: Any) -> bool: 68 | """스택에 포함되어있는 value의 개수를 반환""" 69 | c = 0 70 | for i in range(self.ptr): # 바닥 쪽부터 선형 검색 71 | if self.stk[i] == value: 72 | c += 1 # 들어 있음 73 | return c 74 | 75 | def __contains__(self, value: Any) -> bool: 76 | """스택에 value가 있는가?""" 77 | return self.count(value) 78 | 79 | def dump(self) -> None: 80 | """덤프(스택 안의 모든 데이터를 바닥부터 꼭대기 순으로 출력)""" 81 | if self.is_empty(): # 스택이 비어 있음 82 | print('스택이 비어 있습니다.') 83 | else: 84 | print(self.stk[:self.ptr]) -------------------------------------------------------------------------------- /chap04/fixed_stack_test.py: -------------------------------------------------------------------------------- 1 | # [Do it! 실습 4-2] 고정 길이 스택 FixedStack의 사용하기 2 | 3 | from enum import Enum 4 | from fixed_stack import FixedStack 5 | 6 | Menu = Enum('Menu', ['푸시', '팝', '피크', '검색', '덤프', '종료']) 7 | 8 | def select_menu() -> Menu: 9 | """메뉴 선택""" 10 | s = [f'({m.value}){m.name}' for m in Menu] 11 | while True: 12 | print(*s, sep = ' ', end='') 13 | n = int(input(': ')) 14 | if 1 <= n <= len(Menu): 15 | return Menu(n) 16 | 17 | s = FixedStack(64) # 최대 64개를 푸시할 수 있는 스택 18 | 19 | while True: 20 | print(f'현재 데이터 개수: {len(s)} / {s.capacity}') 21 | menu = select_menu() # 메뉴 선택 22 | 23 | if menu == Menu.푸시: # 푸시 24 | x = int(input('데이터를 입력하세요.: ')) 25 | try: 26 | s.push(x) 27 | except FixedStack.Full: 28 | print('스택이 가득 차 있습니다.') 29 | 30 | elif menu == Menu.팝: # 팝 31 | try: 32 | x = s.pop() 33 | print(f'팝한 데이터는 {x}입니다.') 34 | except FixedStack.Empty: 35 | print('스택이 비어 있습니다.') 36 | 37 | elif menu == Menu.피크: # 피크 38 | try: 39 | x = s.peek() 40 | print(f'피크한 데이터는 {x}입니다.') 41 | except FixedStack.Empty: 42 | print('스택이 비어 있습니다.') 43 | 44 | elif menu == Menu.검색: # 검색 45 | x = int(input('검색할 값을 입력하세요.: ')) 46 | if x in s: 47 | print(f'{s.count(x)}개 포함되고, 맨 앞의 위치는 {s.find(x)}입니다.') 48 | else: 49 | print('검색값을 찾을 수 없습니다.') 50 | 51 | elif menu == Menu.덤프: # 덤프 52 | s.dump() 53 | 54 | else: 55 | break -------------------------------------------------------------------------------- /chap04/last_elements.py: -------------------------------------------------------------------------------- 1 | # [Do it! 실습 4C-2] 원하는 개수(n)만큼 값을 입력받아 마지막 n개를 저장 2 | 3 | n = int(input('정수를 몇 개 저장할까요? : ')) 4 | a = [None] * n # 입력 받은 값을 저장하는 배열 5 | 6 | cnt = 0 # 입력 받은 개수 7 | while True: 8 | a[cnt % n] = int(input((f'{cnt + 1} 번째 정수를 입력하세요.: '))) 9 | cnt += 1 10 | 11 | retry = input(f'계속 할까요?(Y ... Yes / N ... No) : ') 12 | if retry in {'N', 'n'}: 13 | break 14 | 15 | i = cnt - n 16 | if i < 0: i = 0 17 | 18 | while i < cnt: 19 | print(f'{i + 1}번째 = {a[i % n]}') 20 | i += 1 -------------------------------------------------------------------------------- /chap04/stack.py: -------------------------------------------------------------------------------- 1 | # [Do it! 4C-1] 고정 길이 스택 클래스 구현하기(collections.deque를 사용) 2 | 3 | from typing import Any 4 | from collections import deque 5 | 6 | class Stack: 7 | """고정 길이 스택 클래스(collections.deque를 사용)""" 8 | 9 | def __init__(self, maxlen: int = 256) -> None: 10 | """초기화 선언""" 11 | self.capacity = maxlen 12 | self.__stk = deque([], maxlen) 13 | 14 | def __len__(self) -> int: 15 | """스택에 쌓여있는 데이터 개수를 반환""" 16 | return len(self.__stk) 17 | 18 | def is_empty(self) -> bool: 19 | """스택이 비어 있는지 판단""" 20 | return not self.__stk 21 | 22 | def is_full(self) -> bool: 23 | """스택이 가득 찼는지 판단""" 24 | return len(self.__stk) == self.__stk.maxlen 25 | 26 | def push(self, value: Any) -> None: 27 | """스택에 value를 푸시""" 28 | self.__stk.append(value) 29 | 30 | def pop(self) -> Any: 31 | """스택에서 데이터를 팝""" 32 | return self.__stk.pop() 33 | 34 | def peek(self) -> Any: 35 | """스택에서 데이터를 피크""" 36 | return self.__stk[-1] 37 | 38 | def clear(self) -> None: 39 | """스택을 비웁니다""" 40 | self.__stk.clear() 41 | 42 | def find(self, value: Any) -> Any: 43 | """스택에서 value를 찾아 인덱스(없으면 -1)를 반환""" 44 | try: 45 | return self.__stk.index(value) 46 | except ValueError: 47 | return -1 48 | 49 | def count(self, value: Any) -> int: 50 | """스택에 포함된 value의 개수를 반환""" 51 | return self.__stk.count(value) 52 | 53 | def __contains__(self, value: Any) -> bool: 54 | """스택에 value가 포함되어 있는지 판단""" 55 | return self.count(value) 56 | 57 | def dump(self) -> int: 58 | """스택 안에 있는 모든 데이터를 나열""" 59 | print(list(self.__stk)) -------------------------------------------------------------------------------- /chap04/stack_test.py: -------------------------------------------------------------------------------- 1 | # [Do it! 4C-1] 고정 길이 스택 클래스(collections.deque)를 사용하기 2 | 3 | from enum import Enum 4 | from stack import Stack 5 | 6 | Menu = Enum('Menu', ['푸시', '팝', '피크', '검색', '덤프', '종료']) 7 | 8 | def select_menu() -> Menu: 9 | """메뉴 선택""" 10 | s = [f'({m.value}){m.name}' for m in Menu] 11 | while True: 12 | print(*s, sep=' ', end='') 13 | n = int(input(':')) 14 | if 1 <= n <= len(Menu): 15 | return Menu(n) 16 | 17 | s = Stack(64) # 최대 64 개를 푸시할 수 있는 스택 18 | 19 | while True: 20 | print(f'현재 데이터 개수:{len(s)} / {s.capacity}') 21 | menu = select_menu() # 메뉴 선택 22 | 23 | if menu == Menu.푸시: # 푸시 24 | x = int(input('데이터:')) 25 | try: 26 | s.push(x) 27 | except IndexError: 28 | print('스택이 가득 찼습니다.') 29 | 30 | elif menu == Menu.팝: # 팝 31 | try: 32 | x = s.pop() 33 | print(f'팝한 데이터는 {x}입니다.') 34 | except IndexError: 35 | print('스택이 비어 있습니다.') 36 | 37 | elif menu == Menu.피크: # 피크 38 | try: 39 | x = s.peek() 40 | print(f'피크한 데이터는 {x}입니다.') 41 | except IndexError: 42 | print('스택이 비어 있습니다.') 43 | 44 | elif menu == Menu.검색: # 검색 45 | x = int(input('검색 값을 입력하세요:')) 46 | if x in s: 47 | print(f'{s.count(x)} 개를 포함하고, 맨 앞쪽의 위치는 {s.find(x)}입니다.') 48 | else: 49 | print('검색 값은 포함되어 있지 않습니다.') 50 | 51 | elif menu == Menu.덤프: # 덤프 52 | s.dump() 53 | 54 | else: 55 | break -------------------------------------------------------------------------------- /chap05/8queen.py: -------------------------------------------------------------------------------- 1 | # [Do it! 실습 5-9] 8퀸 문제 알고리즘 구현하기 2 | 3 | pos = [0] * 8 # 각 열에 배치한 퀸의 위치 4 | flag_a = [False] * 8 # 각 행에 퀸을 배치했는지 체크 5 | flag_b = [False] * 15 # 대각선 방향(↙↗)으로 퀸을 배치했는지 체크 6 | flag_c = [False] * 15 # 대각선 방향( ↘↖)으로 퀸을 배치했는지 체크 7 | 8 | def put() -> None: 9 | """각 열에 배치한 퀸의 위치를 출력""" 10 | for i in range(8): 11 | print(f'{pos[i]:2}', end='') 12 | print() 13 | 14 | def set(i: int) -> None: 15 | """i 열의 알맞은 위치에 퀸을 배치""" 16 | for j in range(8): 17 | if( not flag_a[j] # j행에 퀸이 배치 되지 않았다면 18 | and not flag_b[i + j] # 대각선 방향(↙↗)으로 퀸이 배치 되지 않았다면 19 | and not flag_c[i - j + 7]): # 대각선 방향( ↘↖)으로 퀸이 배치 되지 않았다면 20 | pos[i] = j # 퀸을 j행에 배치 21 | if i == 7: # 모든 열에 퀸을 배치하는 것을 완료 22 | put() 23 | else: 24 | flag_a[j] = flag_b[i + j] = flag_c[i - j + 7] = True 25 | set(i + 1) # 다음 열에 퀸을 배치 26 | flag_a[j] = flag_b[i + j] = flag_c[i - j + 7] = False 27 | 28 | set(0) # 0열에 퀸을 배치 -------------------------------------------------------------------------------- /chap05/8queen2.py: -------------------------------------------------------------------------------- 1 | # [Do it! 실습 5-9] 8퀸 문제 알고리즘 구현하기(퀸을 놓는 상황을 네모로 표시) 2 | 3 | pos = [0] * 8 # 각 열에 배치한 퀸의 위치 4 | flag_a = [False] * 8 # 각 행에 퀸을 배치했는지 체크 5 | flag_b = [False] * 15 # 대각선 방향(↙↗)으로 퀸을 배치했는지 체크 6 | flag_c = [False] * 15 # 대각선 방향( ↘↖)으로 퀸을 배치했는지 체크 7 | 8 | def put() -> None: 9 | """퀸을 놓는 상황을 □와 ■로 출력""" 10 | for j in range(8): 11 | for i in range(8): 12 | print('■' if pos[i] == j else '□', end='') 13 | print() 14 | print() 15 | 16 | def set(i: int) -> None: 17 | """i 열의 알맞은 위치에 퀸을 놓기""" 18 | for j in range(8): 19 | if( not flag_a[j] # j 행에 아직 퀸을 놓지 않았으면 20 | and not flag_b[i + j] # 대각선 방향(↙↗)으로 퀸이 배치 되지 않았다면 21 | and not flag_c[i - j + 7]): # 대각선 방향( ↘↖)으로 퀸이 배치 되지 않았다면 22 | pos[i] = j # 퀸을 j 행에 놓기 23 | if i == 7: # 모든 열에 퀸을 배치하는 것을 완료 24 | put() 25 | else: 26 | flag_a[j] = flag_b[i + j] = flag_c[i - j + 7] = True 27 | set(i + 1) # 다음 열에 퀸을 놓기 28 | flag_a[j] = flag_b[i + j] = flag_c[i - j + 7] = False 29 | 30 | set(0) # 0 열에 퀸을 놓기 -------------------------------------------------------------------------------- /chap05/8queen_b.py: -------------------------------------------------------------------------------- 1 | # [Do it! 실습 5-7] 각 열에 1개 퀸을 배치한 조합을 재귀적으로 나열하기 2 | 3 | pos = [0] * 8 # 각 열에서 퀸의 위치를 출력 4 | 5 | def put() -> None: 6 | """각 열에 배치한 퀸의 위치를 출력""" 7 | for i in range(8): 8 | print(f'{pos[i]:2}', end='') 9 | print() 10 | 11 | def set(i: int) -> None: 12 | """i 열에 퀸을 배치""" 13 | for j in range(8): 14 | pos[i] = j # 퀸을 j행에 배치 15 | if i == 7 : # 모든 열에 배치를 종료 16 | put() 17 | else: 18 | set(i + 1) # 다음 열에 퀸을 배치 19 | 20 | set(0) # 0 열에 퀸을 배치 -------------------------------------------------------------------------------- /chap05/8queen_bb.py: -------------------------------------------------------------------------------- 1 | # [Do it! 실습 5-8] 행과 열에 퀸을 1개 배치하는 조합을 재귀적으로 나열하기 2 | 3 | pos = [0] * 8 # 각 열에서 퀸의 위치 4 | flag = [False] * 8 # 각 행에 퀸을 배치했는지 체크 5 | 6 | def put() -> None: 7 | """각 열에 놓은 퀸의 위치를 출력""" 8 | for i in range(8): 9 | print(f'{pos[i]:2}', end='') 10 | print() 11 | 12 | def set(i: int) -> None: 13 | """i 열의 알맞은 위치에 퀸을 배치""" 14 | for j in range(8): 15 | if not flag[j]: # j 행에 퀸을 배치하지 않았으면 16 | pos[i] = j # 퀸을 j 행에 배치 17 | if i == 7: # 모든 열에 퀸을 배치를 완료 18 | put() 19 | else: 20 | flag[j] = True 21 | set(i + 1) # 다음 열에 퀸을 배치 22 | flag[j] = False 23 | 24 | set(0) # 0열에 퀸을 배치 -------------------------------------------------------------------------------- /chap05/factorial.py: -------------------------------------------------------------------------------- 1 | # [Do it! 실습 5-1] 양의 정수인 팩토리얼 구하기 2 | 3 | def factorial(n: int) -> int: 4 | """양의 정수 n의 팩토리얼을 구하는 과정""" 5 | if n > 0: 6 | return n * factorial(n - 1) 7 | else: 8 | return 1 9 | 10 | if __name__ == '__main__': 11 | n = int(input('출력할 팩토리얼 값을 입력하세요.: ')) 12 | print(f'{n}의 팩토리얼은 {factorial(n)}입니다.') -------------------------------------------------------------------------------- /chap05/factorial_ve.py: -------------------------------------------------------------------------------- 1 | # [보충수업 5-1] 양의 정수인 팩토리얼 구하기(n이 음수면 ValueError 예외 처리 발생) 2 | 3 | def factorial(n : int) -> int: 4 | """양의 정수 n의 팩토리얼값을 재귀적으로 구함(n이 음수면 ValueError 예외 처리 발생)""" 5 | if n > 0: 6 | return n * factorial(n - 1) 7 | elif n == 0: 8 | return 1 9 | else: 10 | raise ValueError 11 | 12 | if __name__ == '__main__': 13 | n = int(input('출력할 팩토리얼 값을 입력하세요.: ')) 14 | try: 15 | print(f'{n}의 팩토리얼은 {factorial(n)}입니다.') 16 | except ValueError: 17 | print(f'{n}의 팩토리얼은 구할 수 없습니다.') -------------------------------------------------------------------------------- /chap05/gcd.py: -------------------------------------------------------------------------------- 1 | # [Do it! 실습 5-2] 유클리드 호제법으로 최대 공약수 구하기 2 | 3 | def gcd(x: int, y: int) -> int: 4 | """정숫값 x와 y의 최대 공약수를 반환""" 5 | if y == 0: 6 | return x 7 | else: 8 | return gcd(y, x % y) 9 | 10 | if __name__ == '__main__': 11 | print('두 정숫값의 최대 공약수를 구합니다.') 12 | x = int(input('첫 번째 정숫값을 입력하세요.: ')) 13 | y = int(input('두 번째 정숫값을 입력하세요.: ')) 14 | 15 | print(f'두 정숫값의 최대 공약수는 {gcd(x, y)}입니다.') -------------------------------------------------------------------------------- /chap05/hanoi.py: -------------------------------------------------------------------------------- 1 | # [Do it! 실습 5-6] 하노이의 탑 구현하기 2 | 3 | def move(no: int, x: int, y: int) -> None: 4 | """원반을 no개를 x 기둥에서 y 기둥으로 옮김""" 5 | if no > 1: 6 | move(no - 1, x, 6 - x - y) 7 | 8 | print(f'원반 [{no}]을(를) {x}기둥에서 {y}기둥으로 옮깁니다.') 9 | 10 | if no > 1: 11 | move(no - 1, 6 - x - y, y) 12 | 13 | print('하노이의 탑을 구현하는 프로그램입니다.') 14 | n = int(input('원반의 개수를 입력하세요.: ')) 15 | 16 | move(n, 1, 3) -------------------------------------------------------------------------------- /chap05/recur1.py: -------------------------------------------------------------------------------- 1 | # [Do it! 실습 5-3] 순수한 재귀 함수 구현하기 2 | 3 | def recur(n: int) -> int: 4 | """순수한 재귀 함수 recur의 구현""" 5 | if n > 0: 6 | recur(n - 1) 7 | print(n) 8 | recur(n - 2) 9 | 10 | x = int(input('정숫값을 입력하세요.: ')) 11 | 12 | recur(x) -------------------------------------------------------------------------------- /chap05/recur1a.py: -------------------------------------------------------------------------------- 1 | # [Do it! 실습 5-4] 재귀 함수의 구현(꼬리 재귀를 제거) 2 | 3 | def recur(n: int) -> int: 4 | """꼬리 재귀를 제거한 함수 recur""" 5 | while n > 0: 6 | recur(n - 1) 7 | print(n) 8 | n = n - 2 9 | x = int(input('정수값을 입력하세요.: ')) 10 | 11 | recur(x) -------------------------------------------------------------------------------- /chap05/recur1b.py: -------------------------------------------------------------------------------- 1 | # [Do it! 실습 5-5] 스택으로 재귀 함수 구현하기(재귀를 제거) 2 | 3 | from stack import Stack # stack.py의 Stack 클래스를 임포트 4 | 5 | def recur(n: int) -> int: 6 | """재귀를 제거한 함수 recur""" 7 | s = Stack(n) 8 | 9 | while True: 10 | if n > 0: 11 | s.push(n) # n 값을 푸시 12 | n = n - 1 13 | continue 14 | if not s.is_empty(): # 스택이 비어 있지 않으면 15 | n = s.pop() # 저장하고 있는 값을 n에 팝 16 | print(n) 17 | n = n - 2 18 | continue 19 | break 20 | 21 | x = int(input('정수값을 입력하세요.: ')) 22 | 23 | recur(x) -------------------------------------------------------------------------------- /chap05/recur2.py: -------------------------------------------------------------------------------- 1 | # recur 함수를 거꾸로 출력(Do it! 실습 5-3 수정) 2 | 3 | def recur(n: int) -> int: 4 | """순수한 재귀 함수 recur의 구현(거꾸로 출력)""" 5 | if n > 0: 6 | recur(n - 2) 7 | print(n) 8 | recur(n - 1) 9 | 10 | x = int(input('정숫값을 입력하세요.: ')) 11 | 12 | recur(x) -------------------------------------------------------------------------------- /chap05/stack.py: -------------------------------------------------------------------------------- 1 | # 고정 길이 스택 클래스(collections.deque을 사용) 2 | 3 | from typing import Any 4 | from collections import deque 5 | 6 | class Stack: 7 | """고정 길이 스택 클래스(collections.deque을 사용)""" 8 | 9 | def __init__(self, maxlen: int = 256) -> None: 10 | """초기화""" 11 | self.capacity = maxlen 12 | self.__stk = deque([], maxlen) 13 | 14 | def __len__(self) -> int: 15 | """스택에 쌓여있는 데이터 개수를 반환""" 16 | return len(self.__stk) 17 | 18 | def is_empty(self) -> bool: 19 | """스택이 비어 있습니까?""" 20 | return not self.__stk 21 | 22 | def is_full(self) -> bool: 23 | """스택이 가득 찼습니까?""" 24 | return len(self.__stk) == self.__stk.maxlen 25 | 26 | def push(self, value: Any) -> None: 27 | """스택에 value를 푸시""" 28 | self.__stk.append(value) 29 | 30 | def pop(self) -> Any: 31 | """스택에서 데이터를 팝(꼭대기의 데이터를 꺼냄)""" 32 | return self.__stk.pop() 33 | 34 | def peek(self) -> Any: 35 | """스택에서 데이터를 피크(꼭대기의 데이터를 들여다 봄)""" 36 | return self.__stk[-1] 37 | 38 | def clear(self) -> None: 39 | """스택을 비움(모든 데이터를 삭제)""" 40 | self.__stk.clear() 41 | 42 | def find(self, value: Any) -> Any: 43 | """스택에서 value를 찾아 첨자(없으면 -1)를 반환""" 44 | try: 45 | return self.__stk.index(value) 46 | except ValueError: 47 | return -1 48 | 49 | def count(self, value: Any) -> int: 50 | """스택에 포함된 value의 개수를 반환""" 51 | return self.__stk.count(value) 52 | 53 | def __contains__(self, value: Any) -> bool: 54 | """스택에 value가 포함되어 있는가?""" 55 | return self.count(value) 56 | 57 | def dump(self) -> int: 58 | """덤프(스택 안의 모든 데이터를 바닥 → 꼭대기 순서로 출력)""" 59 | print(list(self.__stk)) 60 | -------------------------------------------------------------------------------- /chap06/binary_insertion_sort.py: -------------------------------------------------------------------------------- 1 | # [Do it! 실습 6C-1] 이진 삽입 정렬 알고리즘 구현하기 2 | 3 | from typing import MutableSequence 4 | 5 | def binary_insertion_sort(a: MutableSequence) -> None: 6 | """이진 삽입 정렬""" 7 | n = len(a) 8 | for i in range(1, n): 9 | key = a[i] 10 | pl = 0 # 검색 범위의 맨 앞 원소 인덱스 11 | pr = i - 1 # 검색 범위의 맨 끝 원소 인덱스 12 | 13 | while True: 14 | pc = (pl + pr) // 2 # 검색 범위의 중앙 원소 인덱스 15 | if a[pc] == key: # 검색 성공 16 | break 17 | elif a[pc] < key: 18 | pl = pc + 1 19 | else: 20 | pr = pc - 1 21 | if pl > pr: 22 | break 23 | 24 | pd = pc + 1 if pl <= pr else pr + 1 # 삽입할 위치의 인덱스 25 | 26 | for j in range(i, pd, -1): 27 | a[j] = a[j - 1] 28 | a[pd] = key 29 | 30 | if __name__ == "__main__": 31 | print("이진 삽입 정렬을 수행합니다.") 32 | num = int(input("원소 수를 입력하세요.: ")) 33 | x = [None] * num # 원소 수가 num인 배열을 생성 34 | 35 | for i in range(num): 36 | x[i] = int(input(f"x[{i}]: ")) 37 | 38 | binary_insertion_sort(x) # 배열 x를 이진 삽입 정렬 39 | 40 | print("오름차순으로 정렬했습니다.") 41 | for i in range(num): 42 | print(f"x[{i}] = {x[i]}") -------------------------------------------------------------------------------- /chap06/binary_insort.py: -------------------------------------------------------------------------------- 1 | # [Do it! 실습 6C-2] 이진 삽입 정렬 알고리즘의 구현(bisect.insort 사용) 2 | 3 | from typing import MutableSequence 4 | import bisect 5 | 6 | def binary_insertion_sort(a: MutableSequence) -> None: 7 | """이진 삽입 정렬(bisect.insort을 사용)""" 8 | for i in range(1, len(a)): 9 | bisect.insort(a, a.pop(i), 0, i) 10 | 11 | if __name__ == '__main__': 12 | print('이진 삽입 정렬을 수행합니다.') 13 | num = int(input('원소 수를 입력하세요.: ')) 14 | x = [None] * num # 원소 수가 num인 배열을 생성 15 | 16 | for i in range(num): 17 | x[i] = int(input(f'x[{i}]: ')) 18 | 19 | binary_insertion_sort(x) # 배열 x를 이진 삽입 정렬 20 | 21 | print('오름차순으로 정렬했습니다.') 22 | for i in range(num): 23 | print(f'x[{i}] = {x[i]}') -------------------------------------------------------------------------------- /chap06/bubble_sort1.py: -------------------------------------------------------------------------------- 1 | # [Do it! 실습 6-1] 버블 정렬 알고리즘 구현하기 2 | 3 | from typing import MutableSequence 4 | 5 | def bubble_sort(a: MutableSequence) -> None: 6 | """버블 정렬""" 7 | n = len(a) 8 | for i in range(n - 1): 9 | for j in range(n - 1, i, -1): 10 | if a[j - 1] > a[j]: 11 | a[j - 1], a[j] = a[j], a[j - 1] 12 | 13 | if __name__ == '__main__': 14 | print('버블 정렬을 수행합니다.') 15 | num = int(input('원소 수를 입력하세요.: ')) 16 | x = [None] * num # 원소 수가 num인 배열을 생성 17 | 18 | for i in range(num): 19 | x[i] = int(input(f'x[{i}] : ')) 20 | 21 | bubble_sort(x) # 배열 x를 버블 정렬 22 | 23 | print('오름차순으로 정렬했습니다.') 24 | for i in range(num): 25 | print(f'x[{i}] = {x[i]}') -------------------------------------------------------------------------------- /chap06/bubble_sort1_verbose.py: -------------------------------------------------------------------------------- 1 | # [Do it! 실습 6-2] 버블 정렬 알고리즘 구현(정렬 과정을 출력) 2 | 3 | from typing import MutableSequence 4 | 5 | def bubble_sort_verbose(a: MutableSequence) -> None: 6 | """버블 정렬(정렬 과정을 출력)""" 7 | ccnt = 0 # 비교 횟수 8 | scnt = 0 # 교환 횟수 9 | n = len(a) 10 | for i in range(n - 1): 11 | print(f'패스 {i + 1}') 12 | for j in range(n - 1, i, -1): 13 | for m in range(0, n - 1): 14 | print(f'{a[m]:2}' + (' ' if m != j - 1 else 15 | ' +' if a[j - 1] > a[j] else ' -'), 16 | end='') 17 | print(f'{a[n - 1]:2}') 18 | ccnt += 1 19 | if a[j - 1] > a[j]: 20 | scnt += 1 21 | a[j - 1], a[j] = a[j], a[j - 1] 22 | for m in range(0, n - 1): 23 | print(f'{a[m]:2}', end=' ') 24 | print(f'{a[n - 1]:2}') 25 | print(f'비교를 {ccnt}번 했습니다.') 26 | print(f'교환을 {scnt}번 했습니다.') 27 | 28 | if __name__ == '__main__': 29 | print('버블 정렬을 수행합니다.') 30 | num = int(input('원소 수를 입력하세요.: ')) 31 | x = [None] * num # 원소 수가 num인 배열을 생성 32 | 33 | for i in range(num): 34 | x[i] = int(input(f'x[{i}]: ')) 35 | 36 | bubble_sort_verbose(x) # 배열 x를 버블 정렬 37 | 38 | print('오름차순으로 정렬했습니다.') 39 | for i in range(num): 40 | print(f'x[{i}] = {x[i]}') -------------------------------------------------------------------------------- /chap06/bubble_sort2.py: -------------------------------------------------------------------------------- 1 | # [Do it! 실습 6-3]버블 정렬 알고리즘 구현하기(알고리즘의 개선 1) 2 | 3 | from typing import MutableSequence 4 | 5 | def bubble_sort(a: MutableSequence) -> None: 6 | """버블 정렬(교환 횟수에 따른 중단)""" 7 | n = len(a) 8 | for i in range(n - 1): 9 | exchng = 0 # 패스에서 교환 횟수 10 | for j in range(n - 1, i, -1): 11 | if a[j - 1] > a[j]: 12 | a[j - 1], a[j] = a[j], a[j - 1] 13 | exchng += 1 14 | if exchng == 0: 15 | break 16 | 17 | if __name__ == '__main__': 18 | print('버블 정렬을 합니다.') 19 | num = int(input('원소 수를 입력하세요.: ')) 20 | x = [None] * num # 원소 수 num인 배열을 생성 21 | 22 | for i in range(num): 23 | x[i] = int(input(f'x[{i}]: ')) 24 | 25 | bubble_sort(x) # 배열 x를 버블 정렬 26 | 27 | print('오름차순으로 정렬했습니다.') 28 | for i in range(num): 29 | print(f'x[{i}] = {x[i]}') -------------------------------------------------------------------------------- /chap06/bubble_sort2_verbose.py: -------------------------------------------------------------------------------- 1 | # [Do it! 실습 6-3] 버블 정렬 알고리즘 구현하기(알고리즘의 개선 1) - 정렬 과정을 출력 2 | 3 | from typing import MutableSequence 4 | 5 | def bubble_sort2_verbose(a: MutableSequence) -> None: 6 | """버블 정렬(교환 횟수에 따른 중단)""" 7 | ccnt = 0 # 비교 횟수 8 | scnt = 0 # 교환 횟수 9 | n = len(a) 10 | for i in range(n - 1): 11 | print(f"패스 {i + 1}") 12 | exchng = 0 # 패스에서의 교환 횟수 13 | for j in range(n - 1, i, -1): 14 | for m in range(0, n - 1): 15 | print( 16 | f"{a[m]:2}" 17 | + (" " if m != j - 1 else " +" if a[j - 1] > a[j] else " -"), 18 | end="", 19 | ) 20 | print(f"{a[n - 1]:2}") 21 | ccnt += 1 22 | if a[j - 1] > a[j]: 23 | scnt += 1 24 | a[j - 1], a[j] = a[j], a[j - 1] 25 | exchng += 1 26 | for m in range(0, n - 1): 27 | print(f"{a[m]:2}", end=" ") 28 | print(f"{a[n - 1]:2}") 29 | if exchng == 0: # 교환이 수행되지 않았으면 작업을 중단 30 | break 31 | print(f"비교를 {ccnt}번 했습니다.") 32 | print(f"교환을 {scnt}번 했습니다.") 33 | 34 | if __name__ == "__main__": 35 | print("버블 정렬을 수행합니다") 36 | num = int(input("원소 수를 입력하세요.: ")) 37 | x = [None] * num # 원소 수가 num인 배열을 생성 38 | 39 | for i in range(num): 40 | x[i] = int(input(f"x[{i}]: ")) 41 | 42 | bubble_sort2_verbose(x) # 배열 x를 버블 정렬 43 | 44 | print("오름차순으로 정렬했습니다.") 45 | for i in range(num): 46 | print(f"x[{i}] = {x[i]}") -------------------------------------------------------------------------------- /chap06/bubble_sort3.py: -------------------------------------------------------------------------------- 1 | # [Do it! 실습 6-4] 버블 정렬 알고리즘 구현하기(알고리즘의 개선 2) 2 | 3 | from typing import MutableSequence 4 | 5 | def bubble_sort(a: MutableSequence) -> None: 6 | """버블 정렬(스캔 범위를 제한)""" 7 | n = len(a) 8 | k = 0 9 | while k < n - 1: 10 | last = n - 1 11 | for j in range(n - 1, k, -1): 12 | if a[j - 1] > a[j]: 13 | a[j - 1], a[j] = a[j], a[j - 1] 14 | last = j 15 | k = last 16 | 17 | if __name__ == '__main__': 18 | print('버블 정렬을 합니다.') 19 | num = int(input('원솟수를 입력하세요.: ')) 20 | x = [None] * num # 원솟수 num인 배열을 생성 21 | 22 | for i in range(num): 23 | x[i] = int(input(f'x[{i}] : ')) 24 | 25 | bubble_sort(x) # 배열 x를 버블 정렬 26 | 27 | print('오름차순으로 정렬했습니다.') 28 | for i in range(num): 29 | print(f'x[{i}] = {x[i]}') -------------------------------------------------------------------------------- /chap06/bubble_sort3_verbose.py: -------------------------------------------------------------------------------- 1 | # [Do it! 실습 6-4] 버블 정렬 알고리즘 구현하기(알고리즘의 개선 2) - 정렬 과정을 출력 2 | 3 | from typing import MutableSequence 4 | 5 | def bubble_sort3_verbose(a: MutableSequence) -> None: 6 | """버블 정렬(스캔 범위를 제한)""" 7 | ccnt = 0 # 비교 횟수 8 | scnt = 0 # 교환 횟수 9 | n = len(a) 10 | k = 0 11 | i = 0 12 | while k < n - 1: 13 | print(f'패스 {i + 1}') 14 | i += 1 15 | last = n - 1 16 | for j in range(n - 1, k, -1): 17 | for m in range(0, n - 1): 18 | print(f'{a[m]:2}' + (' ' if m != j - 1 else 19 | ' +' if a[j - 1] > a[j] else ' -'), 20 | end='') 21 | print(f'{a[n - 1]:2}') 22 | ccnt += 1 23 | if a[j - 1] > a[j]: 24 | scnt += 1 25 | a[j - 1], a[j] = a[j], a[j - 1] 26 | last = j 27 | k = last 28 | for m in range(0, n - 1): 29 | print(f'{a[m]:2}', end=' ') 30 | print(f'{a[n - 1]:2}') 31 | print(f'비교를 {ccnt}번 했습니다.') 32 | print(f'교환을 {scnt}번 했습니다.') 33 | 34 | if __name__ == '__main__': 35 | print('버블 정렬을 수행합니다') 36 | num = int(input('원소 수를 입력하세요.: ')) 37 | x = [None] * num # 원소 수 num인 배열을 생성 38 | 39 | for i in range(num): 40 | x[i] = int(input(f'x[{i}] : ')) 41 | 42 | bubble_sort3_verbose(x) # 배열 x를 버블 43 | 44 | print('오름차순으로 정렬했습니다.') 45 | for i in range(num): 46 | print(f'x[{i}] = {x[i]}') -------------------------------------------------------------------------------- /chap06/counting_sort.py: -------------------------------------------------------------------------------- 1 | # [Do it! 실습 6-17] 도수 정렬 알고리즘 구현하기 2 | 3 | from typing import MutableSequence 4 | 5 | def fsort(a: MutableSequence, max: int) -> None: 6 | """도수 정렬(배열 원솟값은 0 이상 max 이하)""" 7 | n = len(a) # 정렬할 배열 a 8 | f = [0] * (max + 1) # 누적 도수 분포표 배열 f 9 | b = [0] * n # 작업용 배열 b 10 | 11 | for i in range(n): f[a[i]] += 1 # [1단계] 12 | for i in range(1, max + 1): f[i] += f[i - 1] # [2단계] 13 | for i in range(n - 1, -1, -1): f[a[i]] -= 1; b[f[a[i]]] = a[i] # [3단계] 14 | for i in range(n): a[i] = b[i] # [4단계] 15 | 16 | def counting_sort(a: MutableSequence) -> None: 17 | """도수 정렬""" 18 | fsort(a, max(a)) 19 | 20 | if __name__ == '__main__': 21 | print('도수 정렬을 합니다.') 22 | num = int(input('원소 수를 입력하세요. : ')) 23 | x = [None] * num # 원소 수가 num인 배열을 생성 24 | 25 | for i in range(num): # 양수만 입력받음 26 | while True: 27 | x[i] = int(input(f'x[{i}] : ')) 28 | if x[i] >= 0: break 29 | 30 | counting_sort(x) # 배열 x를 도수 정렬 31 | 32 | print('오름차순으로 정렬했습니다.') 33 | for i in range(num): 34 | print(f'x[{i}] = {x[i]}') -------------------------------------------------------------------------------- /chap06/heap_sort.py: -------------------------------------------------------------------------------- 1 | # [Do it! 실습 6-16] 힙 정렬 알고리즘 구현하기 2 | 3 | from typing import MutableSequence 4 | 5 | def heap_sort(a: MutableSequence) -> None: 6 | """힙 정렬""" 7 | 8 | def down_heap(a: MutableSequence, left: int, right: int) -> None: 9 | """a[left] ~ a[right]를 힙으로 만들기""" 10 | temp = a[left] # 루트 11 | 12 | parent = left 13 | while parent < (right + 1) // 2: 14 | cl = parent * 2 + 1 # 왼쪽 자식 15 | cr = cl + 1 # 오른쪽 자식 16 | child = cr if cr <= right and a[cr] > a[cl] else cl # 큰 값을 선택합니다. 17 | if temp >= a[child]: 18 | break 19 | a[parent] = a[child] 20 | parent = child 21 | a[parent] = temp 22 | 23 | n = len(a) 24 | 25 | for i in range((n - 1) // 2, -1, -1): # a[i] ~ a[n-1]을 힙으로 만들기 26 | down_heap(a, i, n - 1) 27 | 28 | for i in range(n - 1, 0, -1): 29 | a[0], a[i] = a[i], a[0] # 최댓값인 a[0]과 마지막 원소 a[i]를 교환 30 | down_heap(a, 0, i - 1) # a[0] ~ a[i-1]을 힙으로 만들기 31 | 32 | if __name__ == '__main__': 33 | print('힙 정렬을 수행합니다.') 34 | num = int(input('원소 수를 입력하세요. : ')) 35 | x = [None] * num # 원소 수가 num인 배열을 생성 36 | 37 | for i in range(num): 38 | x[i] = int(input(f'x[{i}] : ')) 39 | 40 | heap_sort(x) # 배열 x를 힙 정렬 41 | 42 | print('오름차순으로 정렬했습니다.') 43 | for i in range(num): 44 | print(f'x[{i}] = {x[i]}') -------------------------------------------------------------------------------- /chap06/heapq_heap_sort.py: -------------------------------------------------------------------------------- 1 | # [Do it! 실습 6C-5] 힙 정렬 알고리즘 구현하기(heapq.push와 heapq.pop를 사용) 2 | 3 | import heapq 4 | from typing import MutableSequence 5 | 6 | def heap_sort(a: MutableSequence) -> None: 7 | """힙 정렬(heapq.push와 heapq.pop를 사용)""" 8 | 9 | heap = [] 10 | for i in a: 11 | heapq.heappush(heap, i) 12 | for i in range(len(a)): 13 | a[i] = heapq.heappop(heap) 14 | 15 | if __name__ == '__main__': 16 | print('힙 정렬을 수행합니다(heapq.push와 heapq.pop를 사용).') 17 | num = int(input('원소 수를 입력하세요. : ')) 18 | x = [None] * num # 원소 수가 num인 배열을 생성 19 | 20 | for i in range(num): 21 | x[i] = int(input(f'x[{i}] : ')) 22 | 23 | heap_sort(x) # 배열 x를 힙 정렬 24 | 25 | print('오름차순으로 정렬했습니다.') 26 | for i in range(num): 27 | print(f'x[{i}] = {x[i]}') -------------------------------------------------------------------------------- /chap06/heapq_merge.py: -------------------------------------------------------------------------------- 1 | # 정렬을 마친 두 배열의 병합 (heapq.merege 사용) 2 | 3 | import heapq 4 | 5 | a = [2, 1, 6, 8, 11, 13] 6 | b = [1, 2, 3, 4, 9, 16, 21] 7 | c = list(heapq.merge(a, b)) # 배열 a와 b를 병합하여 c에 저장 8 | 9 | print('배열 a와 b를 병합하여 배열 c에 저장하였습니다.') 10 | print(f'배열 a: {a}') 11 | print(f'배열 b: {b}') 12 | print(f'배열 c: {c}') -------------------------------------------------------------------------------- /chap06/heapq_merge_sort.py: -------------------------------------------------------------------------------- 1 | # 병합 정렬 알고리즘 구현하기(heapq.merge를 사용) 2 | 3 | from typing import MutableSequence 4 | import heapq 5 | 6 | def merge_sort(a: MutableSequence) -> None: 7 | """병합 정렬(heapq.merge를 사용)""" 8 | atype = type(a) 9 | 10 | def _merge_sort(a: MutableSequence, left: int, right: int) -> None: 11 | """a[left]~a[right]를 재귀적으로 병합 정렬""" 12 | if left < right: 13 | center = (left + right) // 2 14 | 15 | _merge_sort(a, left, center) # 앞부분 배열의 병합 정렬 16 | _merge_sort(a, center + 1, right) # 뒷부분 배열의 병합 정렬 17 | 18 | buff = atype(heapq.merge(a[left: center+1], a[center + 1: right+1])) 19 | for i in range(len(buff)): 20 | a[left + i] = buff[i] 21 | 22 | _merge_sort(a, 0, len(a)) # 배열 전체를 병합 정렬 23 | 24 | if __name__ == '__main__': 25 | print('병합 정렬을 수행합니다(heapq.merge를 사용).') 26 | num = int(input('원소 수를 입력하세요.: ')) 27 | x = [None] * num # 원소 수가 num인 배열을 생성 28 | 29 | for i in range(num): 30 | x[i] = int(input(f'x[{i}] : ')) 31 | 32 | merge_sort(x) # 배열 x를 병합 정렬 33 | 34 | print('오름차순으로 정렬했습니다.') 35 | for i in range(num): 36 | print(f'x[{i}] = {x[i]}') -------------------------------------------------------------------------------- /chap06/insertion_sort.py: -------------------------------------------------------------------------------- 1 | # [Do it! 실습 6-7] 단순 삽입 정렬 알고리즘 구현하기 2 | 3 | from typing import MutableSequence 4 | 5 | def insertion_sort(a: MutableSequence) -> None: 6 | """단순 삽입 정렬""" 7 | n = len(a) 8 | for i in range(1, n): 9 | j = i 10 | tmp = a[i] 11 | while j > 0 and a[j - 1] > tmp: 12 | a[j] = a[j - 1] 13 | j -= 1 14 | a[j] = tmp 15 | 16 | if __name__ == '__main__': 17 | print('단순 삽입 정렬을 수행합니다.') 18 | num = int(input('원소 수를 입력하세요.: ')) 19 | x = [None] * num # 원소 수가 num인 배열을 생성 20 | 21 | for i in range(num): 22 | x[i] = int(input(f'x[{i}]: ')) 23 | 24 | insertion_sort(x) # 배열 x를 단순 삽입 정렬 25 | 26 | print('오름차순으로 정렬했습니다.') 27 | for i in range(num): 28 | print(f'x[{i}] = {x[i]}') -------------------------------------------------------------------------------- /chap06/merge.py: -------------------------------------------------------------------------------- 1 | # [Do it! 실습 6-14] 정렬을 마친 두 배열을 병합하기 2 | 3 | from typing import Sequence, MutableSequence 4 | 5 | def merge_sorted_list(a: Sequence, b: Sequence, c: MutableSequence) -> None: 6 | """정렬을 마친 배열 a와 b를 병합하여 c에 저장""" 7 | pa, pb, pc = 0, 0, 0 # 각 배열의 커서 8 | na, nb, nc = len(a), len(b), len(c) # 각 배열의 원소수 9 | 10 | while pa < na and pb < nb: # pa와 pb를 비교하여 작은 값을 pc에 저장 11 | if a[pa] <= b[pb]: 12 | c[pc] = a[pa] 13 | pa += 1 14 | else: 15 | c[pc] = b[pb] 16 | pb += 1 17 | pc += 1 18 | 19 | while pa < na: # a에 남은 원소를 복사 20 | c[pc] = a[pa] 21 | pa += 1 22 | pc += 1 23 | 24 | while pb < nb: # b에 남은 원소를 복사 25 | c[pc] = b[pb] 26 | pb += 1 27 | pc += 1 28 | 29 | if __name__ == '__main__': 30 | a = [2, 4, 6, 8, 11, 13] 31 | b = [1, 2, 3, 4, 9, 16, 21] 32 | c = [None] * (len(a) + len(b)) 33 | print('정렬을 마친 두 배열의 병합을 수행합니다.') 34 | 35 | merge_sorted_list(a, b, c) # 배열 a와 b를 병합하여 c에 저장 36 | 37 | print('배열 a와 b를 병합하여 배열 c에 저장하였습니다.') 38 | print(f'배열 a: {a}') 39 | print(f'배열 b: {b}') 40 | print(f'배열 c: {c}') -------------------------------------------------------------------------------- /chap06/merge_sort.py: -------------------------------------------------------------------------------- 1 | # [Do it! 실습 6-15] 병합 정렬 알고리즘 구현하기 2 | 3 | from typing import MutableSequence 4 | 5 | def merge_sort(a: MutableSequence) -> None: 6 | """병합 정렬""" 7 | 8 | def _merge_sort(a: MutableSequence, left: int, right: int) -> None: 9 | """a[left] ~ a[right]를 재귀적으로 병합 정렬""" 10 | if left < right: 11 | center = (left + right) // 2 12 | 13 | _merge_sort(a, left, center) # 배열 앞부분을 병합 정렬 14 | _merge_sort(a, center + 1, right) # 배열 뒷부분을 병합 정렬 15 | 16 | p = j = 0 17 | i = k = left 18 | 19 | while i <= center: 20 | buff[p] = a[i] 21 | p += 1 22 | i += 1 23 | 24 | while i <= right and j < p: 25 | if buff[j] <= a[i]: 26 | a[k] = buff[j] 27 | j += 1 28 | else: 29 | a[k] = a[i] 30 | i += 1 31 | k += 1 32 | 33 | while j < p: 34 | a[k] = buff[j] 35 | k += 1 36 | j += 1 37 | 38 | n = len(a) 39 | buff = [None] * n # 작업용 배열을 생성 40 | _merge_sort(a, 0, n - 1) # 배열 전체를 병합 정렬 41 | del buff # 작업용 배열을 소멸 42 | 43 | if __name__ == '__main__': 44 | print('병합 정렬을 수행합니다') 45 | num = int(input('원소 수를 입력하세요.: ')) 46 | x = [None] * num # 원소 수가 num인 배열을 생성 47 | 48 | for i in range(num): 49 | x[i] = int(input(f'x[{i}]: ')) 50 | 51 | merge_sort(x) # 배열 x를 병합 정렬 52 | 53 | print('오름차순으로 정렬했습니다.') 54 | for i in range(num): 55 | print(f'x[{i}] = {x[i]}') -------------------------------------------------------------------------------- /chap06/partition.py: -------------------------------------------------------------------------------- 1 | # [Do it! 실습 6-10] 배열을 두 그룹으로 나누기 2 | 3 | from typing import MutableSequence 4 | 5 | def partition(a: MutableSequence) -> None: 6 | """배열을 분할하여 출력""" 7 | n = len(a) 8 | pl = 0 # 왼쪽 커서 9 | pr = n - 1 # 오른쪽 커서 10 | x = a[n // 2] # 피벗(가운데 원소) 11 | 12 | while pl <= pr: 13 | while a[pl] < x: pl += 1 14 | while a[pr] > x: pr -= 1 15 | if pl <= pr: 16 | a[pl], a[pr] = a[pr], a[pl] 17 | pl += 1 18 | pr -= 1 19 | 20 | print(f'피벗은 {x}입니다.') 21 | 22 | print('피벗 이하인 그룹입니다.') 23 | print(*a[0 : pl]) # a[0] ~ a[pl - 1] 24 | 25 | if pl > pr + 1: 26 | print('피벗과 일치하는 그룹입니다.') 27 | print(*a[pr + 1 : pl]) # a[pr + 1] ~ a[pl - 1] 28 | 29 | print('피벗 이상인 그룹입니다.') 30 | print(*a[pr + 1 : n]) # a[pr + 1] ~ a[n - 1] 31 | 32 | if __name__ == '__main__': 33 | print('배열을 나눕니다.') 34 | num = int(input('원소 수를 입력하세요.: ')) 35 | x = [None] * num # 원소 수가 num인 배열을 생성 36 | 37 | for i in range(num): 38 | x[i] = int(input(f'x[{i}]: ')) 39 | 40 | partition(x) # 배열 x를 나누어서 출력 -------------------------------------------------------------------------------- /chap06/quick_sort1.py: -------------------------------------------------------------------------------- 1 | # [Do it! 실습 6-10] 퀵 정렬 알고리즘 구현 2 | 3 | from typing import MutableSequence 4 | 5 | def qsort(a: MutableSequence, left: int, right: int) -> None: 6 | """a[left] ~ a[right]를 퀵 정렬""" 7 | pl = left # 왼쪽 커서 8 | pr = right # 오른쪽 커서 9 | x = a[(left + right) // 2] # 피벗(가운데 요소) 10 | 11 | while pl <= pr: # 실습 6-10과 같은 while 문 12 | while a[pl] < x: pl += 1 13 | while a[pr] > x: pr -= 1 14 | if pl <= pr: 15 | a[pl], a[pr] = a[pr], a[pl] 16 | pl += 1 17 | pr -= 1 18 | 19 | if left < pr: qsort(a, left, pr) 20 | if pl < right: qsort(a, pl, right) 21 | 22 | def quick_sort(a: MutableSequence) -> None: 23 | """퀵 정렬""" 24 | qsort(a, 0, len(a) - 1) 25 | 26 | if __name__ == '__main__': 27 | print('퀵 정렬을 수행합니다.') 28 | num = int(input('원소 수를 입력하세요.: ')) 29 | x = [None] * num # 원소 수가 num인 배열을 생성 30 | 31 | for i in range(num): 32 | x[i] = int(input(f'x[{i}]: ')) 33 | 34 | quick_sort(x) # 배열 x를 퀵 정렬 35 | 36 | print('오름차순으로 정렬했습니다.') 37 | for i in range(num): 38 | print(f'x[{i}] = {x[i]}') -------------------------------------------------------------------------------- /chap06/quick_sort1_non_recur.py: -------------------------------------------------------------------------------- 1 | # [Do it! 실습 6-12] 퀵 정렬 알고리즘 구현(비재귀적인 퀵 정렬) 2 | 3 | from stack import Stack # 실습 4C-1의 파일 import 4 | from typing import MutableSequence 5 | 6 | def qsort(a: MutableSequence, left: int, right: int) -> None: 7 | """a[left] ~ a [right]를 퀵 정렬(비재귀 버전)""" 8 | range = Stack(right - left + 1) # 스택 생성 9 | 10 | range.push((left, right)) 11 | 12 | while not range.is_empty(): 13 | pl, pr = left, right = range.pop() # 왼쪽, 오른쪽 커서를 꺼냄 14 | x = a[(left + right) // 2] # 피벗(중앙 요소) 15 | 16 | while pl <= pr: 17 | while a[pl] < x: pl += 1 18 | while a[pr] > x: pr -= 1 19 | if pl <= pr: # 실습 6-10, 실습 6-11과 같음 20 | a[pl], a[pr] = a[pr], a[pl] 21 | pl += 1 22 | pr -= 1 23 | 24 | if left < pr: range.push((left, pr)) # 왼쪽 그룹의 커서를 저장 25 | if pl < right: range.push((pl, right)) # 오른쪽 그룹의 커서를 저장 26 | 27 | def quick_sort(a: MutableSequence) -> None: 28 | """퀵 정렬""" 29 | qsort(a, 0, len(a) - 1) 30 | 31 | if __name__ == '__main__': 32 | print('비재귀적인 퀵 정렬') 33 | num = int(input('원소 수를 입력하세요.: ')) 34 | x = [None] * num # 원소 수가 num인 배열을 생성 35 | 36 | for i in range(num): 37 | x[i] = int(input(f'x[{i}]: ')) 38 | 39 | quick_sort(x) # 배열 x를 퀵 정렬 40 | 41 | print('오름차순으로 정렬했습니다.') 42 | for i in range(num): 43 | print(f'x[{i}] = {x[i]}') -------------------------------------------------------------------------------- /chap06/quick_sort1_verbose.py: -------------------------------------------------------------------------------- 1 | # [Do it! 실습 6C-3] 퀵 정렬 알고리즘 구현(배열을 나누는 과정 출력) 2 | 3 | from typing import MutableSequence 4 | 5 | def qsort(a: MutableSequence, left: int, right: int) -> None: 6 | """a[left] ~ a[right]를 퀵 정렬(배열을 나누는 과정 출력)""" 7 | pl = left # 왼쪽 커서 8 | pr = right # 오른쪽 커서 9 | x = a[(left + right) // 2] # 피벗(가운데 원소) 10 | 11 | print(f'a[{left}] ~ a[{right}]: ', *a[left : right + 1]) # 새로 추가된 부분 12 | 13 | while pl <= pr: 14 | while a[pl] < x: pl += 1 15 | while a[pr] > x: pr -= 1 16 | if pl <= pr: 17 | a[pl], a[pr] = a[pr], a[pl] 18 | pl += 1 19 | pr -= 1 20 | 21 | if left < pr: qsort(a, left, pr) 22 | if pl < right: qsort(a, pl, right) 23 | 24 | def quick_sort(a: MutableSequence) -> None: 25 | """퀵 정렬""" 26 | qsort(a, 0, len(a) - 1) 27 | 28 | if __name__ == '__main__': 29 | print('퀵 정렬을 수행합니다(배열을 나누는 과정 출력).') 30 | num = int(input('원소 수를 입력하세요.: ')) 31 | x = [None] * num # 원소 수가 num인 배열을 생성 32 | 33 | for i in range(num): 34 | x[i] = int(input(f'x[{i}]: ')) 35 | 36 | quick_sort(x) # 배열 x를 퀵 정렬 37 | 38 | print('오름차순으로 정렬했습니다.') 39 | for i in range(num): 40 | print(f'x[{i}] = {x[i]}') -------------------------------------------------------------------------------- /chap06/quick_sort2.py: -------------------------------------------------------------------------------- 1 | # [Do it! 실습 6-13] 퀵 정렬 알고리즘 구현하기(원소 수가 9개 미만인 경우 단순 삽입 정렬) 2 | 3 | from typing import MutableSequence 4 | 5 | def sort3(a: MutableSequence, idx1: int, idx2: int, idx3: int): 6 | """a[idx1], a[idx2], a[idx3]을 오름차순으로 정렬하고 가운데 값의 인덱스를 반환""" 7 | if a[idx2] < a[idx1]: a[idx2], a[idx1] = a[idx1], a[idx2] 8 | if a[idx3] < a[idx2]: a[idx3], a[idx2] = a[idx2], a[idx3] 9 | if a[idx2] < a[idx1]: a[idx2], a[idx1] = a[idx1], a[idx2] 10 | return idx2 11 | 12 | def insertion_sort(a: MutableSequence, left: int, right: int) -> None: 13 | """a[left] ~ a[right]를 단순 삽입 정렬""" 14 | for i in range(left + 1, right + 1): 15 | j = i 16 | tmp = a[i] 17 | while j > 0 and a[j - 1] > tmp: 18 | a[j] = a[j - 1] 19 | j -= 1 20 | a[j] = tmp 21 | 22 | def qsort(a: MutableSequence, left: int, right: int) -> None: 23 | """a[left] ~ a[right]를 퀵 정렬""" 24 | if right - left < 9: # 원소 수가 9개 미만이면 단순 삽입 정렬을 호출 25 | insertion_sort(a, left, right) 26 | else: # 원소 수가 9개 이상이면 퀵 정렬을 수행 27 | pl = left # 왼쪽 커서 28 | pr = right # 오른쪽 커서 29 | m = sort3(a, pl, (pl + pr) // 2, pr) 30 | x = a[m] 31 | 32 | a[m], a[pr - 1] = a[pr - 1], a[m] 33 | pl += 1 34 | pr -= 2 35 | while pl <= pr: 36 | while a[pl] < x: pl += 1 37 | while a[pr] > x: pr -= 1 38 | if pl <= pr: 39 | a[pl], a[pr] = a[pr], a[pl] 40 | pl += 1 41 | pr -= 1 42 | 43 | if left < pr: qsort(a, left, pr) 44 | if pl < right: qsort(a, pl, right) 45 | 46 | def quick_sort(a: MutableSequence) -> None: 47 | """퀵 정렬""" 48 | qsort(a, 0, len(a) - 1) 49 | 50 | if __name__ == '__main__': 51 | print('퀵 정렬을 합니다(원소 수가 9개 미만이면 단순 삽입 정렬).') 52 | num = int(input('원소 수를 입력하세요.: ')) 53 | x = [None] * num # 원소 수가 num인 배열을 생성 54 | 55 | for i in range(num): 56 | x[i] = int(input(f'x[{i}]: ')) 57 | 58 | quick_sort(x) # 배열 x를 퀵 정렬 59 | 60 | print('오름차순으로 정렬했습니다.') 61 | for i in range(num): 62 | print(f'x[{i}] = {x[i]}') -------------------------------------------------------------------------------- /chap06/selection_sort.py: -------------------------------------------------------------------------------- 1 | # [Do it! 실습 6-6] 단순 선택 정렬 알고리즘 구현 2 | 3 | from typing import MutableSequence 4 | 5 | def selection_sort(a: MutableSequence) -> None: 6 | """단순 선택 정렬""" 7 | n = len(a) 8 | for i in range(n - 1): 9 | min = i # 정렬 할 부분에서 가장 작은 원소의 인덱스 10 | for j in range(i + 1, n): 11 | if a[j] < a[min]: 12 | min = j 13 | a[i], a[min] = a[min], a[i] # 정렬 할 부분에서 맨 앞의 원소와 가장 작은 원소를 교환 14 | 15 | if __name__ == '__main__': 16 | print('단순 선택 정렬을 수행합니다.') 17 | num = int(input('원소 수를 입력하세요.: ')) 18 | x = [None] * num # 원소 수가 num인 배열을 생성 19 | 20 | for i in range(num): 21 | x[i] = int(input(f'x[{i}] : ')) 22 | 23 | selection_sort(x) # 배열 x를 단순 선택 정렬 24 | 25 | print('오름차순으로 정렬했습니다.') 26 | for i in range(num): 27 | print(f'x[{i}] = {x[i]}') -------------------------------------------------------------------------------- /chap06/shaker_sort.py: -------------------------------------------------------------------------------- 1 | # [Do it! 실습 6-5] 셰이커 정렬 알고리즘 구현하기 2 | 3 | from typing import MutableSequence 4 | 5 | def shaker_sort(a: MutableSequence) -> None: 6 | """셰이커 정렬""" 7 | left = 0 8 | right = len(a) - 1 9 | last = right 10 | while left < right: 11 | for j in range(right, left, -1): 12 | if a[j - 1] > a[j]: 13 | a[j - 1], a[j] = a[j], a[j - 1] 14 | last = j 15 | left = last 16 | 17 | for j in range(left, right): 18 | if a[j] > a[j + 1]: 19 | a[j], a[j + 1] = a[j + 1], a[j] 20 | last = j 21 | right = last 22 | 23 | if __name__ == '__main__': 24 | print('셰이커 정렬을 수행합니다') 25 | num = int(input('원소 수를 입력하세요.: ')) 26 | x = [None] * num # 원소 수가 num인 배열을 생성 27 | 28 | for i in range(num): 29 | x[i] = int(input(f'x[{i}] : ')) 30 | 31 | shaker_sort(x) # 배열 x를 단순 교환 정렬 32 | 33 | print('오름차순으로 정렬했습니다.') 34 | for i in range(num): 35 | print(f'x[{i}] = {x[i]}') -------------------------------------------------------------------------------- /chap06/shaker_sort_verbose.py: -------------------------------------------------------------------------------- 1 | # [Do it! 실습 6-5] 셰이커 정렬 알고리즘 구현하기(정렬 과정을 출력) 2 | 3 | from typing import MutableSequence 4 | 5 | def shaker_sort_verbose(a: MutableSequence) -> None: 6 | """"셰이커 정렬(정렬 과정을 출력)""" 7 | ccnt = 0 # 비교 횟수 8 | scnt = 0 # 교환 횟수 9 | left = 0 10 | n = len(a) 11 | right = len(a) - 1 12 | last = right 13 | i = 0 14 | while left < right: 15 | print(f'패스{i + 1}') 16 | i += 1 17 | for j in range(right, left, -1): 18 | for m in range(0, n - 1): 19 | print(f'{a[m]:2}' + (' ' if m != j - 1 else 20 | ' +' if a[j - 1] > a[j] else ' -'), 21 | end='') 22 | print(f'{a[n - 1]:2}') 23 | ccnt += 1 24 | if a[j - 1] > a[j]: 25 | scnt += 1 26 | a[j - 1], a[j] = a[j], a[j - 1] 27 | last = j 28 | left = last 29 | for m in range(0, n - 1): 30 | print(f'{a[m]:2}', end=' ') 31 | print(f'{a[n - 1]:2}') 32 | 33 | if (left == right): 34 | break 35 | print(f'패스 {i + 1}') 36 | i += 1 37 | for j in range(left, right): 38 | for m in range(0, n - 1): 39 | print(f'{a[m]:2}' + (' ' if m != j else 40 | ' +' if a[j] > a[j + 1] else ' -'), 41 | end='') 42 | print(f'{a[n - 1]:2}') 43 | ccnt += 1 44 | if a[j] > a[j + 1]: 45 | scnt += 1 46 | a[j], a[j + 1] = a[j + 1], a[j] 47 | last = j 48 | right = last 49 | for m in range(0, n - 1): 50 | print(f'{a[m]:2}', end=' ') 51 | print(f'{a[n - 1]:2}') 52 | print(f'비교를 {ccnt}번 했습니다.') 53 | print(f'교환을 {scnt}번 했습니다.') 54 | 55 | if __name__ == '__main__': 56 | print('셰이커 정렬을 수행합니다.') 57 | num = int(input('원소 수를 입력하세요.: ')) 58 | x = [None] * num # 원소 수 num인 배열을 생성 59 | 60 | for i in range(num): 61 | x[i] = int(input(f'x[{i}] : ')) 62 | 63 | shaker_sort_verbose(x) # 배열 x를 단순 교환 정렬 64 | 65 | print('오름차순으로 정렬했습니다.') 66 | for i in range(num): 67 | print(f'x[{i}] = {x[i]}') 68 | -------------------------------------------------------------------------------- /chap06/shell_sort1.py: -------------------------------------------------------------------------------- 1 | # [Do it! 실습 6-8] 셸 정렬 알고리즘 구현하기 2 | 3 | from typing import MutableSequence 4 | 5 | def shell_sort(a: MutableSequence) -> None: 6 | """셸 정렬""" 7 | n = len(a) 8 | h = n // 2 9 | while h > 0: 10 | for i in range(h, n): 11 | j = i - h 12 | tmp = a[i] 13 | while j >= 0 and a[j] > tmp: 14 | a[j + h] = a[j] 15 | j -= h 16 | a[j + h] = tmp 17 | h //= 2 18 | 19 | if __name__ == '__main__': 20 | print('셸 정렬을 수행합니다.') 21 | num = int(input('원소 수를 입력하세요.: ')) 22 | x = [None] * num # 원소 수가 num인 배열을 생성 23 | 24 | for i in range(num): 25 | x[i] = int(input(f'x[{i}]: ')) 26 | 27 | shell_sort(x) # 배열 x를 셸 정렬 28 | 29 | print('오름차순으로 정렬했습니다.') 30 | for i in range(num): 31 | print(f'x[{i}] = {x[i]}') -------------------------------------------------------------------------------- /chap06/shell_sort2.py: -------------------------------------------------------------------------------- 1 | # [Do it! 실습 6-9] 셸 정렬 알고리즘 구현하기(h * 3 + 1의 수열 사용) 2 | 3 | from typing import MutableSequence 4 | 5 | def shell_sort(a: MutableSequence) -> None: 6 | """셸 정렬(h * 3 + 1의 수열 사용)""" 7 | n = len(a) 8 | h = 1 9 | 10 | while h < n // 9: 11 | h = h * 3 + 1 12 | 13 | while h > 0: 14 | for i in range(h, n): 15 | j = i - h 16 | tmp = a[i] 17 | while j >= 0 and a[j] > tmp: 18 | a[j + h] = a[j] 19 | j -= h 20 | a[j + h] = tmp 21 | h //= 3 22 | 23 | if __name__ == '__main__': 24 | print('셸 정렬을 수행합니다(h * 3 + 1의 수열 사용).') 25 | num = int(input('원소 수를 입력하세요.: ')) 26 | x = [None] * num # 원소 수가 num인 배열을 생성 27 | 28 | for i in range(num): 29 | x[i] = int(input(f'x[{i}]: ')) 30 | 31 | shell_sort(x) # 배열 x를 셸 정렬 32 | 33 | print('오름차순으로 정렬했습니다.') 34 | for i in range(num): 35 | print(f'x[{i}] = {x[i]}') -------------------------------------------------------------------------------- /chap06/sorted_sort.py: -------------------------------------------------------------------------------- 1 | # [Do it! 실습 6C-4] sorted() 함수를 사용하여 정렬하기 2 | 3 | print('sorted() 함수를 사용하여 정렬합니다.') 4 | num = int(input('원소 수를 입력하세요.: ')) 5 | x = [None] * num # 원소 수가 num인 배열을 생성 6 | 7 | for i in range(num): 8 | x[i] = int(input(f'x[{i}]: ')) 9 | 10 | # 배열 x를 오름차순으로 정렬 11 | x = sorted(x) 12 | print('오름차순으로 정렬했습니다.') 13 | for i in range(num): 14 | print(f'x[{i}] = {x[i]}') 15 | 16 | # 배열 x를 내림차순으로 정렬 17 | x = sorted(x, reverse = True) 18 | print('내림차순으로 정렬했습니다.') 19 | for i in range(num): 20 | print(f'x[{i}] = {x[i]}') -------------------------------------------------------------------------------- /chap06/stack.py: -------------------------------------------------------------------------------- 1 | # 고정 길이 스택 클래스(collections.deque을 사용) 구현 예제 2 | 3 | from typing import Any 4 | from collections import deque 5 | 6 | class Stack: 7 | """고정 길이 스택 클래스(collections.deque을 사용)""" 8 | 9 | def __init__(self, maxlen: int = 256) -> None: 10 | """초기화 선언""" 11 | self.capacity = maxlen 12 | self.__stk = deque([], maxlen) 13 | 14 | def __len__(self) -> int: 15 | """스택에 쌓여있는 데이터 개수를 반환""" 16 | return len(self.__stk) 17 | 18 | def is_empty(self) -> bool: 19 | """스택이 비어 있는지 판단""" 20 | return not self.__stk 21 | 22 | def is_full(self) -> bool: 23 | """스택이 가득 찼는지 판단""" 24 | return len(self.__stk) == self.__stk.maxlen 25 | 26 | def push(self, value: Any) -> None: 27 | """스택에 value를 푸시""" 28 | self.__stk.append(value) 29 | 30 | def pop(self) -> Any: 31 | """스택에서 데이터를 팝""" 32 | return self.__stk.pop() 33 | 34 | def peek(self) -> Any: 35 | """스택에서 데이터를 피크""" 36 | return self.__stk[-1] 37 | 38 | def clear(self) -> None: 39 | """스택을 비웁니다""" 40 | self.__stk.clear() 41 | 42 | def find(self, value: Any) -> Any: 43 | """스택에서 value를 찾아 인덱스(없으면 -1)를 반환""" 44 | try: 45 | return self.__stk.index(value) 46 | except ValueError: 47 | return -1 48 | 49 | def count(self, value: Any) -> int: 50 | """스택에 포함된 value의 개수를 반환""" 51 | return self.__stk.count(value) 52 | 53 | def __contains__(self, value: Any) -> bool: 54 | """스택에 value가 포함되어 있는지 판단""" 55 | return self.count(value) 56 | 57 | def dump(self) -> int: 58 | """스택 안에 있는 모든 데이터를 나열""" 59 | print(list(self.__stk)) -------------------------------------------------------------------------------- /chap07/bf_match.py: -------------------------------------------------------------------------------- 1 | # [Do it! 실습 7-1] 브루트 포스법으로 문자열 검색하기 2 | 3 | def bf_match(txt: str, pat: str) -> int: 4 | """브루트 포스법으로 문자열 검색""" 5 | pt = 0 # txt(텍스트)를 따라가는 커서 6 | pp = 0 # pat(패턴)를 따라가는 커서 7 | 8 | while pt != len(txt) and pp != len(pat): 9 | if txt[pt] == pat[pp]: 10 | pt += 1 11 | pp += 1 12 | else: 13 | pt = pt - pp + 1 14 | pp = 0 15 | 16 | return pt - pp if pp == len(pat) else -1 17 | 18 | if __name__ == '__main__': 19 | s1 = input('텍스트를 입력하세요.: ') # 텍스트용 문자열 20 | s2 = input('패턴을 입력하세요.: ') # 패턴용 문자열 21 | 22 | idx = bf_match(s1, s2) # 문자열 s1~s2를 브루트 포스법으로 검색 23 | 24 | if idx == -1: 25 | print('텍스트 안에 패턴이 존재하지 않습니다.') 26 | else: 27 | print(f'{(idx + 1)}번째 문자에서 일치합니다.') -------------------------------------------------------------------------------- /chap07/bm_match.py: -------------------------------------------------------------------------------- 1 | # [Do it! 실습 7-3] 보이어 무어법으로 문자열 검색하기(0~255 문자) 2 | 3 | def bm_match(txt: str, pat: str) -> int: 4 | """보이어 무어법에 의한 문자열 검색""" 5 | skip = [None] * 256 # 건너뛰기 표 6 | 7 | # 건너뛰기 표 만들기 8 | for pt in range(256): 9 | skip[pt] = len(pat) 10 | for pt in range(len(pat)): 11 | skip[ord(pat[pt])] = len(pat) - pt - 1 12 | 13 | # 검색하기 14 | while pt < len(txt): 15 | pp = len(pat) - 1 16 | while txt[pt] == pat[pp]: 17 | if pp == 0: 18 | return pt 19 | pt -= 1 20 | pp -= 1 21 | pt += skip[ord(txt[pt])] if skip[ord(txt[pt])] > len(pat) - pp \ 22 | else len(pat) - pp 23 | 24 | return -1 25 | 26 | if __name__ == '__main__': 27 | s1 = input('텍스트를 입력하세요.: ') # 텍스트 문자열 28 | s2 = input('패턴을 입력하세요.: ') # 패턴 문자열 29 | 30 | idx = bm_match(s1, s2) # 문자열 s1~s2를 KMP법으로 검색 31 | 32 | if idx == -1: 33 | print('텍스트 안에 패턴이 존재하지 않습니다.') 34 | else: 35 | print(f'{(idx + 1)}번째 문자에서 일치합니다.') -------------------------------------------------------------------------------- /chap07/find.py: -------------------------------------------------------------------------------- 1 | # 문자열에 포함되어 있는 문자열을 검색(find 계열 함수) 2 | 3 | txt = input('문자열 txt: ') # 문자열 나열 4 | ptn = input('문자열 ptn: ') # 검색할 문자 5 | 6 | c = txt.count(ptn) 7 | 8 | if c == 0: # 포함된 문자가 없음 9 | print('ptn은 txt에 포함되어 있지 않습니다.') 10 | elif c == 1: # 포함된 문자가 1개만 있는 경우 11 | print('ptn이 txt에 포함되어 있는 인덱스: ', txt.find(ptn)) 12 | else: # 포함된 문자가 2개 이상 있는 경우 13 | print('ptn이 txt에 포함되어 있는 맨 앞 인덱스: ', txt.find(ptn)) 14 | print('ptn이 txt에 포함되어 있는 맨 끝 인덱스: ', txt.rfind(ptn)) -------------------------------------------------------------------------------- /chap07/index.py: -------------------------------------------------------------------------------- 1 | # 문자열에 포함되어 있는 문자열을 검색(index 계열 함수) 2 | 3 | txt = input('문자열 txt: ') 4 | ptn = input('문자열 ptn: ') 5 | 6 | c = txt.count(ptn) 7 | 8 | if c == 0: # 포함된 문자가 없음 9 | print('ptn은 txt에 포함되어 있지 않습니다.') 10 | elif c == 1: # 포함된 문자가 1개만 있는 경우 11 | print('ptn이 txt에 포함되어 있는 인덱스: ', txt.index(ptn)) 12 | else: # 포함된 문자가 2개 이상 있는 경우 13 | print('ptn이 txt에 포함되어 있는 맨 앞 인덱스: ', txt.index(ptn)) 14 | print('ptn이 txt에 포함되어 있는 맨 끝 인덱스: ', txt.rindex(ptn)) -------------------------------------------------------------------------------- /chap07/kmp_match.py: -------------------------------------------------------------------------------- 1 | # [Do it! 실습 7-2] KMP법으로 문자열 검색하기 2 | 3 | def kmp_match(txt: str, pat: str) -> int: 4 | """KMP법에 의한 문자열 검색""" 5 | pt = 1 # txt를 따라가는 커서 6 | pp = 0 # pat를 따라가는 커서 7 | skip = [0] * (len(pat) + 1) # 건너뛰기 표 8 | 9 | # 건너뛰기 표 만들기 10 | skip[pt] = 0 11 | while pt != len(pat): 12 | if pat[pt] == pat[pp]: 13 | pt += 1 14 | pp += 1 15 | skip[pt] = pp 16 | elif pp == 0: 17 | pt += 1 18 | skip[pt] = pp 19 | else: 20 | pp = skip[pp] 21 | 22 | # 검색하기 23 | pt = pp = 0 24 | while pt != len(txt) and pp != len(pat): 25 | if txt[pt] == pat[pp]: 26 | pt += 1 27 | pp += 1 28 | elif pp == 0: 29 | pt += 1 30 | else: 31 | pp = skip[pp] 32 | 33 | return pt - pp if pp == len(pat) else -1 34 | 35 | if __name__ == '__main__': 36 | s1 = input('텍스트를 입력하세요.: ') # 텍스트용 문자열 37 | s2 = input('패턴을 입력하세요.: ') # 패턴용 문자열 38 | 39 | idx = kmp_match(s1, s2) # 문자열 s1~s2를 KMP법으로 검색 40 | 41 | if idx == -1: 42 | print('텍스트 안에 패턴이 존재하지 않습니다.') 43 | else: 44 | print(f'{(idx + 1)}번째 문자에서 일치합니다.') -------------------------------------------------------------------------------- /chap08/array_list.py: -------------------------------------------------------------------------------- 1 | # [Do it! 실습 8-3] 커서로 선형 리스트 만들기 2 | 3 | from __future__ import annotations 4 | from typing import Any, Type 5 | 6 | Null = -1 7 | 8 | class Node: 9 | """선형 리스트용 노드 클래스(배열 커서 버전)""" 10 | 11 | def __init__(self, data = Null, next = Null, dnext = Null): 12 | """초기화""" 13 | self.data = data # 데이터 14 | self.next = next # 리스트의 뒤쪽 포인터 15 | self.dnext = dnext # 프리 리스트의 뒤쪽 포인터 16 | 17 | class ArrayLinkedList: 18 | """선형 리스트 클래스(배열 커서 버전)""" 19 | 20 | def __init__(self, capacity: int): 21 | """초기화""" 22 | self.head = Null # 머리 노드 23 | self.current = Null # 주목 노드 24 | self.max = Null # 사용 중인 맨끝 레코드 25 | self.deleted = Null # 프리 리스트의 머리 노드 26 | self.capacity = capacity # 리스트의 크기 27 | self.n = [Node()] * self.capacity # 리스트 본체 28 | self.no = 0 29 | 30 | 31 | def __len__(self) -> int: 32 | """선형 리스트의 노드 수를 반환""" 33 | return self.no 34 | 35 | def get_insert_index(self): 36 | """다음에 삽입할 레코드의 첨자를 구합니다""" 37 | if self.deleted == Null: # 삭제 레코드는 존재하지 않습니다 38 | if self.max+1 < self.capacity: 39 | self.max += 1 40 | return self.max # 새 레코드를 사용 41 | else: 42 | return Null # 크기 초과 43 | else: 44 | rec = self.deleted # 프리 리스트에서 45 | self.deleted = self.n[rec].dnext # 맨 앞 rec를 꺼내기 46 | return rec 47 | 48 | def delete_index(self, idx: int) -> None: 49 | """레코드 idx를 프리 리스트에 등록""" 50 | if self.deleted == Null: # 삭제 레코드는 존재하지 않습니다 51 | self.deleted = idx # idx를 프리 리스트의 52 | self.n[idx].dnext = Null # 맨 앞에 등록 53 | else: 54 | rec = self.deleted # idx를 프리 리스트의 55 | self.deleted = idx # 맨 앞에 삽입 56 | self.n[idx].dnext = rec 57 | 58 | def search(self, data: Any) -> int: 59 | """data와 값이 같은 노드를 검색""" 60 | cnt = 0 61 | ptr = self.head # 현재 스캔 중인 노드 62 | while ptr != Null: 63 | if self.n[ptr].data == data: 64 | self.current = ptr 65 | return cnt # 검색 성공 66 | cnt += 1 67 | ptr = self.n[ptr].next # 뒤쪽 노드에 주목 68 | return Null # 검색 실패 69 | 70 | def __contains__(self, data: Any) -> bool: 71 | """선형 리스트에 data가 포함되어 있는지 확인""" 72 | return self.search(data) >= 0 73 | 74 | def add_first(self, data: Any): 75 | """머리 노드에 삽입""" 76 | ptr = self.head # 삽입하기 전의 머리 노드 77 | rec = self.get_insert_index() 78 | if rec != Null: 79 | self.head = self.current = rec # rec번째 레코드에 삽입 80 | self.n[self.head] = Node(data, ptr) 81 | self.no += 1 82 | 83 | def add_last(self, data: Any) -> None: 84 | """꼬리 노드에 삽입""" 85 | if self.head == Null: # 리스트가 비어 있으면 86 | self.add_first(data) # 맨 앞에 노드 삽입 87 | else: 88 | ptr = self.head 89 | while self.n[ptr].next != Null: 90 | ptr = self.n[ptr].next 91 | rec = self.get_insert_index() 92 | 93 | if rec != Null: # rec번째 레코드에 삽입 94 | self.n[ptr].next = self.current = rec 95 | self.n[rec] = Node(data) 96 | self.no += 1 97 | 98 | def remove_first(self) -> None: 99 | """머리 노드를 삭제""" 100 | if self.head != Null: # 리스트가 비어 있으면 101 | ptr = self.n[self.head].next 102 | self.delete_index(self.head) 103 | self.head = self.current = ptr 104 | self.no -= 1 105 | 106 | def remove_last(self) -> None: 107 | """꼬리 노드를 삭제""" 108 | if self.head != Null: 109 | if self.n[self.head].next == Null: # 노드가 1개 뿐이면 110 | self.remove_first() # 머리 노드를 삭제 111 | else: 112 | ptr = self.head # 스캔 중인 노드 113 | pre = self.head # 스캔 중인 노드의 앞쪽 노드 114 | 115 | while self.n[ptr].next != Null: 116 | pre = ptr 117 | ptr = self.n[ptr].next 118 | self.n[pre].next = Null # pre는 삭제한 뒤의 꼬리 노드 119 | self.delete_index(ptr) 120 | self.current = pre 121 | self.no -= 1 122 | 123 | def remove(self, p: int) -> None: 124 | """레코드 p를 삭제""" 125 | if self.head != Null: 126 | if p == self.head: # p가 머리 노드면 127 | self.remove_first() # 머리 노드를 삭제 128 | else: 129 | ptr = self.head 130 | 131 | while self.n[ptr].next != p: 132 | ptr = self.n[ptr].next 133 | if ptr == Null: 134 | return # p는 리스트에 존재하지 않음 135 | #self.n[ptr].next = Null 136 | self.delete_index(p) 137 | self.n[ptr].next = self.n[p].next 138 | self.current = ptr 139 | self.no -= 1 140 | 141 | def remove_current_node(self) -> None: 142 | """주목 노드를 삭제""" 143 | self.remove(self.current) 144 | 145 | def clear(self) -> None: 146 | """모든 노드를 삭제""" 147 | while self.head != Null: # 리스트 전체가 빌 때까지 148 | self.remove_first() # 머리 노드를 삭제 149 | self.current = Null 150 | 151 | def next(self) -> bool: 152 | """주목 노드를 한 칸 뒤로 진행""" 153 | if self.current == Null or self.n[self.current].next == Null: 154 | return False # 진행할 수 없음 155 | self.current = self.n[self.current].next 156 | return True 157 | 158 | 159 | def print_current_node(self) -> None: 160 | """주목 노드를 출력""" 161 | if self.current == Null: 162 | print('주목 노드가 없습니다.') 163 | else: 164 | print(self.n[self.current].data) 165 | 166 | def print(self) -> None: 167 | """모든 노드를 출력""" 168 | ptr = self.head 169 | 170 | while ptr != Null: 171 | print(self.n[ptr].data) 172 | ptr = self.n[ptr].next 173 | 174 | def dump(self) -> None: 175 | """배열을 덤프""" 176 | for i in self.n: 177 | print(f'[{i}] {i.data} {i.next} {i.dnext}') 178 | 179 | def __iter__(self) -> ArrayLinkedListIterator: 180 | """이터레이터를 반환""" 181 | return ArrayLinkedListIterator(self.n, self.head) 182 | 183 | class ArrayLinkedListIterator: 184 | """클래스 ArrayLinkedList의 이터레이터용 클래스""" 185 | 186 | def __init__(self, n: int, head: int): 187 | self.n = n 188 | self.current = head 189 | 190 | def __iter__(self) -> ArrayLinkedListIterator: 191 | return self 192 | 193 | def __next__(self) -> Any: 194 | if self.current == Null: 195 | raise StopIteration 196 | else: 197 | data = self.n[self.current].data 198 | self.current = self.n[self.current].next 199 | return data -------------------------------------------------------------------------------- /chap08/array_list_test.py: -------------------------------------------------------------------------------- 1 | # [Do it! 실습 8-3] 커서를 이용한 선형 리스트 클래스 ArrayLinkedList 사용하기 2 | 3 | from enum import Enum 4 | from array_list import ArrayLinkedList 5 | 6 | Menu = Enum('Menu', ['머리에노드삽입', '꼬리에노드삽입', '머리노드삭제', 7 | '꼬리노드삭제', '주목노드출력', '주목노드이동', 8 | '주목노드삭제', '모든노드삭제', '검색', '멤버십판단', 9 | '모든노드출력', '스캔', '종료']) 10 | 11 | def select_Menu() -> Menu: 12 | """메뉴 선택""" 13 | s = [f'({m.value}){m.name}' for m in Menu] 14 | while True: 15 | print(*s, sep = ' ', end='') 16 | n = int(input(' : ')) 17 | if 1 <= n <= len(Menu): 18 | return Menu(n) 19 | 20 | lst = ArrayLinkedList(100) # 선형 리스트를 생성 21 | 22 | while True: 23 | menu = select_Menu() # 메뉴 선택 24 | 25 | if menu == Menu.머리에노드삽입: # 맨 앞에 노드 삽입 26 | lst.add_first(int(input('머리 노드에 넣을 값을 입력하세요.: '))) 27 | 28 | elif menu == Menu.꼬리에노드삽입: # 맨 끝에 노드 삽입 29 | lst.add_last(int(input('꼬리 노드에 넣을 값을 입력하세요.: '))) 30 | 31 | elif menu == Menu.머리노드삭제: # 맨 앞 노드를 삭제 32 | lst.remove_first() 33 | 34 | elif menu == Menu.꼬리노드삭제: # 맨 끝 노드를 삭제 35 | lst.remove_last() 36 | 37 | elif menu == Menu.주목노드출력: # 주목 노드를 출력 38 | lst.print_current_node() 39 | 40 | elif menu == Menu.주목노드이동: # 주목 노드를 한 칸 뒤로 이동 41 | lst.next() 42 | 43 | elif menu == Menu.주목노드삭제: # 주목 노드를 삭제 44 | lst.remove_current_node() 45 | 46 | elif menu == Menu.모든노드삭제: # 모두 삭제 47 | lst.clear() 48 | 49 | elif menu == Menu.검색: # 검색 50 | pos = lst.search(int(input('검색할 값을 입력하세요.: '))) 51 | if pos >= 0: 52 | print(f'이 키를 갖는 데이터는 {pos + 1}번째에 있습니다.') 53 | else: 54 | print('해당 데이터가 없습니다.') 55 | 56 | elif menu == Menu.멤버십판단: # 멤버십을 판단 57 | print('그 값의 데이터는 포함되어' 58 | +('있습니다.' if int(input('판단할 값을 입력하세요.')) in lst else ' 있지 않습니다.')) 59 | 60 | elif menu == Menu.모든노드출력: # 모든 노드를 출력 61 | lst.print() 62 | 63 | elif menu == Menu.스캔: # 모든 노드 스캔 64 | for e in lst: 65 | print(e) 66 | 67 | else: # 종료 68 | break -------------------------------------------------------------------------------- /chap08/double_list.py: -------------------------------------------------------------------------------- 1 | # 원형 이중 연결 리스트 구현하기 2 | # Do it! 실습 8-5 [A] 3 | from __future__ import annotations 4 | from typing import Any, Type 5 | 6 | class Node: 7 | """원형 이중 연결 리스트용 노드 클래스""" 8 | 9 | def __init__(self, data: Any = None, prev: Node = None, 10 | next: Node = None) -> None: 11 | """초기화""" 12 | self.data = data # 데이터 13 | self.prev = prev or self # 앞쪽 포인터 14 | self.next = next or self # 뒤쪽 포인터 15 | 16 | class DoubleLinkedList: 17 | """원형 이중 연결 리스트 클래스""" 18 | 19 | def __init__(self) -> None: 20 | """초기화""" 21 | self.head = self.current = Node() # 더미 노드를 생성 22 | self.no = 0 23 | 24 | def __len__(self) -> int: 25 | """선형 리스트의 노드 수를 반환""" 26 | return self.no 27 | 28 | def is_empty(self) -> bool: 29 | """리스트가 비어 있는가?""" 30 | return self.head.next is self.head 31 | 32 | # Do it! 실습 8-5 [B] 33 | def search(self, data: Any) -> Any: 34 | """data와 값이 같은 노드를 검색""" 35 | cnt = 0 36 | ptr = self.head.next # 현재 스캔 중인 노드 37 | while ptr is not self.head: 38 | if data == ptr.data: 39 | self.current = ptr 40 | return cnt # 검색 성공 41 | cnt += 1 42 | ptr = ptr.next # 뒤쪽 노드에 주목 43 | return -1 # 검색 실패 44 | 45 | def __contains__(self, data: Any) -> bool: 46 | """연결 리스트에 data가 포함되어 있는가?""" 47 | return self.search(data) >= 0 48 | 49 | # Do it! 실습 8-5 [C] 50 | def print_current_node(self) -> None: 51 | """주목 노드를 출력""" 52 | if self.is_empty(): 53 | print('주목 노드는 없습니다.') 54 | else: 55 | print(self.current.data) 56 | 57 | def print(self) -> None: 58 | """모든 노드를 출력""" 59 | ptr = self.head.next # 더미 노드의 뒤쪽 노드 60 | while ptr is not self.head: 61 | print(ptr.data) 62 | ptr = ptr.next 63 | 64 | def print_reverse(self) -> None: 65 | """모든 노드를 역순으로 출력""" 66 | ptr = self.head.prev # 더미 노드의 앞쪽 노드 67 | while ptr is not self.head: 68 | print(ptr.data) 69 | ptr = ptr.prev 70 | 71 | def next(self) -> bool: 72 | """주목 노드를 한 칸 뒤로 이동""" 73 | if self.is_empty() or self.current.next is self.head: 74 | return False # 이동할 수 없음 75 | self.current = self.current.next 76 | return True 77 | 78 | def prev(self) -> bool: 79 | """주목 노드를 한 칸 앞으로 이동""" 80 | if self.is_empty() or self.current.prev is self.head: 81 | return False # 이동할 수 없음 82 | self.current = self.current.prev 83 | return True 84 | 85 | # Do it! 실습 8-5[D] 86 | def add(self, data: Any) -> None: 87 | """주목 노드의 바로 뒤에 노드를 삽입""" 88 | node = Node(data, self.current, self.current.next) 89 | self.current.next.prev = node 90 | self.current.next = node 91 | self.current = node 92 | self.no += 1 93 | 94 | def add_first(self, data: Any) -> None: 95 | """맨 앞에 노드를 삽입""" 96 | self.current = self.head # 더미 노드 head의 바로 뒤에 삽입 97 | self.add(data) 98 | 99 | def add_last(self, data: Any) -> None: 100 | """맨 뒤에 노드를 삽입""" 101 | self.current = self.head.prev # 꼬리 노드 head.prev의 바로 뒤에 삽입 102 | self.add(data) 103 | 104 | # Do it! 실습 8-5[E] 105 | def remove_current_node(self) -> None: 106 | """주목 노드 삭제""" 107 | if not self.is_empty(): 108 | self.current.prev.next = self.current.next 109 | self.current.next.prev = self.current.prev 110 | self.current = self.current.prev 111 | self.no -= 1 112 | if self.current is self.head: 113 | self.current = self.head.next 114 | 115 | def remove(self, p: Node) -> None: 116 | """노드 p를 삭제""" 117 | ptr = self.head.next 118 | 119 | while ptr is not self.head: 120 | if ptr is p: # p를 발견 121 | self.current = p 122 | self.remove_current_node() 123 | break 124 | ptr = ptr.next 125 | 126 | def remove_first(self) -> None: 127 | """머리 노드 삭제""" 128 | self.current = self.head.next # 머리 노드 head.next를 삭제 129 | self.remove_current_node() 130 | 131 | def remove_last(self) -> None: 132 | """꼬리 노드 삭제""" 133 | self.current = self.head.prev # 꼬리 노드 head.prev를 삭제 134 | self.remove_current_node() 135 | 136 | def clear(self) -> None: 137 | """모든 노드를 삭제""" 138 | while not self.is_empty(): # 리스트 전체가 빌 때까지 139 | self.remove_first() # 머리 노드를 삭제 140 | self.no = 0 141 | 142 | # Do it! 실습 8-5[F] 143 | def __iter__(self) -> DoubleLinkedListIterator: 144 | """반복자를 반환""" 145 | return DoubleLinkedListIterator(self.head) 146 | 147 | def __reversed__(self) -> DoubleLinkedListReverseIterator: 148 | """내림차순 반복자를 반환""" 149 | return DoubleLinkedListReverseIterator(self.head) 150 | 151 | class DoubleLinkedListIterator: 152 | """DoubleLinkedList의 반복자용 클래스""" 153 | 154 | def __init__(self, head: Node): 155 | self.head = head 156 | self.current = head.next 157 | 158 | def __iter__(self) -> DoubleLinkedListIterator: 159 | return self 160 | 161 | def __next__(self) -> Any: 162 | if self.current is self.head: 163 | raise StopIteration 164 | else: 165 | data = self.current.data 166 | self.current = self.current.next 167 | return data 168 | 169 | class DoubleLinkedListReverseIterator: 170 | """DoubleLinkedList의 내림차순 반복자용 클래스""" 171 | 172 | def __init__(self, head: Node): 173 | self.head = head 174 | self.current = head.prev 175 | 176 | def __iter__(self) -> DoubleLinkedListReverseIterator: 177 | return self 178 | 179 | def __next__(self) -> Any: 180 | if self.current is self.head: 181 | raise StopIteration 182 | else: 183 | data = self.current.data 184 | self.current = self.current.prev 185 | return data -------------------------------------------------------------------------------- /chap08/double_list_test.py: -------------------------------------------------------------------------------- 1 | # [Do it! 실습 8-6] 원형 이중 연결 리스트 클래스 DoubleLinkedList 구현하기 2 | 3 | from enum import Enum 4 | from double_list import DoubleLinkedList 5 | 6 | Menu = Enum('Menu', ['머리에노드삽입', '꼬리에노드삽입', '주목노드바로뒤삽입', 7 | '머리노드삭제', '꼬리노드삭제', '주목노드출력', 8 | '주목노드이동', '주목노드역순이동', '주목노드삭제', 9 | '모든노드삭제', '검색', '멤버십판단', '모든노드출력', 10 | '모든노드역순출력', '모든노드스캔', '모든노드역순스캔', '종료']) 11 | 12 | def select_Menu() -> Menu: 13 | """메뉴 선택""" 14 | s = [f'({m.value}){m.name}' for m in Menu] 15 | while True: 16 | print(*s, sep = ' ', end='') 17 | n = int(input(': ')) 18 | if 1 <= n <= len(Menu): 19 | return Menu(n) 20 | 21 | lst = DoubleLinkedList() # 원형・이중 연결 리스트 생성 22 | 23 | while True: 24 | menu = select_Menu() # 메뉴 선택 25 | 26 | if menu == Menu.머리에노드삽입: # 맨 앞에 노드 삽입 27 | lst.add_first(int(input('머리 노드에 넣을 값을 입력하세요.: '))) 28 | 29 | elif menu == Menu.꼬리에노드삽입: # 맨 끝에 노드 삽입 30 | lst.add_last(int(input('꼬리 노드에 넣을 값을 입력하세요.: '))) 31 | 32 | elif menu == Menu.주목노드바로뒤삽입: # 주목 노드 바로 뒤에 삽입 33 | lst.add(int(input('주목 노드 바로 뒤에 넣을 값을 입력하세요 : '))) 34 | 35 | elif menu == Menu.머리노드삭제: # 맨 앞 노드 삭제 36 | lst.remove_first() 37 | 38 | elif menu == Menu.꼬리노드삭제: # 맨 끝 노드 삭제 39 | lst.remove_last() 40 | 41 | elif menu == Menu.주목노드출력: # 주목 노드 출력 42 | lst.print_current_node() 43 | 44 | elif menu == Menu.주목노드이동: # 주목 노드를 한 칸 뒤로 이동 45 | lst.next() 46 | 47 | elif menu == Menu.주목노드역순이동: # 주목 노드를 한 칸 앞으로 이동 48 | lst.prev() 49 | 50 | elif menu == Menu.주목노드삭제: # 주목 노드 삭제 51 | lst.remove_current_node() 52 | 53 | elif menu == Menu.모든노드삭제: # 모두 삭제 54 | lst.clear() 55 | 56 | elif menu == Menu.검색: # 검색 57 | pos = lst.search(int(input('검색할 값을 입력하세요.: '))) 58 | if pos >= 0: 59 | print(f'그 값의 데이터는 {pos + 1}번째에 있습니다.') 60 | else: 61 | print('해당 데이터가 없습니다.') 62 | 63 | elif menu == Menu.멤버십판단: # 멤버십 판단 64 | print('그 값의 데이터는 포함되어' 65 | +(' 있습니다.' if int(input('판단할 값을 입력하세요.: ')) in lst else ' 있지 않습니다.')) 66 | 67 | elif menu == Menu.모든노드출력: # 모든 노드를 출력 68 | lst.print() 69 | 70 | elif menu == Menu.모든노드역순출력: # 모든 노드 역순 출력 71 | lst.print_reverse() 72 | 73 | elif menu == Menu.모든노드스캔: # 모든 노드 스캔 74 | for e in lst: 75 | print(e) 76 | 77 | elif menu == Menu.모든노드역순스캔: # 모든 노드 역순 스캔 78 | for e in reversed(lst): 79 | print(e) 80 | 81 | else: # 종료 82 | break -------------------------------------------------------------------------------- /chap08/linked_list.py: -------------------------------------------------------------------------------- 1 | # [Do it! 실습 8-1] 포인터로 연결 리스트 만들기 2 | 3 | from __future__ import annotations 4 | from typing import Any, Type 5 | 6 | class Node: 7 | """연결 리스트용 노드 클래스""" 8 | 9 | def __init__(self, data: Any = None, next: Node = None): 10 | """초기화""" 11 | self.data = data # 데이터 12 | self.next = next # 뒤쪽 포인터 13 | 14 | # Do it! 실습 8-1 [B] 15 | class LinkedList: 16 | """연결 리스트 클래스""" 17 | 18 | def __init__(self) -> None: 19 | """초기화""" 20 | self.no = 0 # 노드의 개수 21 | self.head = None # 머리 노드 22 | self.current = None # 주목 노드 23 | 24 | def __len__(self) -> int: 25 | """연결 리스트의 노드 개수를 반환""" 26 | return self.no 27 | 28 | # Do it! 실습 8-1 [C] 29 | def search(self, data: Any) -> int: 30 | """data와 값이 같은 노드를 검색""" 31 | cnt = 0 32 | ptr = self.head 33 | while ptr is not None: 34 | if ptr.data == data: 35 | self.current = ptr 36 | return cnt 37 | cnt += 1 38 | ptr = ptr.next 39 | return -1 40 | 41 | def __contains__(self, data: Any) -> bool: 42 | """연결 리스트에 data가 포함되어 있는가?""" 43 | return self.search(data) >= 0 44 | 45 | # Do it! 실습 8-1 [D] 46 | def add_first(self, data: Any) -> None: 47 | """맨 앞에 노드를 삽입""" 48 | ptr = self.head # 삽입 전의 머리 노드 49 | self.head = self.current = Node(data, ptr) 50 | self.no += 1 51 | 52 | # Do it! 실습 8-1 [E] 53 | def add_last(self, data: Any): 54 | """맨 끝에 노드를 삽입""" 55 | if self.head is None : # 리스트가 비어 있으면 56 | self.add_first(data) # 맨앞에 노드 삽입 57 | else: 58 | ptr = self.head 59 | while ptr.next is not None: 60 | ptr = ptr.next # while문을 종료할 때 ptr은 꼬리 노드를 참조 61 | ptr.next = self.current = Node(data, None) 62 | self.no += 1 63 | 64 | # Do it! 실습 8-1 [F] 65 | def remove_first(self) -> None: 66 | """머리 노드를 삭제""" 67 | if self.head is not None: # 리스트가 비어 있으면 68 | self.head = self.current = self.head.next 69 | self.no -= 1 70 | 71 | # Do it! 실습 8-1 [G] 72 | def remove_last(self): 73 | """꼬리 노드 삭제""" 74 | if self.head is not None: 75 | if self.head.next is None : # 노드가 1개 뿐이라면 76 | self.remove_first() # 머리 노드를 삭제 77 | else: 78 | ptr = self.head # 스캔 중인 노드 79 | pre = self.head # 스캔 중인 노드의 앞쪽 노드 80 | 81 | while ptr.next is not None: 82 | pre = ptr 83 | ptr = ptr.next # while문 종료시 ptr은 꼬리 노드를 참조하고 pre는 맨끝에서 두 번째 노드를 참조 84 | pre.next = None # pre는 삭제 뒤 꼬리 노드 85 | self.current = pre 86 | self.no -= 1 87 | 88 | # Do it! 실습 8-1 [H] 89 | def remove(self, p: Node) -> None: 90 | """노드 p를 삭제""" 91 | if self.head is not None: 92 | if p is self.head: # p가 머리 ​​노드이면 93 | self.remove_first() # 머리 노드를 삭제 94 | else: 95 | ptr = self.head 96 | 97 | while ptr.next is not p: 98 | ptr = ptr.next 99 | if ptr is None: 100 | return # ptr은 리스트에 존재하지 않음 101 | ptr.next = p.next 102 | self.current = ptr 103 | self.no -= 1 104 | 105 | def remove_current_node(self) -> None: 106 | """주목 노드를 삭제""" 107 | self.remove(self.current) 108 | 109 | def clear(self) -> None: 110 | """전체 노드를 삭제""" 111 | while self.head is not None: # 전체가 비어 있게 될 때까지 112 | self.remove_first() # 머리 노드를 삭제 113 | self.current = None 114 | self.no = 0 115 | 116 | def next(self) -> bool: 117 | """주목 노드를 한 칸 뒤로 진행""" 118 | if self.current is None or self.current.next is None: 119 | return False # 진행할 수 없음 120 | self.current = self.current.next 121 | return True 122 | 123 | # Do it! 실습 8-1 [I] 124 | def print_current_node(self) -> None: 125 | """주목 노드를 출력""" 126 | if self.current is None: 127 | print('주목 노드가 존재하지 않습니다.') 128 | else: 129 | print(self.current.data) 130 | 131 | def print(self) -> None: 132 | """모든 노드를 출력""" 133 | ptr = self.head 134 | 135 | while ptr is not None: 136 | print(ptr.data) 137 | ptr = ptr.next 138 | 139 | # Do it! 실습 8-1 [J] 140 | def __iter__(self) -> LinkedListIterator: 141 | """이터레이터(반복자)를 반환""" 142 | return LinkedListIterator(self.head) 143 | 144 | class LinkedListIterator: 145 | """클래스 LinkedList의 이터레이터(반복자)용 클래스""" 146 | 147 | def __init__(self, head: Node): 148 | self.current = head 149 | 150 | def __iter__(self) -> LinkedListIterator: 151 | return self 152 | 153 | def __next__(self) -> Any: 154 | if self.current is None: 155 | raise StopIteration 156 | else: 157 | data = self.current.data 158 | self.current = self.current.next 159 | return data -------------------------------------------------------------------------------- /chap08/linked_list_test.py: -------------------------------------------------------------------------------- 1 | # [Do it! 실습 8-2] 포인터로 이용한 연결 리스트 클래스 LinkedList 사용하기 2 | 3 | from enum import Enum 4 | from linked_list import LinkedList 5 | 6 | Menu = Enum('Menu', ['머리에노드삽입', '꼬리에노드삽입', '머리노드삭제', 7 | '꼬리노드삭제', '주목노드출력', '주목노드이동', 8 | '주목노드삭제', '모든노드삭제', '검색', '멤버십판단', 9 | '모든노드출력', '스캔', '종료',]) 10 | 11 | def select_Menu() -> Menu: 12 | """메뉴 선택""" 13 | s = [f'({m.value}){m.name}' for m in Menu] 14 | while True: 15 | print(*s, sep=' ', end='') 16 | n = int(input(': ')) 17 | if 1 <= n <= len(Menu): 18 | return Menu(n) 19 | 20 | 21 | lst = LinkedList() # 연결 리스트를 생성 22 | 23 | while True: 24 | menu = select_Menu() # 메뉴 선택 25 | 26 | if menu == Menu.머리에노드삽입: # 맨 앞에 노드 삽입 27 | lst.add_first(int(input('머리에 넣을 값을 입력하세요.: '))) 28 | 29 | elif menu == Menu.꼬리에노드삽입: # 맨 끝에 노드 삽입 30 | lst.add_last(int(input('꼬리에 넣을 값을 입력하세요.: '))) 31 | 32 | elif menu == Menu.머리노드삭제: # 맨 앞 노드 삭제 33 | lst.remove_first() 34 | 35 | elif menu == Menu.꼬리노드삭제: # 맨 끝 노드 삭제 36 | lst.remove_last() 37 | 38 | elif menu == Menu.주목노드출력: # 주목 노드 출력 39 | lst.print_current_node() 40 | 41 | elif menu == Menu.주목노드이동: # 주목 노드를 한 칸 뒤로 이동 42 | lst.next() 43 | 44 | elif menu == Menu.주목노드삭제: # 주목 노드 삭제 45 | lst.remove_current_node() 46 | 47 | elif menu == Menu.모든노드삭제: # 모든 노드를 삭제 48 | lst.clear() 49 | 50 | elif menu == Menu.검색: # 노드를 검색 51 | pos = lst.search(int(input('검색할 값을 입력하세요.: '))) 52 | if pos >= 0: 53 | print(f'그 값의 데이터는 {pos + 1}번째에 있습니다.') 54 | else: 55 | print('해당 데이터가 없습니다.') 56 | 57 | elif menu == Menu.멤버십판단: # 멤버십 판단 58 | print('그 값의 데이터는 포함되어' + (' 있습니다.' if int(input('멤버십 판단할 값을 입력하세요.: ')) in lst else ' 있지 않습니다.')) 59 | 60 | elif menu == Menu.모든노드출력: # 모든 노드 출력 61 | lst.print() 62 | 63 | elif menu == Menu.스캔: # 모든 노드 스캔 64 | for e in lst: 65 | print(e) 66 | 67 | else: # 종료 68 | break -------------------------------------------------------------------------------- /chap09/bst.py: -------------------------------------------------------------------------------- 1 | # [Do it! 실습 9-1] 이진 검색 트리의 구현 2 | 3 | from __future__ import annotations 4 | from typing import Any, Type 5 | 6 | class Node: 7 | """이진 검색 트리의 노드""" 8 | def __init__(self, key: Any, value: Any, left: Node = None, 9 | right: Node = None): 10 | """생성자""" 11 | self.key = key # 키 12 | self.value = value # 값 13 | self.left = left # 왼쪽 포인터(왼쪽 자식 참조) 14 | self.right = right # 오른쪽 포인터(오른쪽 자식 참조) 15 | 16 | class BinarySearchTree: 17 | """이진 검색 트리""" 18 | 19 | def __init__(self): 20 | """초기화""" 21 | self.root = None # 루트 22 | 23 | # Do it! 실습 9-1[B] 24 | def search(self, key: Any) -> Any: 25 | """키 key를 갖는 노드를 검색""" 26 | p = self.root # 루트에 주목 27 | while True: 28 | if p is None: # 더 이상 진행할 수 없으면 29 | return None # 검색 실패 30 | if key == p.key: # key와 노드 p의 키가 같으면 31 | return p.value # 검색 성공 32 | elif key < p.key: # key 쪽이 작으면 33 | p = p.left # 왼쪽 서브 트리에서 검색 34 | else: # key 쪽이 크면 35 | p = p.right # 오른쪽 서브 트리에서 검색 36 | 37 | # Do it! 실습 9-1[C] 38 | def add(self, key: Any, value: Any) -> bool: 39 | """키가 key이고, 값이 value인 노드를 삽입""" 40 | 41 | def add_node(node: Node, key: Any, value: Any) -> None: 42 | """node를 루트로 하는 서브 트리에 키가 key이고, 값이 value인 노드를 삽입""" 43 | if key == node.key: 44 | return False # key가 이진검색트리에 이미 존재 45 | elif key < node.key: 46 | if node.left is None: 47 | node.left = Node(key, value, None, None) 48 | else: 49 | add_node(node.left, key, value) 50 | else: 51 | if node.right is None: 52 | node.right = Node(key, value, None, None) 53 | else: 54 | add_node(node.right, key, value) 55 | return True 56 | 57 | if self.root is None: 58 | self.root = Node(key, value, None, None) 59 | return True 60 | else: 61 | return add_node(self.root, key, value) 62 | 63 | # # Do it! 실습 9-1[D] 64 | def remove(self, key: Any) -> bool: 65 | """키가 key인 노드를 삭제""" 66 | p = self.root # 스캔 중인 노드 67 | parent = None # 스캔 중인 노드의 부모 노드 68 | is_left_child = True # p는 parent의 왼쪽 자식 노드인지 확인 69 | 70 | while True: 71 | if p is None: # 더 이상 진행할 수 없으면 72 | return False # 그 키는 존재하지 않음 73 | 74 | if key == p.key: # key와 노드 p의 키가 같으면 75 | break # 검색 성공 76 | else: 77 | parent = p # 가지를 내려가기 전에 부모를 설정 78 | if key < p.key: # key 쪽이 작으면 79 | is_left_child = True # 여기서 내려가는 것은 왼쪽 자식 80 | p = p.left # 왼쪽 서브 트리에서 검색 81 | else: # key 쪽이 크면 82 | is_left_child = False # 여기서 내려가는 것은 오른쪽 자식 83 | p = p.right # 오른쪽 서브 트리에서 검색 84 | 85 | if p.left is None: # p에 왼쪽 자식이 없으면 86 | if p is self.root: 87 | self.root = p.right 88 | elif is_left_child: 89 | parent.left = p.right # 부모의 왼쪽 포인터가 오른쪽 자식을 가리킴 90 | else: 91 | parent.right = p.right # 부모의 오른쪽 포인터가 오른쪽 자식을 가리킴 92 | elif p.right is None: # p에 오른쪽 자식이 없으면 93 | if p is self.root: 94 | self.root = p.left 95 | elif is_left_child: 96 | parent.left = p.left # 부모의 왼쪽 포인터가 왼쪽 자식을 가리킴 97 | else: 98 | parent.right = p.left # 부모의 오른쪽 포인터가 왼쪽 자식을 가리킴 99 | else: 100 | parent = p 101 | left = p.left # 서브 트리 안에서 가장 큰 노드 102 | is_left_child = True 103 | while left.right is not None: # 가장 큰 노드 left를 검색 104 | parent = left 105 | left = left.right 106 | is_left_child = False 107 | 108 | p.key = left.key # left의 키를 p로 이동 109 | p.value = left.value # left의 데이터를 p로 이동 110 | if is_left_child: 111 | parent.left = left.left # left를 삭제 112 | else: 113 | parent.right = left.left # left를 삭제 114 | return True 115 | 116 | # Do it! 실습 9-1[E] 117 | def dump(self) -> None: 118 | """덤프(모든 노드를 키의 오름차순으로 출력)""" 119 | 120 | def print_subtree(node: Node): 121 | """node를 루트로 하는 서브 트리의 노드를 키의 오름차순으로 출력""" 122 | if node is not None: 123 | print_subtree(node.left) # 왼쪽 서브 트리를 오름차순으로 출력 124 | print(f'{node.key} {node.value}') # node를 출력 125 | print_subtree(node.right) # 오른쪽 서브 트리를 오름차순으로 출력 126 | 127 | print_subtree(self.root) 128 | 129 | # Do it! 실습 9-1[F] 130 | def min_key(self) -> Any: 131 | """가장 작은 키""" 132 | if self.root is None: 133 | return None 134 | p = self.root 135 | while p.left is not None: 136 | p = p.left 137 | return p.key 138 | 139 | def max_key(self) -> Any: 140 | """가장 큰 키""" 141 | if self.root is None: 142 | return None 143 | p = self.root 144 | while p.right is not None: 145 | p = p.right 146 | return p.key -------------------------------------------------------------------------------- /chap09/bst2.py: -------------------------------------------------------------------------------- 1 | # [Do it! 실습 9C-1] 이진 검색 트리의 구현(키를 내림차순으로 덤프) 2 | 3 | from __future__ import annotations 4 | from typing import Any, Type 5 | 6 | class Node: 7 | """이진검색트리의 노드""" 8 | def __init__(self, key: Any, value: Any, left: Node = None, 9 | right: Node = None): 10 | """생성자""" 11 | self.key = key # 키 12 | self.value = value # 값 13 | self.left = left # 왼쪽 포인터(왼쪽 자식에 대한 참조) 14 | self.right = right # 오른쪽 포인터(오른쪽 자식에 대한 참조) 15 | 16 | class BinarySearchTree: 17 | """이진검색트리""" 18 | 19 | def __init__(self): 20 | """초기화""" 21 | self.root = None # 루트 22 | 23 | def search(self, key: Any) -> Any: 24 | """키 key를 갖는 노드를 검색""" 25 | p = self.root # 루트에 주목 26 | while True: 27 | if p is None: # 더 이상 진행할 수 없으면 28 | return None # ... 검색 실패 29 | if key == p.key: # key와 노드 p의 키가 같으면 30 | return p.value # ... 검색 성공 31 | elif key < p.key: # key 쪽이 작으면 32 | p = p.left # ... 왼쪽 서브 트리에서 검색 33 | else: # key 쪽이 크면 34 | p = p.right # ... 오른쪽 서브 트리에서 검색 35 | 36 | def add(self, key: Any, value: Any) -> bool: 37 | """키가 key이고, 값이 value인 노드를 삽입""" 38 | 39 | def add_node(node: Node, key: Any, value: Any) -> None: 40 | """node를 루트로 하는 서브 트리에 키가 key이고, 값이 value인 노드를 삽입""" 41 | if key == node.key: 42 | return False # key가 이진검색트리에 이미 존재 43 | elif key < node.key: 44 | if node.left is None: 45 | node.left = Node(key, value, None, None) 46 | else: 47 | add_node(node.left, key, value) 48 | else: 49 | if node.right is None: 50 | node.right = Node(key, value, None, None) 51 | else: 52 | add_node(node.right, key, value) 53 | return True 54 | 55 | if self.root is None: 56 | self.root = Node(key, value, None, None) 57 | return True 58 | 59 | else: 60 | return add_node(self.root, key, value) 61 | 62 | def remove(self, key: Any) -> bool: 63 | """키가 key인 노드를 삭제""" 64 | p = self.root # 스캔 중인 노드 65 | parent = None # 스캔 중인 노드의 부모 노드 66 | is_left_child = True # p는 parent의 왼쪽 자식 노드입니까? 67 | 68 | while True: 69 | if p is None: # 더 이상 진행할 수 없으면 70 | return False # ... 그 키는 존재하지 않음 71 | 72 | if key == p.key: # key와 노드 p의 키가 같으면 73 | break # ... 검색 성공 74 | else: 75 | parent = p # 가지를 내려가기 전에 부모를 설정 76 | if key < p.key: # key 쪽이 작으면 77 | is_left_child = True # ... 여기서 내려가는 것은 왼쪽 자식 78 | p = p.left #... 왼쪽 서브 트리에서 검색 79 | else: # key 쪽이 크면 80 | is_left_child = False # ... 여기서 내려가는 것은 오른쪽 자식 81 | p = p.right # ... 오른쪽 서브 트리에서 검색 82 | 83 | if p.left is None: # p에 왼쪽 자식이 없으면 ... 84 | if p is self.root: 85 | self.root = p.right 86 | elif is_left_child: 87 | parent.left = p.right # 부모의 왼쪽 포인터가 오른쪽 자식을 가리킵니다. 88 | else: 89 | parent.right = p.right # 부모의 오른쪽 포인터가 오른쪽 자식을 가리킵니다. 90 | elif p.right is None: # p에 오른쪽 자식이 없으면 ... 91 | if p is self.root: 92 | self.root = p.left 93 | elif is_left_child: 94 | parent.left = p.left # 부모의 왼쪽 포인터가 왼쪽 자식을 가리킵니다. 95 | else: 96 | parent.right = p.left # 부모의 오른쪽 포인터가 왼쪽 자식을 가리킵니다. 97 | else: 98 | parent = p 99 | left = p.left # 서브 트리 안에서 가장 큰 노드 100 | is_left_child = True 101 | while left.right is not None: # 가장 큰 노드 left를 검색 102 | parent = left 103 | left = left.right 104 | is_left_child = False 105 | 106 | p.key = left.key # left의 키를 p로 이동 107 | p.value = left.value # left의 데이터를 p로 이동 108 | if is_left_child: 109 | parent.left = left.left # left를 삭제 110 | else: 111 | parent.right = left.left # left를 삭제 112 | return True 113 | 114 | def dump(self, reverse = False) -> None: 115 | """덤프(모든 노드를 키의 오름차순/내림차순으로 출력)""" 116 | 117 | def print_subtree(node: Node): 118 | """node를 루트로 하는 서브 트리의 노드를 키의 오름차순으로 출력""" 119 | if node is not None: 120 | print_subtree(node.left) # 왼쪽 서브 트리를 오름차순으로 출력 121 | print(f'{node.key} {node.value}') # node를 출력 122 | print_subtree(node.right) # 오른쪽 서브 트리를 오름차순으로 출력 123 | 124 | def print_subtree_rev(node: Node): 125 | """node를 루트로 하는 서브 트리의 노드를 키의 내림차순으로 출력""" 126 | if node is not None: 127 | print_subtree_rev(node.right) # 오른쪽 서브 트리를 내림차순으로 출력 128 | print(f'{node.key} {node.value}') # node를 출력 129 | print_subtree_rev(node.left) # 왼쪽 서브 트리를 내림차순으로 출력 130 | 131 | print_subtree_rev(self.root) if reverse else print_subtree(self.root) 132 | 133 | def min_key(self) -> Any: 134 | """가장 작은 키""" 135 | if self.root is None: 136 | return None 137 | p = self.root 138 | while p.left is not None: 139 | p = p.left 140 | return p.key 141 | 142 | def max_key(self) -> Any: 143 | """가장 큰 키""" 144 | if self.root is None: 145 | return None 146 | p = self.root 147 | while p.right is not None: 148 | p = p.right 149 | return p.key -------------------------------------------------------------------------------- /chap09/bst2_test.py: -------------------------------------------------------------------------------- 1 | # [Do it! 실습 9C-1] 이진 검색 트리 클래스 BinarySearchTree 사용하기(오름차순, 내림차순으로 덤프) 2 | 3 | from enum import Enum 4 | from bst2 import BinarySearchTree 5 | 6 | Menu = Enum('Menu', ['삽입', '삭제', '검색', '오름차순덤프', '내림차순덤프', '키의범위', '종료']) 7 | 8 | def select_Menu() -> Menu: 9 | """메뉴 선택""" 10 | s = [f'({m.value}){m.name}' for m in Menu] 11 | while True: 12 | print(*s, sep = ' ', end='') 13 | n = int(input(' : ')) 14 | if 1 <= n <= len(Menu): 15 | return Menu(n) 16 | 17 | tree = BinarySearchTree() # 이진 검색 트리를 생성 18 | 19 | while True: 20 | menu = select_Menu() # 메뉴 선택 21 | 22 | if menu == Menu.삽입: # 삽입 23 | key = int(input('삽입할 키를 입력하세요.: ')) 24 | val = input('삽입할 값을 입력하세요.: ') 25 | if not tree.add(key, val): 26 | print('삽입에 실패했습니다!') 27 | 28 | elif menu == Menu.삭제: # 삭제 29 | key = int(input('삭제할 키를 입력하세요.: ')) 30 | tree.remove(key) 31 | 32 | elif menu == Menu.검색: # 검색 33 | key = int(input('검색할 키를 입력하세요.: ')) 34 | t = tree.search(key) 35 | if t is not None: 36 | print(f'이 키를 갖는 값은 {t}입니다.') 37 | else: 38 | print('해당 데이터가 없습니다.') 39 | 40 | elif menu == Menu.오름차순덤프: # 오름차순 덤프 41 | tree.dump() 42 | 43 | 44 | elif menu == Menu.내림차순덤프: # 내림차순 덤프 45 | tree.dump(reverse = True) 46 | 47 | elif menu == Menu.키의범위 : # 키의 범위(최솟값과 최댓값) 48 | print(f'키의 최솟값은 {tree.min_key()}입니다.') 49 | print(f'키의 최댓값은 {tree.max_key()}입니다.') 50 | 51 | else:# 종료 52 | break -------------------------------------------------------------------------------- /chap09/bst_test.py: -------------------------------------------------------------------------------- 1 | # [Do it! 실습 9-2] 이진 검색 트리 클래스 BinarySearchTree 사용하기 2 | 3 | from enum import Enum 4 | from bst import BinarySearchTree 5 | 6 | Menu = Enum('Menu', ['삽입', '삭제', '검색', '덤프', '키의범위', '종료']) 7 | 8 | def select_Menu() -> Menu: 9 | """메뉴 선택""" 10 | s = [f'({m.value}){m.name}' for m in Menu] 11 | while True: 12 | print(*s, sep = ' ', end='') 13 | n = int(input(': ')) 14 | if 1 <= n <= len(Menu): 15 | return Menu(n) 16 | 17 | tree = BinarySearchTree() # 이진 검색 트리를 생성 18 | 19 | while True: 20 | menu = select_Menu() # 메뉴 선택 21 | 22 | if menu == Menu.삽입: # 삽입 23 | key = int(input('삽입할 키를 입력하세요.: ')) 24 | val = input('삽입할 값을 입력하세요.: ') 25 | if not tree.add(key, val): 26 | print('삽입에 실패했습니다!') 27 | 28 | elif menu == Menu.삭제: # 삭제 29 | key = int(input('삭제할 키를 입력하세요.: ')) 30 | tree.remove(key) 31 | 32 | elif menu == Menu.검색: # 검색 33 | key = int(input('검색할 키를 입력하세요.: ')) 34 | t = tree.search(key) 35 | if t is not None: 36 | print(f'이 키를 갖는 값은 {t}입니다.') 37 | else: 38 | print('해당 데이터가 없습니다.') 39 | 40 | elif menu == Menu.덤프: # 덤프(모두 출력) 41 | tree.dump() 42 | 43 | elif menu == Menu.키의범위 : # 키의 범위(최솟값과 최댓값) 44 | print(f'키의 최솟값은 {tree.min_key()}입니다.') 45 | print(f'키의 최댓값은 {tree.max_key()}입니다.') 46 | 47 | else: # 종료 48 | break --------------------------------------------------------------------------------