├── .env ├── .gitignore ├── .python-version ├── .travis.yml ├── Pipfile ├── Pipfile.lock ├── README.md ├── README.rst ├── algorithms ├── __init__.py ├── array.py ├── basic.py ├── dp.py ├── math.py ├── queue.py ├── search.py ├── sort.py ├── stack.py ├── string.py └── tree.py ├── setup.py └── tests ├── __init__.py ├── test_array.py ├── test_basic.py ├── test_dp.py ├── test_math.py ├── test_queue.py ├── test_search.py ├── test_sort.py ├── test_stack.py ├── test_string.py ├── test_tree.py └── test_with_unittest.py /.env: -------------------------------------------------------------------------------- 1 | pyenv shell 3.7.0 -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__/ 2 | *.swp 3 | .DS_Store 4 | *.pyc 5 | algo_venv/ 6 | venv/ 7 | build/ 8 | *~ 9 | dist/ 10 | /*.egg-info 11 | *.ropeproject/ 12 | docs/_build 13 | .idea/* 14 | .cache/* 15 | .vscode/* -------------------------------------------------------------------------------- /.python-version: -------------------------------------------------------------------------------- 1 | 3.7.0 -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | python: 3 | - 2.7 4 | - 3.7 5 | install: 6 | - pip install tox-travis 7 | - pip install pytest-cov coveralls 8 | script: 9 | - tox 10 | after_success: 11 | - coveralls -------------------------------------------------------------------------------- /Pipfile: -------------------------------------------------------------------------------- 1 | [[source]] 2 | url = "https://pypi.org/simple" 3 | verify_ssl = true 4 | name = "pypi" 5 | 6 | [packages] 7 | requests = "*" 8 | 9 | [dev-packages] 10 | pytest = "*" -------------------------------------------------------------------------------- /Pipfile.lock: -------------------------------------------------------------------------------- 1 | { 2 | "_meta": { 3 | "hash": { 4 | "sha256": "e76ae491d793d659f05c7f7eab261fd6167dc062efcba08f17be68e73eb87665" 5 | }, 6 | "pipfile-spec": 6, 7 | "requires": {}, 8 | "sources": [ 9 | { 10 | "name": "pypi", 11 | "url": "https://pypi.org/simple", 12 | "verify_ssl": true 13 | } 14 | ] 15 | }, 16 | "default": { 17 | "certifi": { 18 | "hashes": [ 19 | "sha256:993f830721089fef441cdfeb4b2c8c9df86f0c63239f06bd025a76a7daddb033" 20 | ], 21 | "version": "==2018.11.29" 22 | }, 23 | "chardet": { 24 | "hashes": [ 25 | "sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae", 26 | "sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691" 27 | ], 28 | "version": "==3.0.4" 29 | }, 30 | "idna": { 31 | "hashes": [ 32 | "sha256:ea8b7f6188e6fa117537c3df7da9fc686d485087abf6ac197f9c46432f7e4a3c" 33 | ], 34 | "version": "==2.8" 35 | }, 36 | "requests": { 37 | "hashes": [ 38 | "sha256:502a824f31acdacb3a35b6690b5fbf0bc41d63a24a45c4004352b0242707598e", 39 | "sha256:7bf2a778576d825600030a110f3c0e3e8edc51dfaafe1c146e39a2027784957b" 40 | ], 41 | "index": "pypi", 42 | "version": "==2.21.0" 43 | }, 44 | "urllib3": { 45 | "hashes": [ 46 | "sha256:61bf29cada3fc2fbefad4fdf059ea4bd1b4a86d2b6d15e1c7c0b582b9752fe39", 47 | "sha256:de9529817c93f27c8ccbfead6985011db27bd0ddfcdb2d86f3f663385c6a9c22" 48 | ], 49 | "version": "==1.24.1" 50 | } 51 | }, 52 | "develop": { 53 | "atomicwrites": { 54 | "hashes": [ 55 | "sha256:0312ad34fcad8fac3704d441f7b317e50af620823353ec657a53e981f92920c0", 56 | "sha256:ec9ae8adaae229e4f8446952d204a3e4b5fdd2d099f9be3aaf556120135fb3ee" 57 | ], 58 | "version": "==1.2.1" 59 | }, 60 | "attrs": { 61 | "hashes": [ 62 | "sha256:10cbf6e27dbce8c30807caf056c8eb50917e0eaafe86347671b57254006c3e69", 63 | "sha256:ca4be454458f9dec299268d472aaa5a11f67a4ff70093396e1ceae9c76cf4bbb" 64 | ], 65 | "version": "==18.2.0" 66 | }, 67 | "more-itertools": { 68 | "hashes": [ 69 | "sha256:38a936c0a6d98a38bcc2d03fdaaedaba9f412879461dd2ceff8d37564d6522e4", 70 | "sha256:c0a5785b1109a6bd7fac76d6837fd1feca158e54e521ccd2ae8bfe393cc9d4fc", 71 | "sha256:fe7a7cae1ccb57d33952113ff4fa1bc5f879963600ed74918f1236e212ee50b9" 72 | ], 73 | "version": "==5.0.0" 74 | }, 75 | "pluggy": { 76 | "hashes": [ 77 | "sha256:8ddc32f03971bfdf900a81961a48ccf2fb677cf7715108f85295c67405798616", 78 | "sha256:980710797ff6a041e9a73a5787804f848996ecaa6f8a1b1e08224a5894f2074a" 79 | ], 80 | "version": "==0.8.1" 81 | }, 82 | "py": { 83 | "hashes": [ 84 | "sha256:bf92637198836372b520efcba9e020c330123be8ce527e535d185ed4b6f45694", 85 | "sha256:e76826342cefe3c3d5f7e8ee4316b80d1dd8a300781612ddbc765c17ba25a6c6" 86 | ], 87 | "version": "==1.7.0" 88 | }, 89 | "pytest": { 90 | "hashes": [ 91 | "sha256:41568ea7ecb4a68d7f63837cf65b92ce8d0105e43196ff2b26622995bb3dc4b2", 92 | "sha256:c3c573a29d7c9547fb90217ece8a8843aa0c1328a797e200290dc3d0b4b823be" 93 | ], 94 | "index": "pypi", 95 | "version": "==4.1.1" 96 | }, 97 | "six": { 98 | "hashes": [ 99 | "sha256:3350809f0555b11f552448330d0b52d5f24c91a322ea4a15ef22629740f3761c", 100 | "sha256:d16a0141ec1a18405cd4ce8b4613101da75da0e9a7aec5bdd4fa804d0e0eba73" 101 | ], 102 | "version": "==1.12.0" 103 | } 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | ## python-algorithms 3 | 4 | 이 공간은 Problem Solving 이하 PS 스터디를 위한 공간입니다. 5 | 6 | ## Get Started 7 | 8 | #### Install Python 9 | 10 | 파이썬은 공식 사이트인 [python.org](https://www.python.org/)에서 다운로드할 수 있다. 설치가 매우 간단하며 OSX 사용자라면 이미 파이썬이 설치되어 있을 것이다. 11 | 12 | 가능하면 가장 최신의 버전의 python3를 설치하는 것을 권장한다. 설치 후 커맨드 라인에서 아래와 같이 입력하면, 파이썬 Interpeter를 통해 프로그래밍할 수 있는 환경이 갖추어진다. 13 | 14 | ``` 15 | $ python3 16 | Python 3.6.1 (v3.6.1:69c0db5050, Mar 21 2017, 01:21:04) 17 | [GCC 4.2.1 (Apple Inc. build 5666) (dot 3)] on darwin 18 | Type "help", "copyright", "credits" or "license" for more information. 19 | >>> 20 | ``` 21 | 22 | #### IDLE과 PyCharm IDE 23 | 24 | Interpreter 언어인 파이썬은 위와 같은 Interactive 모드를 통해 별도의 도구 없이 한 줄 한 줄 프로그래밍 하도록 도와준다. 25 | 26 | 이 REPL은 매우 유용하지만 앞으로 파이썬 코드를 파일에 작성하고자 한다면 JetBrain의 [PyCharm IDE](https://www.jetbrains.com/pycharm/)를 사용하는 것을 추천한다. 27 | 28 | > 파이썬에는 파일에 작성하기 위한 기본 도구인 IDLE를 포함하고 있다. 29 | 30 | 자 뻔한 과정은 생략하고 아래와 같이 Hello World를 출력하는 첫 파이썬 프로그램을 작성해보자. 31 | 32 | ``` 33 | $ echo "print('Hello World!')" > hello_world.py 34 | ``` 35 | 36 | 파일에 작성된 코드 역시 파이썬 Interpreter에 의해서 실행되며 방법은 아래와 같다. 정상적으로 출력이 된다면 우리는 파이썬 프로그래밍을 위한 모든 준비를 마쳤다! 37 | 38 | ``` 39 | $ python3 hello_world.py 40 | Hello World! 41 | ``` 42 | 43 | ## TDD로 Python 시작하기 44 | 45 | 만약 파이썬이 처음이라면 TDD를 통해 프로젝트를 구성하고 파이썬을 더욱 멋지게 활용할 수 있는 아래의 글을 참고하도록 하자. 프로젝트의 소스코드는 현재 지금의 Repo 되시겠다. 도움이 될 듯하다면 ★ Star를 누르는 센스도 잊지 말자! 46 | 47 | - [시작하기](https://www.holaxprogramming.com/2017/06/15/python-get-started/) 48 | - [unittest와 함께하는 파이썬 테스트](https://www.holaxprogramming.com/2017/06/17/python-with-test/) 49 | - [파이썬 프로젝트의 구조](https://www.holaxprogramming.com/2017/06/28/python-project-structures/) 50 | - [파이썬 실행 환경을 지탱하는 도구들](https://www.holaxprogramming.com/2017/07/15/python-virtual-environments/) 51 | 52 | ## Run 53 | 54 | ``` 55 | $ python3 -m unittest 56 | ``` 57 | 58 | ## How to prepare coding interviews 59 | 60 | - [Google](https://careers.google.com/how-we-hire/interview/) 61 | - [Facebook](https://www.facebook.com/notes/facebook-engineering/get-that-job-at-facebook/10150964382448920/) 62 | 63 | ## PS Sites 64 | 65 | - [Hacker Rank](https://www.hackerrank.com/dashboard) 66 | - [LeetCode](https://leetcode.com/) 67 | - [Codility](https://codility.com/programmers/) 68 | - [Kaggle](https://www.kaggle.com/) 69 | - [Visualgo](https://visualgo.net/en) 70 | - [Algorithm Visualizer](http://algo-visualizer.jasonpark.me/#path=backtracking/knight's_tour/basic) 71 | 72 | ## Contents 73 | 74 | - [Testing](#testing) 75 | - [Time Complexity](#time-complexity) 76 | - [Big-O Notations](#big-o-notations) 77 | - [Space Complexity](#space-complexity) 78 | - [Algorithm Solving Strategies](#algorithm-solving-strategies) 79 | - [Divide and Conquer](#divide-and-conquer) 80 | - [Dynamic Programming](#dynamic-programming) 81 | - [Greedy Method](#greedy-method) 82 | - [Combinatorial Search](#combinatorial-search) 83 | - [Combinatorial Optimization](#combinatorial-optimization) 84 | - [Algorithms](#algorithms) 85 | - [Numerical Analysis](#numerical-analysis) 86 | - [Number Theory](#number-theory) 87 | - [Computational Geometry](#computational-geometry) 88 | - [Data Structures](#data-structures) 89 | - [Bitmask](#bitmask) 90 | - [Dynamic Array](#dynamic-array) 91 | - [Linked List](#linked-list) 92 | - [Queue](#queue) 93 | - [Stack](#stack) 94 | - [String](#string) 95 | - [Tree](#tree) 96 | - [Graph](#graph) 97 | 98 | ## Testing 99 | 100 | #### Time Complexity와 Space Complexity 101 | 102 | 알고리듬을 테스트하면서 가장 고려할 요소는 Time Complexity와 Space complexity이다. 103 | 104 | #### Time Complexity 105 | 106 | 107 | 108 | Time Complexity(시간 복잡도)는 문제를 해결하는데 걸리는 시간과 입력의 함수 관계를 표현한다. 얼마나 많은 데이터를 입력 받았는지 그에 따라 CPU는 얼마나 사용하는지 수행 시간은 얼마나 걸리는지를 표현할 수 있다. 109 | 110 | 가장 많이 쓰이는 표현법으로는 알고리듬의 실행 시간의 상한을 나타내는 `Big-O` 표기법이 있다. 111 | 112 | #### Big-O Notations 113 | 114 | 115 | 116 | Big-O | Operations for 10 things | Operations for 100 things 117 | --|--|-- 118 | O(1) | 1 | 1 119 | O(log n) | 3 | 7 120 | O(n log n) | 30 | 700 | 121 | 0(n^2) | 100 | 10000 | 122 | 123 | > Solutions - https://www.martinkysel.com/codility-solutions/ 124 | 125 | `O(1) - Constant Time` 126 | 127 | 입력되는 데이터양과 상관없이 일정한 실행 시간을 가진다. 128 | 129 | `O(log n) - Logarithmic Time` 130 | 131 | - 입력 데이터 양이 많아져도 수행 시간이 조금씩 늘어난다. 132 | - 시간에 비례하여, 탐색 가능한 데이터양이 2의 n승이 된다. 133 | 134 | > Binary Search 135 | 136 | 137 | `O(n) - Linear Time` 138 | 139 | - 입력 데이터 양에 따라 수행 시간이 정비례한다. 140 | 141 | > 선형 탐색, for 문을 통한 탐색을 생각하면 되겠다. 142 | 143 | `O(n log n) - Linearithmic time` 144 | 145 | - 입력 데이터 양이 n배 많이 진다면, 수행 시간은 n배 보다 조금 더 많아 진다. 146 | - 정비례하지 않는다. 147 | 148 | > 예를 들면, 이진 트리 정렬은 n 크기의 배열 각 요소를 하나하나 삽입하여 이진 트리를 만든다. 자가 균형 이진 탐색 트리의 삽입 연산은 O(log n)시간이 걸리기 때문에, 전체 알고리즘은 Linearithmic time이 걸린다. 149 | 150 | `O(n^2) - Quadratic Time` 151 | 152 | - 입력 데이터의 양에 따라 수행 시간은 제곱에 비례한다. 153 | 154 | > Bubble Sort 155 | 156 | #### Space Complexity 157 | 158 | #### `이하 컨텐츠를 채우는데 함께하고 싶은 분은 PR을 보내주세요! 🤗` 159 | 160 | ## Algorithm Solving Strategies 161 | 162 | #### Divide and Conquer 163 | 164 | #### Dynamic Programming 165 | 166 | #### Greedy Method 167 | 168 | ## Algorithms 169 | 170 | #### Numerical Analysis 171 | 172 | #### Number Theory 173 | 174 | #### Computational Geometry 175 | 176 | ## Data Structures 177 | 178 | #### Bitmask 179 | 180 | #### Dynamic Arra 181 | 182 | #### Linked List 183 | 184 | #### Queue 185 | 186 | #### Stack 187 | 188 | #### String 189 | 190 | #### Tree 191 | 192 | #### Graph 193 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | 2 | python-algorithms 3 | ================== 4 | 5 | Practices to solve problems with Python -------------------------------------------------------------------------------- /algorithms/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Description for Package 3 | """ 4 | __version__ = '1.0.0' 5 | -------------------------------------------------------------------------------- /algorithms/array.py: -------------------------------------------------------------------------------- 1 | 2 | 3 | class Array(object): 4 | def __init__(self, numbers): 5 | self.numbers = numbers 6 | 7 | def __len__(self): 8 | return len(self.numbers) 9 | 10 | def __str__(self): 11 | result = '[' 12 | for n in self.numbers: 13 | result += str(n) + ', ' 14 | return result[:-2] + ']' 15 | 16 | def sum(self, size): 17 | if size != len(self.numbers): 18 | raise Exception('array size is not matched') 19 | result = sum(self.numbers) 20 | return result 21 | 22 | def rotate(self, shift): 23 | head = shift % len(self.numbers) 24 | self.numbers = self.numbers[head:] + self.numbers[:head] 25 | return self.numbers 26 | 27 | @staticmethod 28 | def pop(numbers): 29 | numbers.pop() 30 | 31 | 32 | -------------------------------------------------------------------------------- /algorithms/basic.py: -------------------------------------------------------------------------------- 1 | """ 2 | Basic algorithms 3 | """ 4 | from collections import Counter 5 | 6 | 7 | class Basic: 8 | 9 | @classmethod 10 | def factorial(cls, number): 11 | """ 12 | :return: recursive pattern if n = 5, 5 * 4 * 3 * 2 * 1 = 120 13 | """ 14 | if number == 1: 15 | return 1 16 | return number * cls.factorial(number - 1) 17 | 18 | @classmethod 19 | def fibonacci(cls, index): 20 | """ 21 | :return: 0, 1, 1, 2, 3, 5, 8, 13, 21 ... 22 | """ 23 | if index == 0 or index == 1: 24 | return index 25 | return cls.fibonacci(index - 2) + cls.fibonacci(index - 1) 26 | 27 | @classmethod 28 | def time_conversion(cls, formatted_time: str): 29 | hours = int(formatted_time[:2]) 30 | mins = int(formatted_time[3:5]) 31 | seconds = int(formatted_time[6:8]) 32 | if 'PM' in formatted_time and hours == 12: 33 | pass 34 | elif 'PM' in formatted_time: 35 | hours += 12 36 | elif hours >= 12: 37 | hours -= 12 38 | return '{:02}:{:02}:{:02}'.format(hours, mins, seconds) 39 | 40 | @staticmethod 41 | def multiple_sum(num1, num2, end): 42 | total = 0 43 | for i in range(1, end): 44 | if i % num1 == 0 or i % num2 == 0: 45 | total += i 46 | return total 47 | 48 | @staticmethod 49 | def ransom_note(magazine: str, ransom: str): 50 | magazine_words = magazine.split(' ') 51 | ransom_words = ransom.split(' ') 52 | magazine_word_counts = Counter(magazine_words) 53 | ransom_word_counts = Counter(ransom_words) 54 | for word in ransom_words: 55 | if magazine_word_counts[word] < ransom_word_counts[word]: 56 | return False 57 | return True 58 | 59 | @staticmethod 60 | def compare_the_triplets(alice_points, bob_points): 61 | alice_score = 0 62 | bob_score = 0 63 | for i in range(len(alice_points)): 64 | if alice_points[i] > bob_points[i]: 65 | alice_score += 1 66 | elif alice_points[i] == bob_points[i]: 67 | pass 68 | else: 69 | bob_score += 1 70 | return ' '.join(map(lambda x: str(x), [alice_score, bob_score])) 71 | 72 | 73 | -------------------------------------------------------------------------------- /algorithms/dp.py: -------------------------------------------------------------------------------- 1 | 2 | 3 | class DP: 4 | 5 | def __init__(self, max_size=100): 6 | self.values = [0] * max_size 7 | self.values[1] = 1 8 | 9 | def fibonacci(self, index): 10 | if index == 0 or index == 1: 11 | return self.values[index] 12 | if self.values[index] != 0: 13 | return self.values[index] 14 | self.values[index] = self.fibonacci(index - 2) + self.fibonacci(index - 1) 15 | return self.values[index] 16 | 17 | 18 | -------------------------------------------------------------------------------- /algorithms/math.py: -------------------------------------------------------------------------------- 1 | 2 | 3 | class Math(object): 4 | 5 | @staticmethod 6 | def lonely_integer(numbers: list): 7 | value = 0 8 | for number in numbers: 9 | value = value ^ number 10 | return value 11 | 12 | @staticmethod 13 | def bin(value: int): 14 | buffer = [''] * 16 15 | char_index = len(buffer) 16 | while value > 0: 17 | char_index -= 1 18 | buffer[char_index] = '01'[value % 2] 19 | value = int(value / 2) 20 | return ''.join(buffer) 21 | 22 | @staticmethod 23 | def oct(value: int): 24 | buffer = [''] * 16 25 | char_index = len(buffer) 26 | while value > 0: 27 | char_index -= 1 28 | buffer[char_index] = '01234567'[value % 8] 29 | value = int(value / 8) 30 | return ''.join(buffer) 31 | 32 | @staticmethod 33 | def prime(value: int): 34 | if value == 0 or value == 1: 35 | return False 36 | if value == 2 or value == 3: 37 | return True 38 | if value % 2 == 0 or value % 3 == 0: 39 | return False 40 | j = 5 41 | while (j ** 2) <= value: 42 | if value % j == 0 or value % (j + 2) == 0: 43 | return False 44 | j += 6 45 | return True 46 | 47 | 48 | -------------------------------------------------------------------------------- /algorithms/queue.py: -------------------------------------------------------------------------------- 1 | 2 | 3 | class AbstractQueue(object): 4 | 5 | def __init__(self): 6 | self.head = 0 7 | self.size = 0 8 | self.items = [] 9 | 10 | def put(self): 11 | raise NotImplementedError() 12 | 13 | def get(self): 14 | raise NotImplementedError() 15 | 16 | def qsize(self): 17 | return self.size; 18 | 19 | def empty(self): 20 | return not self.qsize() 21 | 22 | def full(self): 23 | return self.qsize() == self.size 24 | 25 | 26 | class Queue(AbstractQueue): 27 | 28 | def __init__(self, capacity=10): 29 | self.head = 0 30 | self.size = 0 31 | self.items = [None] * capacity 32 | 33 | def put(self, item): 34 | self.items[(self.head + self.size) % len(self.items)] = item 35 | self.size += 1 36 | 37 | def get(self): 38 | if self.empty(): 39 | raise IndexError('Queue is empty') 40 | self.size -= 1 41 | result = self.items[self.head] 42 | self.head = (self.head + 1) % len(self.items) 43 | return result 44 | 45 | 46 | -------------------------------------------------------------------------------- /algorithms/search.py: -------------------------------------------------------------------------------- 1 | 2 | 3 | class Search: 4 | 5 | @staticmethod 6 | def binary_search(numbers, x): 7 | return Search.binary_search_recursive(numbers, x, 0, len(numbers) - 1) 8 | 9 | @staticmethod 10 | def binary_search_recursive(numbers, x, left, right): 11 | if left > right: 12 | return -1 13 | mid = int(left + (right - left) / 2) 14 | if numbers[mid] == x: 15 | return mid 16 | elif x < numbers[mid]: 17 | return Search.binary_search_recursive(numbers, x, left, mid - 1) 18 | elif x > numbers[mid]: 19 | return Search.binary_search_recursive(numbers, x, mid + 1, right) 20 | else: 21 | return -1 22 | 23 | @staticmethod 24 | def binary_search_iterative(numbers, x): 25 | left = 0 26 | right = len(numbers) - 1 27 | mid = int(left + (right - left) / 2) 28 | while left <= right: 29 | if numbers[mid] == x: 30 | return mid 31 | elif x < numbers[mid]: 32 | right = mid - 1 33 | mid = int(left + (right - left) / 2) 34 | elif x > numbers[mid]: 35 | left = mid + 1 36 | mid = int(left + (right - left) / 2) 37 | else: 38 | return -1 39 | return -1 40 | 41 | @staticmethod 42 | def index(flavors: list, value, exclude): 43 | for i in range(0, len(flavors)): 44 | if flavors[i] == value and i != exclude: 45 | return i 46 | return -1 47 | 48 | @staticmethod 49 | def ice_cream_parlor(money, flavors: list): 50 | """ 51 | https://www.hackerrank.com/challenges/ctci-ice-cream-parlor 52 | """ 53 | sorted_flavors = sorted(flavors) 54 | for i, flavor in enumerate(sorted_flavors): 55 | complement = money - flavor 56 | location = Search.binary_search_iterative(sorted_flavors, complement) 57 | if 0 <= location < len(sorted_flavors): 58 | a = Search.index(flavors, sorted_flavors[i], -1) 59 | b = Search.index(flavors, complement, a) 60 | return ' '.join(map(lambda x: str(x), [min(a + 1, b + 1), max(a + 1, b + 1)])) -------------------------------------------------------------------------------- /algorithms/sort.py: -------------------------------------------------------------------------------- 1 | from enum import Enum 2 | 3 | 4 | class SortType(Enum): 5 | ASC = 1 6 | DESC = 2 7 | 8 | 9 | class Sorting: 10 | def __init__(self, numbers: list): 11 | self.numbers = numbers 12 | 13 | def bubble_sort(self): 14 | print('bubble_sort = {}'.upper().format(self.numbers)) 15 | for i in range(len(self.numbers)): 16 | swapped = False 17 | for j in range(len(self.numbers) - 1): 18 | if self.numbers[j] > self.numbers[j + 1]: 19 | self.numbers[j], self.numbers[j + 1] = self.numbers[j + 1], self.numbers[j] 20 | swapped = True 21 | if not swapped: 22 | break 23 | print('{}'.format(self.numbers)) 24 | return self.numbers 25 | 26 | def selection_sort(self, sort_type=SortType.ASC): 27 | print('selection_sort(type={}) = {}'.upper().format(sort_type, self.numbers)) 28 | for i in range(len(self.numbers)): 29 | index = i 30 | for j in range(i, len(self.numbers)): 31 | if sort_type == SortType.ASC and self.numbers[index] > self.numbers[j]: 32 | index = j 33 | if sort_type == SortType.DESC and self.numbers[index] < self.numbers[j]: 34 | index = j 35 | self.numbers[i], self.numbers[index] = self.numbers[index], self.numbers[i] 36 | return self.numbers 37 | 38 | def selection(self, k, sort_type=SortType.ASC): 39 | for i in range(len(self.numbers)): 40 | index = i 41 | for j in range(i, len(self.numbers)): 42 | if sort_type == SortType.ASC and self.numbers[index] > self.numbers[j]: 43 | index = j 44 | if sort_type == SortType.DESC and self.numbers[index] < self.numbers[j]: 45 | index = j 46 | self.numbers[i], self.numbers[index] = self.numbers[index], self.numbers[i] 47 | if i == k - 1: 48 | return self.numbers[i] 49 | 50 | def merge_sort(self): 51 | print('merge_sort = {}'.upper().format(self.numbers)) 52 | temp = [0] * len(self.numbers) 53 | self.merge_sort_recursive(temp, 0, len(self.numbers) - 1) 54 | 55 | def merge_sort_recursive(self, temp, left_start, right_end): 56 | if left_start >= right_end: 57 | return 58 | mid = int((left_start + right_end) / 2) 59 | self.merge_sort_recursive(temp, left_start, mid) 60 | self.merge_sort_recursive(temp, mid + 1, right_end) 61 | self.merge(temp, left_start, right_end) 62 | 63 | def merge(self, temp, left_start, right_end): 64 | left_end = int((right_end + left_start) / 2) 65 | right_start = left_end + 1 66 | 67 | size = right_end - left_start + 1 68 | left = left_start 69 | right = right_start 70 | index = left_start 71 | print('MERGE > left={} right={} index={} size={}'.format(left, right, index, size)) 72 | while left <= left_end and right <= right_end: 73 | if self.numbers[left] <= self.numbers[right]: 74 | temp[index] = self.numbers[left] 75 | left += 1 76 | else: 77 | temp[index] = self.numbers[right] 78 | right += 1 79 | index += 1 80 | 81 | Sorting.array_copy(self.numbers, left, temp, index, left_end - left + 1) 82 | Sorting.array_copy(self.numbers, right, temp, index, right_end - right + 1) 83 | Sorting.array_copy(temp, left_start, self.numbers, left_start, size) 84 | print(self.numbers) 85 | 86 | @staticmethod 87 | def array_copy(src: list, src_pos: int, dest: list, dest_pos: int, size: int): 88 | for i in range(size): 89 | dest[dest_pos + i] = src[src_pos + i] 90 | 91 | 92 | -------------------------------------------------------------------------------- /algorithms/stack.py: -------------------------------------------------------------------------------- 1 | 2 | 3 | class Stack: 4 | def __init__(self, max_size=10): 5 | self.items = [None] * max_size 6 | self.max_size = max_size 7 | self.top = -1 8 | 9 | def push(self, item): 10 | if self.is_full(): 11 | self.expand() 12 | self.top += 1 13 | self.items[self.top] = item 14 | 15 | def pop(self): 16 | if self.is_empty(): 17 | raise IndexError("Stack is empty") 18 | item = self.items[self.top] 19 | self.items[self.top] = None 20 | self.top -= 1 21 | return item 22 | 23 | def expand(self): 24 | self.max_size = self.max_size * 2 25 | new_items = [None] * (self.max_size) 26 | for index, item in enumerate(self.items): 27 | new_items[index] = item 28 | self.items = new_items 29 | 30 | def is_empty(self): 31 | return self.top == -1 32 | 33 | def is_full(self): 34 | return self.top == (self.max_size - 1) 35 | 36 | def has(self, value): 37 | return value in self.items 38 | 39 | BRACKET_PAIRS = {"{": "}", "[": "]", "(": ")"} 40 | 41 | def balanced_brackets(expression: str): 42 | if not type(expression) is str or not expression: 43 | return False 44 | stack = Stack() 45 | for char in expression: 46 | if char in BRACKET_PAIRS: 47 | stack.push(BRACKET_PAIRS[char]) 48 | elif not stack.is_empty() and stack.has(char): 49 | stack.pop() 50 | else: 51 | return False 52 | return stack.is_empty() 53 | 54 | -------------------------------------------------------------------------------- /algorithms/string.py: -------------------------------------------------------------------------------- 1 | """ 2 | About String 3 | 4 | Ascii - 1 byte 0-255, 0x00-0xFF, A(65) 5 | Unicode - 2 bytes 가(0xAC00) 6 | EUC-KR - 2 bytes 7 | UTF-8 - 2 bytes+ Korean 3 bytes 8 | UTF-16 - 2 bytes+ Korean 4 bytes 9 | """ 10 | 11 | 12 | class String: 13 | 14 | @classmethod 15 | def reverse(cls, word: str): 16 | if (word is None) or (word == ''): 17 | return None 18 | start = 0 19 | end = len(word) - 1 20 | char_list = list(word) 21 | while start < end: 22 | temp = char_list[start] 23 | char_list[start] = char_list[end] 24 | char_list[end] = temp 25 | start += 1 26 | end -= 1 27 | return ''.join(char_list) 28 | 29 | @classmethod 30 | def check_anagrams(cls, str1: str, str2: str): 31 | if str1 == '' or str1 is None or str2 == '' or str2 is None: 32 | return False 33 | if len(str1) != len(str2): 34 | return False 35 | str1 = ''.join(sorted(str1)) 36 | str2 = ''.join(sorted(str2)) 37 | return str1 == str2 38 | 39 | @classmethod 40 | def making_anagrams(cls, str1: str, str2: str): 41 | delete_count = 0 42 | letter_counts = [0] * (ord('z') - ord('a') + 1) 43 | 44 | for char in str1: 45 | letter_counts[ord(char) - ord('a')] += 1 46 | 47 | for char in str2: 48 | letter_counts[ord(char) - ord('a')] -= 1 49 | 50 | for number in letter_counts: 51 | delete_count += abs(number) 52 | 53 | return delete_count 54 | 55 | @classmethod 56 | def advanced_anagrams(cls, input: str): 57 | if not (len(input) % 2) == 0: 58 | return -1 59 | str1 = input[0:int(len(input) / 2)] 60 | str2 = input[int(len(input) / 2):] 61 | letter_counts = [0] * (ord('z') - ord('a') + 1) 62 | count = 0 63 | 64 | for i in range(len(str1)): 65 | letter_counts[ord(str1[i]) - ord('a')] += 1 66 | letter_counts[ord(str2[i]) - ord('a')] -= 1 67 | 68 | for number in letter_counts: 69 | count += abs(number) 70 | 71 | return count / 2 72 | 73 | @staticmethod 74 | def pangrams(sentence: str): 75 | sentence = sentence.lower().replace(' ', '') 76 | letter_counts = [0] * (ord('z') - ord('a') + 1) 77 | 78 | for letter in sentence: 79 | letter_counts[ord(letter) - ord('a')] += 1 80 | 81 | for count in letter_counts: 82 | if count == 0: 83 | return False 84 | return True 85 | 86 | @staticmethod 87 | def is_iterative_pattern(sentence: str): 88 | if sentence is None or sentence == '': 89 | return False 90 | if len(sentence) == 1: 91 | return True 92 | divide_number = 2 93 | while divide_number <= len(sentence): 94 | if len(sentence) % divide_number != 0: 95 | divide_number += 1 96 | continue 97 | parts = String.split(sentence, divide_number) 98 | divide_number += 1 99 | if String.is_matched(parts): 100 | return True 101 | return False 102 | 103 | @staticmethod 104 | def split(sentence: str, divide_number: int): 105 | size = int(len(sentence) / divide_number) 106 | array = [] 107 | for i in range(divide_number): 108 | start = i * size 109 | end = (i + 1) * size 110 | array.append(sentence[start:end]) 111 | return array 112 | 113 | @staticmethod 114 | def is_matched(parts: list): 115 | for i in range(1, len(parts)): 116 | if parts[i] != parts[0]: 117 | return False 118 | return True 119 | -------------------------------------------------------------------------------- /algorithms/tree.py: -------------------------------------------------------------------------------- 1 | """ 2 | Example 1: 3 | 2 4 | / \ 5 | 1 3 6 | Binary tree [2,1,3], return true. 7 | Example 2: 8 | 1 9 | / \ 10 | 2 3 11 | Binary tree [1,2,3], return false. 12 | """ 13 | 14 | 15 | class Node: 16 | 17 | def __init__(self, value=0): 18 | self.value = value 19 | self.left = None 20 | self.right = None 21 | 22 | @classmethod 23 | def check_bst(cls, root, min_value, max_value): 24 | if root is None: 25 | return True 26 | return ( 27 | min_value < root.value < max_value and 28 | Node.check_bst(root.left, min_value, root.value) and 29 | Node.check_bst(root.right, root.value, max_value)) 30 | 31 | @classmethod 32 | def is_binary_search_tree(cls, root): 33 | return Node.check_bst(root, -float('inf'), float('inf')) 34 | 35 | 36 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | import io 2 | from setuptools import find_packages, setup 3 | 4 | 5 | # Read in the README for the long description on PyPI 6 | def long_description(): 7 | with io.open('README.rst', 'r', encoding='utf-8') as f: 8 | readme = f.read() 9 | return readme 10 | 11 | setup(name='python-algorithms', 12 | version='1.0.0', 13 | description='Practices to solve problems with Python', 14 | long_description=long_description(), 15 | url='https://github.com/stunstunstun/python-algorithms', 16 | author='stunstunstun', 17 | license='MIT', 18 | packages=find_packages(), 19 | python_requires='>=3', 20 | classifiers=[ 21 | 'Programming Language :: Python :: 2.7', 22 | 'Programming Language :: Python :: 3', 23 | 'Programming Language :: Python :: 3.6', 24 | ], 25 | zip_safe=False) -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stunstunstun/python-algorithms/c38ac55090d90deb421bfed1b48ece0a5f56351f/tests/__init__.py -------------------------------------------------------------------------------- /tests/test_array.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | from algorithms.array import Array 3 | 4 | 5 | class TestArray(unittest.TestCase): 6 | """ 7 | Create class instance 8 | """ 9 | def setUp(self): 10 | self.array = Array([1, 11, 98, 3, 50, 72, 22, 29, 99, 2]) 11 | 12 | def test_sum(self): 13 | """ 14 | Test that the result sum of all numbers 15 | """ 16 | result = self.array.sum(10) 17 | self.assertEqual(result, 387) 18 | 19 | def test_sum_raise_exception(self): 20 | """ 21 | Tests that an exception occurs when the number of arguments is different 22 | """ 23 | self.assertRaises(Exception, lambda: self.array.sum(5)) 24 | 25 | def test_rotate(self): 26 | self.assertEqual(self.array.rotate(2), [98, 3, 50, 72, 22, 29, 99, 2, 1, 11]) 27 | 28 | def test_reference(self): 29 | numbers = [98, 3, 50, 72, 22, 29, 99, 2, 1, 11] 30 | Array.pop(numbers) 31 | self.assertEqual(numbers, [98, 3, 50, 72, 22, 29, 99, 2, 1]) 32 | 33 | def tearDown(self): 34 | """ 35 | Print array elements 36 | """ 37 | print('length = {}, elements = {}'.format(self.array.__len__(), self.array)) 38 | -------------------------------------------------------------------------------- /tests/test_basic.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | from algorithms.basic import Basic 3 | 4 | 5 | class TestBasic(unittest.TestCase): 6 | 7 | def test_range_loop(self): 8 | # 1 2 3 9 | n = [1, 2] 10 | n.append(3) 11 | # 3 2 1 12 | n = list(reversed(n)) 13 | self.assertEqual(n, [3, 2, 1]) 14 | self.assertEqual(n, list(range(3, 0, -1))) 15 | # 1 3 5 16 | n = [1, 3, 5] 17 | self.assertEqual(n, list(range(1, 6, 2))) 18 | 19 | def test_factorial(self): 20 | self.assertEquals(Basic.factorial(5), 120) 21 | 22 | def test_fibonacci(self): 23 | self.assertEquals(Basic.fibonacci(0), 0) 24 | self.assertEquals(Basic.fibonacci(1), 1) 25 | self.assertEquals(Basic.fibonacci(2), 1) 26 | self.assertEquals(Basic.fibonacci(3), 2) 27 | self.assertEquals(Basic.fibonacci(4), 3) 28 | self.assertEquals(Basic.fibonacci(5), 5) 29 | self.assertEquals(Basic.fibonacci(6), 8) 30 | self.assertEquals(Basic.fibonacci(7), 13) 31 | self.assertEquals(Basic.fibonacci(8), 21) 32 | self.assertEquals(Basic.fibonacci(9), 34) 33 | self.assertEquals(Basic.fibonacci(31), 1346269) 34 | 35 | def test_time_conversion(self): 36 | self.assertEquals(Basic.time_conversion('07:05:45PM'), '19:05:45') 37 | self.assertEquals(Basic.time_conversion('12:40:22AM'), '00:40:22') 38 | self.assertEquals(Basic.time_conversion('12:45:54PM'), '12:45:54') 39 | 40 | def test_multiple_sum(self): 41 | # 3 5 6 9 10 12 15 18 42 | self.assertEqual(Basic.multiple_sum(3, 5, 1000), 233168) 43 | 44 | def test_hash_table(self): 45 | self.assertEqual(Basic.ransom_note('give me one grand today night', 'give one grand today'), True) 46 | self.assertEqual(Basic.ransom_note('apgo clm w lxkvg mwz elo bg elo lxkvg elo apgo apgo w elo bg', 'elo lxkvg bg mwz clm w'), True) 47 | 48 | def test_compare_the_triplets(self): 49 | result = Basic.compare_the_triplets([5, 6, 7], [3, 6, 10]) 50 | self.assertEquals(result, '1 1') 51 | 52 | 53 | -------------------------------------------------------------------------------- /tests/test_dp.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | from algorithms.dp import DP 3 | 4 | 5 | class TestDP(unittest.TestCase): 6 | 7 | def test_fibonacci(self): 8 | dp = DP(100) 9 | self.assertEquals(dp.fibonacci(0), 0) 10 | self.assertEquals(dp.fibonacci(1), 1) 11 | self.assertEquals(dp.fibonacci(2), 1) 12 | self.assertEquals(dp.fibonacci(3), 2) 13 | self.assertEquals(dp.fibonacci(4), 3) 14 | self.assertEquals(dp.fibonacci(5), 5) 15 | self.assertEquals(dp.fibonacci(6), 8) 16 | self.assertEquals(dp.fibonacci(7), 13) 17 | self.assertEquals(dp.fibonacci(8), 21) 18 | self.assertEquals(dp.fibonacci(9), 34) 19 | self.assertEquals(dp.fibonacci(31), 1346269) 20 | self.assertEquals(dp.fibonacci(40), 102334155) 21 | -------------------------------------------------------------------------------- /tests/test_math.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | from algorithms.math import Math 3 | 4 | 5 | class TestMath(unittest.TestCase): 6 | 7 | def test_lonely_integer(self): 8 | self.assertEqual(Math.lonely_integer([0, 0, 1, 2, 1]), 2) 9 | self.assertEqual(Math.lonely_integer([1]), 1) 10 | self.assertEqual(Math.lonely_integer([1, 1, 2]), 2) 11 | 12 | def test_format(self): 13 | self.assertEqual(Math.bin(10), '1010') 14 | self.assertEqual(Math.bin(255), '11111111') 15 | self.assertEqual(Math.oct(10), '12') 16 | self.assertEqual(Math.oct(255), '377') 17 | 18 | def test_prime(self): 19 | self.assertEqual(Math.prime(0), False) 20 | self.assertEqual(Math.prime(1), False) 21 | self.assertEqual(Math.prime(2), True) 22 | self.assertEqual(Math.prime(3), True) 23 | self.assertEqual(Math.prime(4), False) 24 | self.assertEqual(Math.prime(5), True) 25 | self.assertEqual(Math.prime(13), True) 26 | self.assertEqual(Math.prime(1000000007), True) 27 | self.assertEqual(Math.prime(1000003), True) 28 | -------------------------------------------------------------------------------- /tests/test_queue.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | from algorithms.queue import Queue 3 | 4 | 5 | class TestQueue(unittest.TestCase): 6 | 7 | def test_queue(self): 8 | q = Queue() 9 | self.assertEqual(q.empty(), True) 10 | 11 | q.put(0) 12 | q.put(1) 13 | self.assertEqual(q.get(), 1) 14 | self.assertEqual(q.qsize(), 1) 15 | self.assertEqual(q.empty(), False) 16 | 17 | for i in range(2, 10): 18 | print(i) 19 | q.put(i) 20 | 21 | self.assertEqual(q.full(), True) 22 | 23 | def test_queue(self): 24 | q = Queue() 25 | self.assertRaises(IndexError, q.get) 26 | -------------------------------------------------------------------------------- /tests/test_search.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | from algorithms.search import Search 3 | 4 | 5 | class TestSearch(unittest.TestCase): 6 | 7 | def test_binary_search_recursive(self): 8 | self.assertEqual(Search.binary_search([1, 2, 3, 11, 22, 29, 50, 72, 98, 99], 11), 3) 9 | 10 | def test_binary_search_iterative(self): 11 | self.assertEqual(Search.binary_search_iterative([1, 2, 3, 11, 22, 29, 50, 72, 98, 99], 11), 3) 12 | self.assertEqual(Search.binary_search_iterative([1, 2, 3, 4, 5], 3), 2) 13 | 14 | def test_ice_cream_parlor(self): 15 | self.assertEqual(Search.ice_cream_parlor(4, [1, 4, 5, 3, 2]), '1 4') 16 | self.assertEqual(Search.ice_cream_parlor(4, [2, 2, 4, 3]), '1 2') 17 | -------------------------------------------------------------------------------- /tests/test_sort.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | from algorithms.sort import Sorting, SortType 3 | 4 | 5 | class TestSort(unittest.TestCase): 6 | """ 7 | Create class instance 8 | """ 9 | def setUp(self): 10 | self.sorting = Sorting([1, 11, 98, 3, 50, 72, 22, 29, 99, 2]) 11 | 12 | def test_bubble_sort(self): 13 | self.assertEqual(self.sorting.bubble_sort(), [1, 2, 3, 11, 22, 29, 50, 72, 98, 99]) 14 | 15 | def test_selection_sort(self): 16 | self.assertEqual(self.sorting.selection_sort(), [1, 2, 3, 11, 22, 29, 50, 72, 98, 99]) 17 | self.assertEqual(self.sorting.selection_sort(sort_type=SortType.DESC), [99, 98, 72, 50, 29, 22, 11, 3, 2, 1]) 18 | 19 | def test_selection(self): 20 | self.assertEqual(self.sorting.selection(1), 1) 21 | self.assertEqual(self.sorting.selection(2), 2) 22 | self.assertEqual(self.sorting.selection(3), 3) 23 | self.assertEqual(self.sorting.selection(4), 11) 24 | self.assertEqual(self.sorting.selection(5), 22) 25 | 26 | self.assertEqual(self.sorting.selection(1, sort_type=SortType.DESC), 99) 27 | self.assertEqual(self.sorting.selection(2, sort_type=SortType.DESC), 98) 28 | self.assertEqual(self.sorting.selection(3, sort_type=SortType.DESC), 72) 29 | 30 | def test_merge_sort(self): 31 | self.sorting.merge_sort() 32 | self.assertEqual(self.sorting.numbers, [1, 2, 3, 11, 22, 29, 50, 72, 98, 99]) 33 | -------------------------------------------------------------------------------- /tests/test_stack.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | from algorithms.stack import Stack, balanced_brackets 3 | 4 | 5 | class TestStack(unittest.TestCase): 6 | 7 | def test_stack(self): 8 | stack = Stack() 9 | stack.push(2) 10 | stack.push(4) 11 | stack.push(6) 12 | 13 | self.assertEqual(stack.pop(), 6) 14 | self.assertEqual(stack.is_empty(), False) 15 | self.assertEqual(stack.top, 1) 16 | self.assertEqual(stack.has(4), True) 17 | self.assertEqual(stack.is_full(), False) 18 | 19 | for i in range(8): 20 | stack.push(i) 21 | self.assertEqual(stack.is_full(), True) 22 | self.assertEqual(stack.top, stack.max_size - 1) 23 | 24 | for i in range(10): 25 | stack.push(i) 26 | self.assertEqual(stack.is_full(), True) 27 | self.assertEqual(stack.top, stack.max_size - 1) 28 | 29 | def test_balanced_brackets(self): 30 | self.assertEqual(balanced_brackets(0), False) 31 | self.assertEqual(balanced_brackets(''), False) 32 | self.assertEqual(balanced_brackets('aabb'), False) 33 | self.assertEqual(balanced_brackets('{[(}])'), False) 34 | self.assertEqual(balanced_brackets('{[()]}'), True) 35 | self.assertEqual(balanced_brackets('}][}}(}][))]'), False) 36 | self.assertEqual(balanced_brackets('[](){()}'), True) 37 | self.assertEqual(balanced_brackets('({}([][]))[]()'), True) 38 | self.assertEqual(balanced_brackets('{)[](}]}]}))}(())('), False) 39 | -------------------------------------------------------------------------------- /tests/test_string.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | import binascii 3 | from algorithms.string import String 4 | 5 | 6 | class TestString(unittest.TestCase): 7 | 8 | def test_reverse(self): 9 | self.assertEqual(String.reverse('abcde'), 'edcba') 10 | self.assertEqual(String.reverse(None), None) 11 | self.assertEqual(String.reverse(''), None) 12 | 13 | def test_binary(self): 14 | self.assertEqual(int('1010', 2), 10) 15 | self.assertEqual(hex(255), '0xff') 16 | 17 | def test_ascii(self): 18 | for i in range(0, 256, 1): 19 | print('{} {:03} {} {}'.format(chr(i), i, hex(i).upper(), bin(i))) 20 | self.assertEqual(chr(65), 'A') 21 | self.assertEqual(chr(44032), '가') 22 | 23 | def test_unicode(self): 24 | for i in range(ord(u'A'), ord(u'Z'), 1): 25 | print('{} {:03} {} {}'.format(chr(i), i, hex(i).upper(), bin(i))) 26 | for i in range(ord(u'가'), ord(u'겮'), 1): 27 | print('{} {:03} {} {}'.format(chr(i), i, hex(i).upper(), bin(i))) 28 | self.assertEqual(hex(ord(u'가')), '0xac00') 29 | 30 | b = u'A'.encode(encoding='utf-8') 31 | print('{} {} {}'.format('A', 'utf-8', binascii.hexlify(b))) 32 | self.assertEqual(len(b), 1) 33 | b = u'A'.encode(encoding='euc-kr') 34 | print('{} {} {}'.format('A', 'euc-kr', binascii.hexlify(b))) 35 | self.assertEqual(len(b), 1) 36 | b = u'A'.encode(encoding='utf-16') 37 | print('{} {} {}'.format('A', 'utf-16', binascii.hexlify(b))) 38 | self.assertEqual(len(b), 4) 39 | 40 | b = u'가'.encode(encoding='utf-8') 41 | print('{} {} {}'.format('가', 'utf-8', binascii.hexlify(b))) 42 | self.assertEqual(len(b), 3) 43 | b = u'가'.encode(encoding='euc-kr') 44 | print('{} {} {}'.format('가', 'euc-kr', binascii.hexlify(b))) 45 | self.assertEqual(len(b), 2) 46 | b = u'가'.encode(encoding='utf-16') 47 | print('{} {} {}'.format('가', 'utf-16', binascii.hexlify(b))) 48 | self.assertEqual(len(b), 4) 49 | b = u'가'.encode() 50 | self.assertEqual(len(b), 3) 51 | 52 | def test_check_anagrams(self): 53 | self.assertEqual(String.check_anagrams('abc', 'cab'), True) 54 | self.assertEqual(String.check_anagrams('bat', 'tab'), True) 55 | self.assertEqual(String.check_anagrams('abc', 'eft'), False) 56 | self.assertEqual(String.check_anagrams('ba', 'tab'), False) 57 | self.assertEqual(String.check_anagrams('abc', 'cb'), False) 58 | self.assertEqual(String.check_anagrams('', ''), False) 59 | self.assertEqual(String.check_anagrams(None, None), False) 60 | 61 | def test_making_anagrams(self): 62 | self.assertEqual(String.making_anagrams('cde', 'abc'), 4) 63 | self.assertEqual(String.making_anagrams('fcrxzw', 'jxjw'), 6) 64 | self.assertEqual(String.making_anagrams('fcrxzwscanmligyxyvym', 'jxwtrhvujlmrpdoqbisbwhmgpmeoke'), 30) 65 | 66 | def test_advanced_anagrams(self): 67 | self.assertEqual(String.advanced_anagrams('aaabbb'), 3) 68 | self.assertEqual(String.advanced_anagrams('ab'), 1) 69 | self.assertEqual(String.advanced_anagrams('abc'), -1) 70 | self.assertEqual(String.advanced_anagrams('mnop'), 2) 71 | self.assertEqual(String.advanced_anagrams('xyyx'), 0) 72 | self.assertEqual(String.advanced_anagrams('xaxbbbxx'), 1) 73 | self.assertEqual(String.advanced_anagrams('xaxbbbxa'), 1) 74 | self.assertEqual(String.advanced_anagrams('abxxabbx'), 1) 75 | 76 | self.assertEqual(String.advanced_anagrams('hhpddlnnsjfoyxpciioigvjqzfbpllssuj'), 10) 77 | self.assertEqual(String.advanced_anagrams('xulkowreuowzxgnhmiqekxhzistdocbnyozmnqthhpievvlj'), 13) 78 | self.assertEqual(String.advanced_anagrams('dnqaurlplofnrtmh'), 5) 79 | self.assertEqual(String.advanced_anagrams('aujteqimwfkjoqodgqaxbrkrwykpmuimqtgulojjwtukjiqrasqejbvfbixnchzsahpnyayutsgecwvcqngzoehrmeeqlgknnb'), 26) 80 | self.assertEqual(String.advanced_anagrams('lbafwuoawkxydlfcbjjtxpzpchzrvbtievqbpedlqbktorypcjkzzkodrpvosqzxmpad'), 15) 81 | self.assertEqual(String.advanced_anagrams('drngbjuuhmwqwxrinxccsqxkpwygwcdbtriwaesjsobrntzaqbe'), -1) 82 | self.assertEqual(String.advanced_anagrams('ubulzt'), 3) 83 | self.assertEqual(String.advanced_anagrams('vxxzsqjqsnibgydzlyynqcrayvwjurfsqfrivayopgrxewwruvemzy'), 13) 84 | self.assertEqual(String.advanced_anagrams('xtnipeqhxvafqaggqoanvwkmthtfirwhmjrbphlmeluvoa'), 13) 85 | self.assertEqual(String.advanced_anagrams('gqdvlchavotcykafyjzbbgmnlajiqlnwctrnvznspiwquxxsiwuldizqkkaawpyyisnftdzklwagv'), -1) 86 | 87 | def test_pangrams(self): 88 | self.assertEqual(String.pangrams('We promptly judged antique ivory buckles for the next prize'), True) 89 | self.assertEqual(String.pangrams('We promptly judged antique ivory buckles for the prize'), False) 90 | 91 | def test_matched_pattern(self): 92 | self.assertEqual(String.is_iterative_pattern('a'), True) 93 | self.assertEqual(String.is_iterative_pattern('aaa'), True) 94 | self.assertEqual(String.is_iterative_pattern('abcabc'), True) 95 | self.assertEqual(String.is_iterative_pattern('ababab'), True) 96 | 97 | self.assertEqual(String.is_iterative_pattern(''), False) 98 | self.assertEqual(String.is_iterative_pattern('ab'), False) 99 | self.assertEqual(String.is_iterative_pattern('ababc'), False) 100 | 101 | -------------------------------------------------------------------------------- /tests/test_tree.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | from algorithms.tree import Node 3 | 4 | 5 | class TestTree(unittest.TestCase): 6 | 7 | def test_is_binary_search_tree(self): 8 | tree = Node(4) 9 | tree.left = Node(2) 10 | tree.right = Node(6) 11 | tree.left.left = Node(1) 12 | tree.left.right = Node(3) 13 | tree.right.left = Node(5) 14 | tree.right.right = Node(7) 15 | self.assertEqual(Node.is_binary_search_tree(tree), True) 16 | 17 | tree = Node(3) 18 | tree.left = Node(2) 19 | tree.right = Node(6) 20 | tree.left.left = Node(1) 21 | tree.left.right = Node(4) 22 | tree.right.left = Node(5) 23 | tree.right.right = Node(7) 24 | self.assertEqual(Node.is_binary_search_tree(tree), False) 25 | -------------------------------------------------------------------------------- /tests/test_with_unittest.py: -------------------------------------------------------------------------------- 1 | """ 2 | available by this command 3 | $ python3 tests/test_with_unittest.py 4 | """ 5 | import unittest 6 | 7 | 8 | class Array: 9 | 10 | def __init__(self, size, numbers): 11 | self.number_of_swaps = 0 12 | self.size = size 13 | self.numbers = numbers 14 | self.count = 0 15 | 16 | def bubble_sort(self): 17 | for i in range(self.size): 18 | is_swap = False 19 | for j in range(self.size - 1): 20 | self.count += 1 21 | if self.numbers[j] > self.numbers[j + 1]: 22 | self.numbers[j], self.numbers[j + 1] = self.numbers[j + 1], self.numbers[j] 23 | self.number_of_swaps += 1 24 | is_swap = True 25 | if not is_swap: 26 | break 27 | 28 | 29 | class TestArray(unittest.TestCase): 30 | 31 | def test_bubble_sort(self): 32 | numbers = [3, 10, 9, 2, 100, 1, 99, 1000, 232, 7] 33 | print(numbers) 34 | array = Array(10, numbers) 35 | array.bubble_sort() 36 | 37 | number_of_swaps = array.number_of_swaps 38 | numbers = array.numbers 39 | 40 | self.assertEqual(array.number_of_swaps, 17) 41 | self.assertEqual(array.numbers, [1, 2, 3, 7, 9, 10, 99, 100, 232, 1000]) 42 | 43 | print('Array is sorted in {} swaps.'.format(number_of_swaps)) 44 | print(array.numbers) 45 | 46 | if __name__ == "__main__": 47 | unittest.main() 48 | --------------------------------------------------------------------------------