├── tests ├── algorithms │ ├── __init__.py │ ├── commons │ │ ├── __init__.py │ │ ├── shuffling_unit_test.py │ │ ├── selecting_unit_test.py │ │ ├── util_unit_test.py │ │ ├── union_find_unit_test.py │ │ └── sorting_unit_test.py │ ├── graphs │ │ ├── __init__.py │ │ ├── directed_cycle_unit_test.py │ │ ├── topological_sort_unit_test.py │ │ ├── max_flow_unit_test.py │ │ ├── minimum_spanning_tree_unit_test.py │ │ ├── connectivity_unit_test.py │ │ ├── search_unit_test.py │ │ ├── shortest_path_unit_test.py │ │ └── util.py │ └── strings │ │ ├── __init__.py │ │ ├── util.py │ │ ├── longest_repeated_substring_unit_test.py │ │ ├── sorting_unit_test.py │ │ └── substring_search_unit_test.py ├── data_structures │ ├── __init__.py │ ├── commons │ │ ├── __init__.py │ │ ├── bag_unit_test.py │ │ ├── priority_queue_unit_test.py │ │ ├── stack_unit_test.py │ │ ├── queue_unit_test.py │ │ ├── binary_search_tree_unit_test.py │ │ ├── hashed_set_unit_test.py │ │ └── hashed_map_unit_test.py │ ├── graphs │ │ ├── __init__.py │ │ └── graph_unit_test.py │ └── strings │ │ ├── __init__.py │ │ └── search_tries_unit_test.py └── __init__.py ├── pyalgs ├── algorithms │ ├── __init__.py │ ├── commons │ │ ├── __init__.py │ │ ├── shuffling.py │ │ ├── util.py │ │ ├── selecting.py │ │ ├── union_find.py │ │ └── sorting.py │ ├── graphs │ │ ├── __init__.py │ │ ├── topological_sort.py │ │ ├── directed_cycle.py │ │ ├── max_flow.py │ │ ├── search.py │ │ ├── connectivity.py │ │ ├── minimum_spanning_trees.py │ │ └── shortest_path.py │ └── strings │ │ ├── __init__.py │ │ ├── longest_repeated_substring.py │ │ ├── sorting.py │ │ └── substring_search.py ├── data_structures │ ├── __init__.py │ ├── commons │ │ ├── __init__.py │ │ ├── bag.py │ │ ├── stack.py │ │ ├── queue.py │ │ ├── hashed_set.py │ │ ├── hashed_map.py │ │ ├── binary_search_tree.py │ │ └── priority_queue.py │ ├── graphs │ │ ├── __init__.py │ │ └── graph.py │ └── strings │ │ ├── __init__.py │ │ └── search_tries.py └── __init__.py ├── scripts ├── unittest-py3.ps1 ├── build.ps1 ├── unittest.ps1 ├── upload.ps1 └── virtualenv.md ├── docs ├── algorithms.rst ├── algorithms-strings.rst ├── algorithms-commons.rst ├── data_structures.rst ├── algorithms-graphs.rst ├── commons-shuffle.rst ├── graphs-topological-sort.rst ├── bag.rst ├── graphs-cyclic.rst ├── commons-search.rst ├── strings-longest-repeated-substring.rst ├── graphs-max-flow.rst ├── stack.rst ├── commons-union-find.rst ├── queue.rst ├── Makefile ├── graphs-mst.rst ├── make.bat ├── strings-sort.rst ├── graphs-search.rst ├── priority-queue.rst ├── graphs-connectivity.rst ├── search-tries.rst ├── strings-substring-search.rst ├── graph.rst ├── index.rst ├── commons-sort.rst ├── graphs-shortest-path.rst ├── symbol-table.rst ├── graphs-create.rst └── conf.py ├── .gitignore ├── .travis.yml ├── LICENSE ├── setup.py └── README.rst /tests/algorithms/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /pyalgs/algorithms/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/data_structures/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /pyalgs/algorithms/commons/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /pyalgs/algorithms/graphs/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /pyalgs/algorithms/strings/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /pyalgs/data_structures/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/algorithms/commons/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/algorithms/graphs/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/algorithms/strings/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /pyalgs/data_structures/commons/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /pyalgs/data_structures/graphs/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /pyalgs/data_structures/strings/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/data_structures/commons/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/data_structures/graphs/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/data_structures/strings/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /scripts/unittest-py3.ps1: -------------------------------------------------------------------------------- 1 | python3 -m unittest discover -s .. -p "*_unit_test.py" -------------------------------------------------------------------------------- /scripts/build.ps1: -------------------------------------------------------------------------------- 1 | $current_directory = $pwd 2 | 3 | cd $PSScriptRoot/.. 4 | python setup.py sdist 5 | 6 | cd $current_directory 7 | 8 | -------------------------------------------------------------------------------- /scripts/unittest.ps1: -------------------------------------------------------------------------------- 1 | python -m unittest discover -s .. -p "*_unit_test.py" 2 | coverage run -m unittest discover -s .. -p "*_unit_test.py" 3 | coverage report -m .. -------------------------------------------------------------------------------- /docs/algorithms.rst: -------------------------------------------------------------------------------- 1 | Algorithms 2 | ========== 3 | 4 | .. toctree:: 5 | :maxdepth: 4 6 | 7 | algorithms-commons 8 | algorithms-graphs 9 | algorithms-strings -------------------------------------------------------------------------------- /docs/algorithms-strings.rst: -------------------------------------------------------------------------------- 1 | Strings 2 | ======= 3 | 4 | 5 | .. toctree:: 6 | :maxdepth: 3 7 | 8 | strings-sort 9 | strings-substring-search 10 | strings-longest-repeated-substring -------------------------------------------------------------------------------- /docs/algorithms-commons.rst: -------------------------------------------------------------------------------- 1 | Common Algorithms 2 | ================= 3 | 4 | .. toctree:: 5 | :maxdepth: 3 6 | 7 | commons-union-find 8 | commons-sort 9 | commons-shuffle 10 | commons-search -------------------------------------------------------------------------------- /scripts/upload.ps1: -------------------------------------------------------------------------------- 1 | $current_directory = $pwd 2 | 3 | cd $PSScriptRoot/.. 4 | 5 | # run "python setup.py register" if the project has not been register with pypi.python.org 6 | 7 | python setup.py sdist upload 8 | 9 | cd $current_directory 10 | 11 | -------------------------------------------------------------------------------- /docs/data_structures.rst: -------------------------------------------------------------------------------- 1 | Data Structures 2 | =============== 3 | 4 | .. toctree:: 5 | :maxdepth: 3 6 | 7 | stack 8 | queue 9 | bag 10 | priority-queue 11 | symbol-table 12 | graph 13 | search-tries 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /tests/algorithms/strings/util.py: -------------------------------------------------------------------------------- 1 | def some_text(): 2 | return "bed bug dad yes zoo now for tip ilk dim tag jot sob nob sky hut men egg few jay owl joy rap gig wee was wad fee tap tar dug jam all bad yet" 3 | 4 | 5 | def words3(): 6 | words = some_text().split(' ') 7 | return words 8 | -------------------------------------------------------------------------------- /docs/algorithms-graphs.rst: -------------------------------------------------------------------------------- 1 | Graphs 2 | ====== 3 | 4 | 5 | .. toctree:: 6 | :maxdepth: 3 7 | 8 | graphs-create 9 | graphs-search 10 | graphs-connectivity 11 | graphs-topological-sort 12 | graphs-cyclic 13 | graphs-mst 14 | graphs-shortest-path 15 | graphs-max-flow -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | *.pyc 3 | *.pyo 4 | env 5 | env* 6 | dist 7 | build 8 | *.egg 9 | *.egg-info 10 | _mailinglist 11 | .tox 12 | .cache/ 13 | .idea/ 14 | docs/_build 15 | 16 | #Coverage reports 17 | htmlcov 18 | .coverage 19 | .coverage.* 20 | *,cover 21 | 22 | *.un~ 23 | .gitignore~ 24 | -------------------------------------------------------------------------------- /docs/commons-shuffle.rst: -------------------------------------------------------------------------------- 1 | Shuffling 2 | ========= 3 | 4 | Knuth Shuffle 5 | ------------- 6 | 7 | .. code-block:: python 8 | 9 | from pyalgs.algorithms.commons.shuffling import KnuthShuffle 10 | 11 | a = [1, 2, 13, 22, 123] 12 | KnuthShuffle.shuffle(a) 13 | print(a) 14 | 15 | 16 | -------------------------------------------------------------------------------- /docs/graphs-topological-sort.rst: -------------------------------------------------------------------------------- 1 | Topological Sort 2 | ================ 3 | 4 | .. code-block:: python 5 | 6 | from pyalgs.algorithms.graphs.topological_sort import DepthFirstOrder 7 | G = create_graph() 8 | topological_sort = DepthFirstOrder(G) 9 | print(' => '.join([str(i) for i in topological_sort.postOrder()])) -------------------------------------------------------------------------------- /pyalgs/algorithms/commons/shuffling.py: -------------------------------------------------------------------------------- 1 | from random import randint 2 | 3 | from pyalgs.algorithms.commons.util import exchange 4 | 5 | 6 | class KnuthShuffle(object): 7 | @staticmethod 8 | def shuffle(a): 9 | for i in range(1, len(a)): 10 | r = randint(0, i) 11 | exchange(a, r, i) 12 | -------------------------------------------------------------------------------- /docs/bag.rst: -------------------------------------------------------------------------------- 1 | Bag 2 | ==== 3 | 4 | .. code-block:: python 5 | 6 | from pyalgs.data_structures.commons.bag import Bag 7 | 8 | bag = Bag.create() 9 | 10 | bag.add(10) 11 | bag.add(20) 12 | bag.add(30) 13 | 14 | print [i for i in bag.iterate()] 15 | 16 | print bag.size() 17 | print bag.is_empty() 18 | 19 | -------------------------------------------------------------------------------- /pyalgs/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | pyalgs 4 | ~~~~~ 5 | pyalgs provides the python implementation of the Robert Sedgwick's Coursera course on Algorithms (Part I and Part II). 6 | :copyright: (c) 2017 by Xianshun Chen. 7 | :license: BSD, see LICENSE for more details. 8 | """ 9 | 10 | __version__ = '0.0.14' -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | pyalgs 4 | ~~~~~ 5 | pyalgs provides the python implementation of the Robert Sedgwick's Coursera course on Algorithms (Part I and Part II). 6 | :copyright: (c) 2017 by Xianshun Chen. 7 | :license: BSD, see LICENSE for more details. 8 | """ 9 | 10 | __version__ = '0.01-dev' -------------------------------------------------------------------------------- /docs/graphs-cyclic.rst: -------------------------------------------------------------------------------- 1 | Cyclic Graph Detection 2 | ====================== 3 | 4 | Directed Cycle Detection 5 | ------------------------ 6 | 7 | .. code-block:: python 8 | 9 | from pyalgs.algorithms.graphs.directed_cycle import DirectedCycle 10 | dag = create_dag() 11 | dc = DirectedCycle(dag) 12 | assertFalse(dc.hasCycle()) 13 | 14 | -------------------------------------------------------------------------------- /tests/algorithms/commons/shuffling_unit_test.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | 3 | from pyalgs.algorithms.commons.shuffling import KnuthShuffle 4 | 5 | 6 | class KnuthShuffleUnitTest(unittest.TestCase): 7 | def test_shuffle(self): 8 | a = [1, 2, 13, 22, 123] 9 | KnuthShuffle.shuffle(a) 10 | print(a) 11 | 12 | if __name__ == '__main__': 13 | unittest.main() -------------------------------------------------------------------------------- /docs/commons-search.rst: -------------------------------------------------------------------------------- 1 | Searching 2 | ========= 3 | 4 | Binary Selection 5 | ---------------- 6 | 7 | .. code-block:: python 8 | 9 | from pyalgs.algorithms.commons.selecting import BinarySelection 10 | from pyalgs.algorithms.commons.util import is_sorted 11 | 12 | 13 | a = [1, 2, 13, 22, 123] 14 | assert is_sorted(a) 15 | print BinarySelection.index_of(a, 13) -------------------------------------------------------------------------------- /docs/strings-longest-repeated-substring.rst: -------------------------------------------------------------------------------- 1 | Longest Repeated Substring 2 | ========================== 3 | 4 | .. code-block:: python 5 | 6 | from pyalgs.algorithms.strings.longest_repeated_substring import LongestRepeatedSubstringSearch 7 | start, len = LongestRepeatedSubstringSearch.lrs('Hello World', 'World Record') 8 | print('Hello World'[start:(start+len+1)]) 9 | 10 | 11 | -------------------------------------------------------------------------------- /docs/graphs-max-flow.rst: -------------------------------------------------------------------------------- 1 | Max-Flow-Min-Cut 2 | ================ 3 | 4 | MaxFlow MinCut (Ford-Fulkerson) 5 | ------------------------------- 6 | 7 | .. code-block:: python 8 | 9 | from pyalgs.algorithms.graphs.max_flow import FordFulkersonMaxFlow 10 | network = create_flow_network() 11 | ff = FordFulkersonMaxFlow(network, 0, 7) 12 | print('max-flow: '+str(ff.max_flow_value())) 13 | 14 | -------------------------------------------------------------------------------- /docs/stack.rst: -------------------------------------------------------------------------------- 1 | Stack 2 | ===== 3 | 4 | .. code-block:: python 5 | 6 | from pyalgs.data_structures.commons.stack import Stack 7 | 8 | stack = Stack.create() 9 | stack.push(10) 10 | stack.push(1) 11 | 12 | print [i for i in stack.iterate()] 13 | 14 | print stack.is_empty() 15 | print stack.size() 16 | 17 | popped_item = stack.pop() 18 | print popped_item 19 | 20 | 21 | -------------------------------------------------------------------------------- /docs/commons-union-find.rst: -------------------------------------------------------------------------------- 1 | Union Find 2 | ========== 3 | 4 | .. code-block:: python 5 | 6 | from pyalgs.algorithms.commons.union_find import UnionFind 7 | 8 | uf = UnionFind.create(10) 9 | 10 | uf.union(1, 3) 11 | uf.union(2, 4) 12 | uf.union(1, 5) 13 | 14 | print(uf.connected(1, 3)) 15 | print(uf.connected(3, 5)) 16 | print(uf.connected(1, 2)) 17 | print(uf.connected(1, 4)) 18 | 19 | -------------------------------------------------------------------------------- /docs/queue.rst: -------------------------------------------------------------------------------- 1 | Queue 2 | ===== 3 | 4 | .. code-block:: python 5 | 6 | from pyalgs.data_structures.commons.queue import Queue 7 | 8 | queue = Queue.create() 9 | queue.enqueue(10) 10 | queue.enqueue(20) 11 | queue.enqueue(30) 12 | 13 | print [i for i in queue.iterate()] 14 | 15 | print queue.size() 16 | print queue.is_empty() 17 | 18 | deleted_item = queue.dequeue()) 19 | print deleted_item 20 | 21 | 22 | -------------------------------------------------------------------------------- /tests/algorithms/commons/selecting_unit_test.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | 3 | from pyalgs.algorithms.commons.selecting import BinarySelection 4 | from pyalgs.algorithms.commons.util import is_sorted 5 | 6 | 7 | class BinarySelectionUnitTest(unittest.TestCase): 8 | def test_select(self): 9 | a = [1, 2, 13, 22, 123] 10 | assert is_sorted(a) 11 | print(BinarySelection.index_of(a, 13)) 12 | 13 | if __name__ == '__main__': 14 | unittest.main() 15 | -------------------------------------------------------------------------------- /tests/algorithms/graphs/directed_cycle_unit_test.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | 3 | from pyalgs.algorithms.graphs.directed_cycle import DirectedCycle 4 | from tests.algorithms.graphs.util import create_dag 5 | 6 | 7 | class DirectedCycleUnitTest(unittest.TestCase): 8 | 9 | def test_cycle(self): 10 | 11 | dag = create_dag() 12 | dc = DirectedCycle(dag) 13 | 14 | self.assertFalse(dc.hasCycle()) 15 | 16 | 17 | if __name__ == '__main__': 18 | unittest.main() -------------------------------------------------------------------------------- /tests/algorithms/commons/util_unit_test.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | 3 | from pyalgs.algorithms.commons.util import less, exchange 4 | 5 | 6 | class UtilTest(unittest.TestCase): 7 | def test_less(self): 8 | self.assertTrue(less(4, 5)) 9 | self.assertFalse(less(4, 4)) 10 | 11 | def test_exchange(self): 12 | a = [2, 4, 5] 13 | exchange(a, 0, 1) 14 | self.assertEqual(4, a[0]) 15 | self.assertEqual(2, a[1]) 16 | 17 | if __name__ == '__main__': 18 | unittest.main() 19 | -------------------------------------------------------------------------------- /tests/algorithms/graphs/topological_sort_unit_test.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | 3 | from pyalgs.algorithms.graphs.topological_sort import DepthFirstOrder 4 | from tests.algorithms.graphs.util import create_dag 5 | 6 | 7 | class DepthFirstOrderUnitTest(unittest.TestCase): 8 | 9 | def test_topological_sort(self): 10 | G = create_dag() 11 | 12 | topological_sort = DepthFirstOrder(G) 13 | 14 | print(' => '.join([str(i) for i in topological_sort.postOrder()])) 15 | 16 | 17 | if __name__ == '__main__': 18 | unittest.main() -------------------------------------------------------------------------------- /tests/data_structures/commons/bag_unit_test.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | 3 | from pyalgs.data_structures.commons.bag import Bag 4 | 5 | 6 | class BagUnitTest(unittest.TestCase): 7 | def test_bag(self): 8 | bag = Bag.create() 9 | 10 | bag.add(100) 11 | bag.add(200) 12 | bag.add(300) 13 | bag.add(400) 14 | 15 | print([i for i in bag.iterate()]) 16 | 17 | self.assertEqual(4, bag.size()) 18 | self.assertFalse(bag.is_empty()) 19 | 20 | if __name__ == '__main__': 21 | unittest.main() 22 | -------------------------------------------------------------------------------- /pyalgs/data_structures/commons/bag.py: -------------------------------------------------------------------------------- 1 | from pyalgs.data_structures.commons.stack import Stack 2 | 3 | 4 | class Bag(object): 5 | def __init__(self): 6 | self.stack = Stack.create() 7 | 8 | def add(self, item): 9 | self.stack.push(item) 10 | 11 | def size(self): 12 | return self.stack.size() 13 | 14 | def is_empty(self): 15 | return self.stack.is_empty() 16 | 17 | def iterate(self): 18 | return self.stack.iterate() 19 | 20 | @staticmethod 21 | def create(): 22 | return Bag() 23 | -------------------------------------------------------------------------------- /tests/algorithms/strings/longest_repeated_substring_unit_test.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | 3 | from pyalgs.algorithms.strings.longest_repeated_substring import LongestRepeatedSubstringSearch 4 | 5 | 6 | class LongestRepeatedSubstringSearchUnitTest(unittest.TestCase): 7 | 8 | def test_lrs(self): 9 | start, len = LongestRepeatedSubstringSearch.lrs('Hello World', 'World Record') 10 | print('Hello World'[start:(start+len+1)]) 11 | self.assertEqual('World', 'Hello World'[start:(start+len+1)]) 12 | 13 | 14 | if __name__ == '__main__': 15 | unittest.main() -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | # Config file for automatic testing at travis-ci.org 2 | 3 | language: python 4 | 5 | python: 6 | - "2.7" 7 | - "3.3" 8 | - "3.4" 9 | - "3.5" 10 | - "pypy" 11 | - "pypy3" 12 | 13 | # command to install dependencies, e.g. pip install -r requirements.txt --use-mirrors 14 | install: 15 | # - "pip install -r requirements.txt --use-mirrors" 16 | - "pip install coverage" 17 | - "pip install coveralls" 18 | 19 | # command to run tests, e.g. python setup.py test 20 | script: 21 | "coverage run -m unittest discover -s . -p '*_unit_test.py'" 22 | 23 | after_success: 24 | coveralls -------------------------------------------------------------------------------- /pyalgs/algorithms/commons/util.py: -------------------------------------------------------------------------------- 1 | def less(a, b): 2 | return cmp(a, b) < 0 3 | 4 | 5 | def greater(a, b): 6 | return cmp(a, b) > 0 7 | 8 | 9 | def cmp(a, b): 10 | if a is None: 11 | return -1 12 | if b is None: 13 | return -1 14 | return (a > b) - (a < b) 15 | 16 | 17 | def exchange(a, i, j): 18 | tmp = a[j] 19 | a[j] = a[i] 20 | a[i] = tmp 21 | 22 | 23 | def is_sorted(a): 24 | if len(a) <= 1: 25 | return True 26 | for i in range(1, len(a)): 27 | if less(a[i], a[i - 1]): 28 | return False 29 | return True 30 | -------------------------------------------------------------------------------- /tests/algorithms/graphs/max_flow_unit_test.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | 3 | from pyalgs.algorithms.graphs.max_flow import FordFulkersonMaxFlow 4 | from tests.algorithms.graphs.util import create_flow_network 5 | 6 | 7 | class FordFulkersonMaxFlowUnitTest(unittest.TestCase): 8 | def test_max_flow(self): 9 | network = create_flow_network() 10 | ff = FordFulkersonMaxFlow(network, 0, 7) 11 | print('max-flow: '+str(ff.max_flow_value())) 12 | self.assertEqual(28, ff.max_flow_value()) 13 | for e in ff.min_cut(): 14 | print('min-cut: ' + str(e)) 15 | 16 | if __name__ == '__main__': 17 | unittest.main() 18 | -------------------------------------------------------------------------------- /scripts/virtualenv.md: -------------------------------------------------------------------------------- 1 | # Prepare virtualenv 2 | 3 | ## Install virtualenv 4 | 5 |
6 | $ sudo pip install virtualenv 7 |8 | 9 | ## Create the virtualenv directory if not exist 10 | 11 |
12 | $ mkdir $HOME/.virtualenvs 13 |14 | 15 | ## create the virtualenv for the pyalgs 16 | 17 |
18 | cd $HOME/.virtualenvs 19 | virtualenv pyalgs 20 |21 | 22 | # Activate and deactivate virtualenv 23 | 24 | To activate the virtualenv for pyalgs: 25 | 26 |
27 | cd $HOME/.virtualenvs 28 | . pyalgs/bin/activate 29 |30 | 31 | To deactivate 32 | 33 |
34 | . pyalgs/bin/deactivate 35 |36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Minimal makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line. 5 | SPHINXOPTS = 6 | SPHINXBUILD = sphinx-build 7 | SPHINXPROJ = pyalgs 8 | SOURCEDIR = . 9 | BUILDDIR = _build 10 | 11 | # Put it first so that "make" without argument is like "make help". 12 | help: 13 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 14 | 15 | .PHONY: help Makefile 16 | 17 | # Catch-all target: route all unknown targets to Sphinx using the new 18 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). 19 | %: Makefile 20 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) -------------------------------------------------------------------------------- /pyalgs/algorithms/commons/selecting.py: -------------------------------------------------------------------------------- 1 | from pyalgs.algorithms.commons.util import is_sorted, less 2 | 3 | 4 | class BinarySelection(object): 5 | @staticmethod 6 | def index_of(a, x, lo=None, hi=None): 7 | if not is_sorted(a): 8 | raise ValueError('array must be sorted before running selection') 9 | 10 | if lo is None: 11 | lo = 0 12 | if hi is None: 13 | hi = len(a) - 1 14 | 15 | while lo <= hi: 16 | mid = lo + (hi - lo) // 2 17 | if less(x, a[mid]): 18 | hi = mid - 1 19 | elif less(a[mid], x): 20 | lo = mid + 1 21 | else: 22 | return mid 23 | 24 | return -1 25 | -------------------------------------------------------------------------------- /tests/data_structures/graphs/graph_unit_test.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | 3 | from pyalgs.data_structures.graphs.graph import Graph, Digraph 4 | 5 | 6 | class GraphUnitTest(unittest.TestCase): 7 | def test_graph(self): 8 | G = Graph(100) 9 | 10 | G.add_edge(1, 2) 11 | G.add_edge(1, 3) 12 | 13 | print([i for i in G.adj(1)]) 14 | 15 | self.assertEqual(100, G.vertex_count()) 16 | 17 | 18 | class DigraphUnitTest(unittest.TestCase): 19 | def test_digraph(self): 20 | G = Digraph(100) 21 | 22 | G.add_edge(1, 2) 23 | G.add_edge(1, 3) 24 | 25 | print([i for i in G.adj(1)]) 26 | 27 | self.assertEqual(100, G.vertex_count()) 28 | 29 | if __name__ == '__main__': 30 | unittest.main() -------------------------------------------------------------------------------- /docs/graphs-mst.rst: -------------------------------------------------------------------------------- 1 | Minimum Spanning Tree 2 | ===================== 3 | 4 | Minimum Spanning Tree (Kruskal) 5 | ------------------------------- 6 | 7 | .. code-block:: python 8 | 9 | from pyalgs.algorithms.graphs.minimum_spanning_trees import KruskalMST 10 | g = create_edge_weighted_graph() 11 | mst = KruskalMST(g) 12 | 13 | tree = mst.spanning_tree() 14 | 15 | for e in tree: 16 | print(e) 17 | 18 | Minimum Spanning Tree (Lazy Prim) 19 | --------------------------------- 20 | 21 | .. code-block:: python 22 | 23 | from pyalgs.algorithms.graphs.minimum_spanning_trees import LazyPrimMST 24 | g = create_edge_weighted_graph() 25 | mst = LazyPrimMST(g) 26 | 27 | tree = mst.spanning_tree() 28 | 29 | for e in tree: 30 | print(e) -------------------------------------------------------------------------------- /pyalgs/algorithms/strings/longest_repeated_substring.py: -------------------------------------------------------------------------------- 1 | 2 | class LongestRepeatedSubstringSearch(object): 3 | @staticmethod 4 | def lrs(text, pattern): 5 | n = len(text) 6 | a = [''] * n 7 | for i in range(0, n): 8 | a[i] = text[i:] 9 | 10 | sorted(a) 11 | 12 | max_len = 0 13 | j = -1 14 | for i in range(0, n): 15 | l = LongestRepeatedSubstringSearch.lcp(a[i], pattern) 16 | if l > max_len: 17 | j = i 18 | max_len = l 19 | 20 | return (j, max_len) 21 | 22 | @staticmethod 23 | def lcp(text, pattern): 24 | i = 0 25 | for i in range(min(len(text), len(pattern))): 26 | if text[i] != pattern[i]: 27 | return i 28 | return i 29 | -------------------------------------------------------------------------------- /docs/make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | pushd %~dp0 4 | 5 | REM Command file for Sphinx documentation 6 | 7 | if "%SPHINXBUILD%" == "" ( 8 | set SPHINXBUILD=sphinx-build 9 | ) 10 | set SOURCEDIR=. 11 | set BUILDDIR=_build 12 | set SPHINXPROJ=pyalgs 13 | 14 | if "%1" == "" goto help 15 | 16 | %SPHINXBUILD% >NUL 2>NUL 17 | if errorlevel 9009 ( 18 | echo. 19 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx 20 | echo.installed, then set the SPHINXBUILD environment variable to point 21 | echo.to the full path of the 'sphinx-build' executable. Alternatively you 22 | echo.may add the Sphinx directory to PATH. 23 | echo. 24 | echo.If you don't have Sphinx installed, grab it from 25 | echo.http://sphinx-doc.org/ 26 | exit /b 1 27 | ) 28 | 29 | %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% 30 | goto end 31 | 32 | :help 33 | %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% 34 | 35 | :end 36 | popd 37 | -------------------------------------------------------------------------------- /docs/strings-sort.rst: -------------------------------------------------------------------------------- 1 | String Sorting 2 | ============== 3 | 4 | LSD Radix Sort 5 | -------------- 6 | 7 | .. code-block:: python 8 | 9 | from pyalgs.algorithms.strings.sorting import LSD 10 | LSD.sort(['good', 'cool', 'done', 'come']) 11 | 12 | MSD Radix Sort 13 | -------------- 14 | 15 | .. code-block:: python 16 | 17 | from pyalgs.algorithms.strings.sorting import MSD 18 | words = 'more details are provided in the docs for implementation, complexities and further info'.split(' ') 19 | print(words) 20 | MSD.sort(words) 21 | print(words) 22 | 23 | 24 | Sort (3-Ways String Quick Sort) 25 | ------------------------------- 26 | 27 | .. code-block:: python 28 | 29 | from pyalgs.algorithms.strings.sorting import String3WayQuickSort 30 | words = 'more details are provided in the docs for implementation, complexities and further info'.split(' ') 31 | print(words) 32 | String3WayQuickSort.sort(words) 33 | print(words) 34 | 35 | 36 | -------------------------------------------------------------------------------- /pyalgs/algorithms/graphs/topological_sort.py: -------------------------------------------------------------------------------- 1 | from pyalgs.data_structures.commons.stack import Stack 2 | from pyalgs.data_structures.graphs.graph import EdgeWeightedGraph, DirectedEdgeWeightedGraph 3 | 4 | 5 | class DepthFirstOrder(object): 6 | 7 | marked = None 8 | reversePostOrder = None 9 | 10 | def __init__(self, G): 11 | if isinstance(G, DirectedEdgeWeightedGraph): 12 | G = G.to_digraph() 13 | 14 | self.reversePostOrder = Stack.create() 15 | vertex_count = G.vertex_count() 16 | self.marked = [False] * vertex_count 17 | 18 | for v in range(vertex_count): 19 | if not self.marked[v]: 20 | self.dfs(G, v) 21 | 22 | def dfs(self, G, v): 23 | self.marked[v] = True 24 | for w in G.adj(v): 25 | if not self.marked[w]: 26 | self.dfs(G, w) 27 | self.reversePostOrder.push(v) 28 | 29 | def postOrder(self): 30 | return self.reversePostOrder.iterate() -------------------------------------------------------------------------------- /docs/graphs-search.rst: -------------------------------------------------------------------------------- 1 | Graph Search 2 | ============ 3 | 4 | Depth First Search 5 | ------------------ 6 | 7 | .. code-block:: python 8 | 9 | from pyalgs.algorithms.graphs.search import DepthFirstSearch 10 | g = create_graph() 11 | s = 0 12 | dfs = DepthFirstSearch(g, s) 13 | 14 | for v in range(1, g.vertex_count()): 15 | if dfs.hasPathTo(v): 16 | print(str(s) + ' is connected to ' + str(v)) 17 | print('path is ' + ' => '.join([str(i) for i in dfs.pathTo(v)])) 18 | 19 | Breadth First Search 20 | -------------------- 21 | 22 | .. code-block:: python 23 | 24 | from pyalgs.algorithms.graphs.search import BreadthFirstSearch 25 | g = create_graph() 26 | s = 0 27 | dfs = BreadthFirstSearch(g, s) 28 | 29 | for v in range(1, g.vertex_count()): 30 | if dfs.hasPathTo(v): 31 | print(str(s) + ' is connected to ' + str(v)) 32 | print('path is ' + ' => '.join([str(i) for i in dfs.pathTo(v)])) 33 | 34 | -------------------------------------------------------------------------------- /tests/algorithms/strings/sorting_unit_test.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | 3 | from pyalgs.algorithms.strings.sorting import LSD, MSD, String3WayQuickSort 4 | from tests.algorithms.strings.util import words3 5 | 6 | 7 | class LSDUnitTest(unittest.TestCase): 8 | def test_lsd(self): 9 | words = words3() 10 | print(words) 11 | LSD.sort(words) 12 | print(words) 13 | 14 | class MSDUnitTest(unittest.TestCase): 15 | def test_msd(self): 16 | words = 'more details are provided in the docs for implementation, complexities and further info'.split(' ') 17 | print(words) 18 | MSD.sort(words) 19 | print(words) 20 | 21 | 22 | class String3WayQuickSortUnitTest(unittest.TestCase): 23 | def test_msd(self): 24 | words = 'more details are provided in the docs for implementation, complexities and further info'.split(' ') 25 | print(words) 26 | String3WayQuickSort.sort(words) 27 | print(words) 28 | 29 | 30 | if __name__ == '__main__': 31 | unittest.main() -------------------------------------------------------------------------------- /docs/priority-queue.rst: -------------------------------------------------------------------------------- 1 | Priority Queue 2 | ============== 3 | 4 | Minimum Priority Queue 5 | ---------------------- 6 | 7 | .. code-block:: python 8 | 9 | from pyalgs.data_structures.commons.priority_queue import MinPQ 10 | 11 | pq = MinPQ.create() 12 | pq.enqueue(10) 13 | pq.enqueue(5) 14 | pq.enqueue(12) 15 | pq.enqueue(14) 16 | pq.enqueue(2) 17 | 18 | print pq.is_empty() 19 | print pq.size() 20 | 21 | print [i for i in pq.iterate()] 22 | 23 | deleted = pq.del_min() 24 | print(deleted) 25 | 26 | 27 | Maximum Priority Queue 28 | ---------------------- 29 | 30 | 31 | .. code-block:: python 32 | 33 | from pyalgs.data_structures.commons.priority_queue import MaxPQ 34 | 35 | pq = MaxPQ.create() 36 | pq.enqueue(10) 37 | pq.enqueue(5) 38 | pq.enqueue(12) 39 | pq.enqueue(14) 40 | pq.enqueue(2) 41 | 42 | print pq.is_empty() 43 | print pq.size() 44 | 45 | print [i for i in pq.iterate()] 46 | 47 | deleted = pq.del_max() 48 | print deleted 49 | 50 | -------------------------------------------------------------------------------- /tests/algorithms/graphs/minimum_spanning_tree_unit_test.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | 3 | from pyalgs.algorithms.graphs.minimum_spanning_trees import KruskalMST, LazyPrimMST, EagerPrimMST 4 | from tests.algorithms.graphs.util import create_edge_weighted_graph 5 | 6 | 7 | class KruskalMSTUnitTest(unittest.TestCase): 8 | def test_mst(self): 9 | g = create_edge_weighted_graph() 10 | mst = KruskalMST(g) 11 | 12 | tree = mst.spanning_tree() 13 | 14 | for e in tree: 15 | print(e) 16 | 17 | 18 | class LazyPrimMSTUnitTest(unittest.TestCase): 19 | def test_mst(self): 20 | g = create_edge_weighted_graph() 21 | mst = LazyPrimMST(g) 22 | 23 | tree = mst.spanning_tree() 24 | 25 | for e in tree: 26 | print(e) 27 | 28 | 29 | class EagerPrimMSTUnitTest(unittest.TestCase): 30 | def test_mst(self): 31 | g = create_edge_weighted_graph() 32 | mst = EagerPrimMST(g) 33 | 34 | tree = mst.spanning_tree() 35 | 36 | for e in tree: 37 | print(e) 38 | 39 | 40 | if __name__ == '__main__': 41 | unittest.main() 42 | -------------------------------------------------------------------------------- /docs/graphs-connectivity.rst: -------------------------------------------------------------------------------- 1 | Graph Connectivity 2 | ================== 3 | 4 | Connected Components for undirected graph 5 | ----------------------------------------- 6 | 7 | .. code-block:: python 8 | 9 | from pyalgs.algorithms.graphs.connectivity import ConnectedComponents 10 | G = create_graph() 11 | 12 | cc = ConnectedComponents(G) 13 | print('connected component count: ' + str(cc.count())) 14 | 15 | 16 | for v in range(G.vertex_count()): 17 | print('id[' + str(v) + ']: ' + str(cc.id(v))) 18 | for v in range(G.vertex_count()): 19 | r = randint(0, G.vertex_count()-1) 20 | if cc.connected(v, r): 21 | print(str(v) + ' is connected to ' + str(r)) 22 | 23 | 24 | Strongly Connected Components for directed graph 25 | ------------------------------------------------ 26 | 27 | .. code-block:: python 28 | 29 | from pyalgs.algorithms.graphs.connectivity import StronglyConnectedComponents 30 | G = create_graph() 31 | 32 | cc = StronglyConnectedComponents(G) 33 | print('strongly connected component count: ' + str(cc.count())) 34 | 35 | 36 | for v in range(G.vertex_count()): 37 | print('id[' + str(v) + ']: ' + str(cc.id(v))) 38 | for v in range(G.vertex_count()): 39 | r = randint(0, G.vertex_count()-1) 40 | if cc.connected(v, r): 41 | print(str(v) + ' is connected to ' + str(r)) 42 | 43 | -------------------------------------------------------------------------------- /docs/search-tries.rst: -------------------------------------------------------------------------------- 1 | Search Tries 2 | ============ 3 | 4 | Symbol Table using R-ways Search Tries 5 | -------------------------------------- 6 | 7 | .. code-block:: python 8 | 9 | from pyalgs.data_structures.strings.search_tries import RWaySearchTries 10 | st = RWaySearchTries() 11 | 12 | st.put("one", 1) 13 | st.put("two", 2) 14 | st.put("three", 3) 15 | st.put("six", 6) 16 | st.put("ten", 10) 17 | 18 | for key in st.keys(): 19 | print(key) 20 | 21 | print st.get("one")) 22 | print st.contains_key("two") 23 | 24 | print st.size() 25 | print st.is_empty() 26 | 27 | st.delete("one") 28 | 29 | for key in st.keys_with_prefix('t'): 30 | print(key) 31 | 32 | 33 | Symbol Table using Ternary Search Tries 34 | --------------------------------------- 35 | 36 | 37 | .. code-block:: python 38 | 39 | from pyalgs.data_structures.strings.search_tries import TernarySearchTries 40 | st = TernarySearchTries() 41 | 42 | st.put("one", 1) 43 | st.put("two", 2) 44 | st.put("three", 3) 45 | st.put("six", 6) 46 | st.put("ten", 10) 47 | 48 | for key in st.keys(): 49 | print(key) 50 | 51 | print st.get("one")) 52 | print st.contains_key("two") 53 | 54 | print st.size() 55 | print st.is_empty() 56 | 57 | st.delete("one") 58 | 59 | for key in st.keys_with_prefix('t'): 60 | print(key) 61 | 62 | -------------------------------------------------------------------------------- /docs/strings-substring-search.rst: -------------------------------------------------------------------------------- 1 | Substring Search 2 | ================ 3 | 4 | 5 | Substring Search (Brute force) 6 | ------------------------------ 7 | 8 | .. code-block:: python 9 | 10 | from pyalgs.algorithms.strings.substring_search import BruteForceSubstringSearch 11 | ss = BruteForceSubstringSearch('find') 12 | print(ss.search_in('I can find it here')) 13 | print(ss.search_in('It is not here')) 14 | 15 | 16 | Substring Search (Rabin Karp) 17 | ----------------------------- 18 | 19 | .. code-block:: python 20 | 21 | from pyalgs.algorithms.strings.substring_search import RabinKarp 22 | ss = RabinKarp('find') 23 | print(ss.search_in('I can find it here')) 24 | print(ss.search_in('It is not here')) 25 | 26 | 27 | Substring Search (Boyer Moore) 28 | ------------------------------ 29 | 30 | .. code-block:: python 31 | 32 | from pyalgs.algorithms.strings.substring_search import BoyerMoore 33 | ss = BoyerMoore('find') 34 | print(ss.search_in('I can find it here')) 35 | print(ss.search_in('It is not here')) 36 | 37 | 38 | Substring Search (Knuth Morris Pratt) 39 | ------------------------------------- 40 | 41 | .. code-block:: python 42 | 43 | from pyalgs.algorithms.strings.substring_search import KnuthMorrisPratt 44 | ss = KnuthMorrisPratt('find') 45 | print(ss.search_in('I can find it here')) 46 | print(ss.search_in('It is not here')) 47 | 48 | 49 | -------------------------------------------------------------------------------- /docs/graph.rst: -------------------------------------------------------------------------------- 1 | Graph 2 | ===== 3 | 4 | Undirected Graph 5 | ---------------- 6 | 7 | .. code-block:: python 8 | 9 | from pyalgs.data_structures.graphs.graph import Graph 10 | G = Graph(100) 11 | 12 | G.add_edge(1, 2) 13 | G.add_edge(1, 3) 14 | 15 | print([i for i in G.adj(1)]) 16 | print([i for i in G.adj(2)]) 17 | print([i for i in G.adj(3)]) 18 | 19 | print(G.vertex_count()) 20 | 21 | Directed Graph 22 | -------------- 23 | 24 | .. code-block:: python 25 | 26 | from pyalgs.data_structures.graphs.graph import Digraph 27 | G = Digraph(100) 28 | 29 | G.add_edge(1, 2) 30 | G.add_edge(1, 3) 31 | 32 | print([i for i in G.adj(1)]) 33 | print([i for i in G.adj(2)]) 34 | print([i for i in G.adj(3)]) 35 | 36 | print(G.vertex_count()) 37 | 38 | 39 | Edge Weighted Graph 40 | ------------------- 41 | 42 | .. code-block:: python 43 | 44 | from pyalgs.data_structures.graphs.graph import EdgeWeightGraph 45 | def create_edge_weighted_graph(): 46 | g = EdgeWeightedGraph(8) 47 | g.add_edge(Edge(0, 7, 0.16)) 48 | g.add_edge(Edge(2, 3, 0.17)) 49 | g.add_edge(Edge(1, 7, 0.19)) 50 | g.add_edge(Edge(0, 2, 0.26)) 51 | g.add_edge(Edge(5, 7, 0.28)) 52 | 53 | print([edge for edge in G.adj(3)]) 54 | 55 | print(G.vertex_count()) 56 | print(', '.join([edge for edge in G.edges()])) 57 | return g 58 | -------------------------------------------------------------------------------- /tests/algorithms/commons/union_find_unit_test.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | 3 | from pyalgs.algorithms.commons.union_find import UnionFind, QuickFind, QuickUnion 4 | 5 | 6 | class UnionFindUnitTest(unittest.TestCase): 7 | def test_find(self): 8 | uf = UnionFind.create(10) 9 | 10 | uf.union(1, 3) 11 | uf.union(2, 4) 12 | uf.union(1, 5) 13 | 14 | self.assertTrue(uf.connected(1, 3)) 15 | self.assertTrue(uf.connected(3, 5)) 16 | self.assertFalse(uf.connected(1, 2)) 17 | self.assertFalse(uf.connected(1, 4)) 18 | 19 | 20 | class QuickFindUnitTest(unittest.TestCase): 21 | def test_find(self): 22 | uf = QuickFind(10) 23 | 24 | uf.union(1, 3) 25 | uf.union(2, 4) 26 | uf.union(1, 5) 27 | 28 | self.assertTrue(uf.connected(1, 3)) 29 | self.assertTrue(uf.connected(3, 5)) 30 | self.assertFalse(uf.connected(1, 2)) 31 | self.assertFalse(uf.connected(1, 4)) 32 | 33 | 34 | class QuickUnionUnitTest(unittest.TestCase): 35 | def test_find(self): 36 | uf = QuickUnion(10) 37 | 38 | uf.union(1, 3) 39 | uf.union(2, 4) 40 | uf.union(1, 5) 41 | 42 | self.assertTrue(uf.connected(1, 3)) 43 | self.assertTrue(uf.connected(3, 5)) 44 | self.assertFalse(uf.connected(1, 2)) 45 | self.assertFalse(uf.connected(1, 4)) 46 | 47 | 48 | if __name__ == '__main__': 49 | unittest.main() 50 | -------------------------------------------------------------------------------- /tests/algorithms/strings/substring_search_unit_test.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | 3 | from pyalgs.algorithms.strings.substring_search import BruteForceSubstringSearch, RabinKarp, BoyerMoore, \ 4 | KnuthMorrisPratt 5 | from tests.algorithms.strings.util import some_text 6 | 7 | 8 | class BruteForceSubstringSearchUnitTest(unittest.TestCase): 9 | 10 | def test_search(self): 11 | t = some_text() 12 | ss = BruteForceSubstringSearch('men') 13 | self.assertNotEqual(-1, ss.search_in(t)) 14 | self.assertEqual(-1, ss.search_in('Hello World')) 15 | 16 | 17 | class RabinKarpUnitTest(unittest.TestCase): 18 | 19 | def test_search(self): 20 | t = some_text() 21 | ss = RabinKarp('men') 22 | self.assertNotEqual(-1, ss.search_in(t)) 23 | self.assertEqual(-1, ss.search_in('Hello World')) 24 | 25 | 26 | class BoyerMooreUnitTest(unittest.TestCase): 27 | 28 | def test_search(self): 29 | t = some_text() 30 | ss = BoyerMoore('men') 31 | self.assertNotEqual(-1, ss.search_in(t)) 32 | self.assertEqual(-1, ss.search_in('Hello World')) 33 | 34 | 35 | class KnuthMorrisPrattUnitTest(unittest.TestCase): 36 | 37 | def test_search(self): 38 | t = some_text() 39 | ss = KnuthMorrisPratt('men') 40 | self.assertNotEqual(-1, ss.search_in(t)) 41 | self.assertEqual(-1, ss.search_in('Hello World')) 42 | 43 | if __name__ == '__main__': 44 | unittest.main() -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2017, Xianshun Chen 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 5 | 6 | * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 7 | 8 | * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 9 | 10 | * Neither the name of pyalgs nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 11 | 12 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /tests/data_structures/commons/priority_queue_unit_test.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | 3 | from pyalgs.data_structures.commons.priority_queue import MinPQ, MaxPQ 4 | 5 | 6 | class MinPQUnitTest(unittest.TestCase): 7 | def test_min(self): 8 | pq = MinPQ.create() 9 | pq.enqueue(10) 10 | pq.enqueue(5) 11 | pq.enqueue(12) 12 | pq.enqueue(14) 13 | pq.enqueue(2) 14 | 15 | self.assertFalse(pq.is_empty()) 16 | self.assertEqual(5, pq.size()) 17 | 18 | print([i for i in pq.iterate()]) 19 | 20 | self.assertEqual(2, pq.del_min()) 21 | self.assertEqual(5, pq.del_min()) 22 | self.assertEqual(10, pq.del_min()) 23 | self.assertEqual(12, pq.del_min()) 24 | self.assertEqual(14, pq.del_min()) 25 | 26 | self.assertTrue(pq.is_empty()) 27 | self.assertEqual(0, pq.size()) 28 | 29 | 30 | class MaxPQUnitTest(unittest.TestCase): 31 | def test_min(self): 32 | pq = MaxPQ.create() 33 | pq.enqueue(10) 34 | pq.enqueue(5) 35 | pq.enqueue(12) 36 | pq.enqueue(14) 37 | pq.enqueue(2) 38 | 39 | self.assertFalse(pq.is_empty()) 40 | self.assertEqual(5, pq.size()) 41 | 42 | print([i for i in pq.iterate()]) 43 | 44 | self.assertEqual(14, pq.del_max()) 45 | self.assertEqual(12, pq.del_max()) 46 | self.assertEqual(10, pq.del_max()) 47 | self.assertEqual(5, pq.del_max()) 48 | self.assertEqual(2, pq.del_max()) 49 | 50 | self.assertTrue(pq.is_empty()) 51 | self.assertEqual(0, pq.size()) 52 | 53 | 54 | if __name__ == '__main__': 55 | unittest.main() 56 | -------------------------------------------------------------------------------- /tests/algorithms/graphs/connectivity_unit_test.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | from random import randint 3 | 4 | from pyalgs.algorithms.graphs.connectivity import ConnectedComponents, StronglyConnectedComponents 5 | from tests.algorithms.graphs.util import create_graph_4_connected_components, \ 6 | create_digraph_4_strongly_connected_components 7 | 8 | 9 | class ConnectedComponentsUnitTest(unittest.TestCase): 10 | def test_cc(self): 11 | G = create_graph_4_connected_components() 12 | 13 | cc = ConnectedComponents(G) 14 | print('connected component count: ' + str(cc.count())) 15 | 16 | self.assertEqual(3, cc.count()) 17 | 18 | for v in range(G.vertex_count()): 19 | print('id[' + str(v) + ']: ' + str(cc.id(v))) 20 | for v in range(G.vertex_count()): 21 | r = randint(0, G.vertex_count() - 1) 22 | if cc.connected(v, r): 23 | print(str(v) + ' is connected to ' + str(r)) 24 | 25 | 26 | class StronglyConnectedComponentsUnitTest(unittest.TestCase): 27 | def test_cc(self): 28 | G = create_digraph_4_strongly_connected_components() 29 | 30 | cc = StronglyConnectedComponents(G) 31 | print('strongly connected component count: ' + str(cc.count())) 32 | 33 | self.assertEqual(5, cc.count()) 34 | 35 | for v in range(G.vertex_count()): 36 | print('id[' + str(v) + ']: ' + str(cc.id(v))) 37 | for v in range(G.vertex_count()): 38 | r = randint(0, G.vertex_count() - 1) 39 | if cc.connected(v, r): 40 | print(str(v) + ' is connected to ' + str(r)) 41 | 42 | 43 | if __name__ == '__main__': 44 | unittest.main() 45 | -------------------------------------------------------------------------------- /pyalgs/algorithms/graphs/directed_cycle.py: -------------------------------------------------------------------------------- 1 | from pyalgs.data_structures.commons.stack import Stack 2 | from pyalgs.data_structures.graphs.graph import DirectedEdgeWeightedGraph, Digraph 3 | 4 | 5 | class DirectedCycle(object): 6 | 7 | marked = None 8 | onStack = None 9 | cycle = None 10 | edgeTo = None 11 | 12 | def __init__(self, G): 13 | if isinstance(G, DirectedEdgeWeightedGraph): 14 | G = G.to_digraph() 15 | 16 | if not isinstance(G, Digraph): 17 | raise ValueError('Graph must be unweighted digraph') 18 | 19 | vertex_count = G.vertex_count() 20 | self.marked = [False] * vertex_count 21 | self.onStack = [False] * vertex_count 22 | self.edgeTo = [-1] * vertex_count 23 | 24 | for v in range(vertex_count): 25 | if not self.marked[v]: 26 | self.dfs(G, v) 27 | 28 | def dfs(self, G, v): 29 | self.marked[v] = True 30 | self.onStack[v] = True 31 | 32 | for w in G.adj(v): 33 | if not self.marked[w]: 34 | self.edgeTo[w] = v 35 | self.dfs(G, w) 36 | elif self.cycle is not None: 37 | break 38 | elif self.onStack[w]: 39 | self.cycle = Stack.create() 40 | x = v 41 | while x != w: 42 | self.cycle.push(x) 43 | x = self.edgeTo[x] 44 | self.cycle.push(w) 45 | self.cycle.push(v) 46 | break 47 | 48 | self.onStack[v] = False 49 | 50 | def hasCycle(self): 51 | return self.cycle is not None 52 | 53 | def get_cycle(self): 54 | return self.cycle.iterate() 55 | -------------------------------------------------------------------------------- /pyalgs/algorithms/commons/union_find.py: -------------------------------------------------------------------------------- 1 | from abc import ABCMeta, abstractmethod 2 | 3 | 4 | class UnionFind(object): 5 | __metaclass__ = ABCMeta 6 | 7 | @abstractmethod 8 | def union(self, v, w): 9 | pass 10 | 11 | @abstractmethod 12 | def connected(self, v, w): 13 | pass 14 | 15 | @staticmethod 16 | def create(size): 17 | return QuickUnion(size) 18 | 19 | 20 | class QuickFind(UnionFind): 21 | id = None 22 | 23 | def __init__(self, capacity): 24 | self.id = [i for i in range(capacity)] 25 | 26 | def connected(self, v, w): 27 | return self.id[v] == self.id[w] 28 | 29 | def union(self, v, w): 30 | p = self.id[v] 31 | q = self.id[w] 32 | 33 | if p != q: 34 | for i in range(len(self.id)): 35 | if self.id[i] == p: 36 | self.id[i] = q 37 | 38 | 39 | class QuickUnion(UnionFind): 40 | id = None 41 | sizes = None 42 | 43 | def __init__(self, capacity): 44 | self.id = [i for i in range(capacity)] 45 | self.sizes = [1] * capacity 46 | 47 | def root(self, v): 48 | while v != self.id[v]: 49 | self.id[v] = self.id[self.id[v]] # path compression 50 | v = self.id[v] 51 | return v 52 | 53 | def connected(self, v, w): 54 | return self.root(v) == self.root(w) 55 | 56 | def union(self, v, w): 57 | vroot = self.root(v) 58 | wroot = self.root(w) 59 | 60 | if self.sizes[vroot] > self.sizes[wroot]: 61 | self.id[wroot] = vroot 62 | self.sizes[vroot] += self.sizes[wroot] 63 | else: 64 | self.id[vroot] = wroot 65 | self.sizes[wroot] += self.sizes[vroot] 66 | -------------------------------------------------------------------------------- /docs/index.rst: -------------------------------------------------------------------------------- 1 | pyalgs 2 | ========== 3 | 4 | pyalgs is a library of python implementation for algorithms in the "Algorithms" book (http://algs4.cs.princeton.edu/home/) and Robert Sedgwick's Algorithms course using Python (Part I and Part II) 5 | 6 | The main purpose of this library is to provide a companion library for python developers who are learning the algorithms in the "Algorithms" book 7 | 8 | Installation: 9 | ------------- 10 | 11 | To install the package using pip: 12 | 13 | :: 14 | 15 | $ pip install pyalgs 16 | 17 | 18 | 19 | Usage 20 | ----- 21 | 22 | To use the algorithms or data structures in your python code: 23 | 24 | .. code-block:: python 25 | 26 | from pyalgs.data_structures.commons.stack import Stack 27 | 28 | stack = Stack.create() 29 | stack.push(10) 30 | stack.push(1) 31 | 32 | print [i for i in stack.iterate()] 33 | 34 | print stack.is_empty() 35 | print stack.size() 36 | 37 | popped_item = stack.pop() 38 | print popped_item 39 | 40 | Features 41 | -------- 42 | 43 | - Algorithms and data structures introduced in the "Algorithms" book. 44 | - Test coverage for each algorithm and data structure. 45 | 46 | 47 | Tests: 48 | ------ 49 | 50 | the unit tests of all algorithms and data structures can be run with the following command from the root folder: 51 | 52 | .. code-block:: bash 53 | 54 | $ python -m unittest discover -s . -p "*_unit_test.py" 55 | 56 | 57 | Contributing: 58 | ------------- 59 | 60 | Contributions are always welcome. Check out the contributing guidelines to get 61 | started. 62 | 63 | .. _`docs`: http://pyalgs.readthedocs.org/en/latest/ 64 | .. _`documentation`: http://pyalgs.readthedocs.org/en/latest/ 65 | 66 | Table of Contents: 67 | ------------------ 68 | 69 | .. toctree:: 70 | :maxdepth: 4 71 | 72 | data_structures 73 | algorithms -------------------------------------------------------------------------------- /docs/commons-sort.rst: -------------------------------------------------------------------------------- 1 | Sorting 2 | ======= 3 | 4 | The sorting algorithms sort an array in ascending order 5 | 6 | Selection Sort 7 | -------------- 8 | 9 | .. code-block:: python 10 | 11 | from pyalgs.algorithms.commons.sorting import SelectionSort 12 | 13 | a = [4, 2, 1] 14 | SelectionSort.sort(a) 15 | print(a) 16 | 17 | 18 | Insertion Sort 19 | -------------- 20 | 21 | .. code-block:: python 22 | 23 | from pyalgs.algorithms.commons.sorting import InsertionSort 24 | 25 | a = [4, 2, 1] 26 | InsertionSort.sort(a) 27 | print(a) 28 | 29 | 30 | Shell Sort 31 | ---------- 32 | 33 | .. code-block:: python 34 | 35 | from pyalgs.algorithms.commons.sorting import ShellSort 36 | 37 | a = [4, 2, 1, 23, 4, 5, 6, 7, 8, 9, 20, 11, 13, 34, 66] 38 | ShellSort.sort(a) 39 | print(a) 40 | 41 | 42 | Merge Sort 43 | ---------- 44 | 45 | .. code-block:: python 46 | 47 | from pyalgs.algorithms.commons.sorting import MergeSort 48 | 49 | a = [4, 2, 1, 23, 4, 5, 6, 7, 8, 9, 20, 11, 13, 34, 66] 50 | MergeSort.sort(a) 51 | print(a) 52 | 53 | 54 | Quick Sort 55 | ---------- 56 | 57 | .. code-block:: python 58 | 59 | from pyalgs.algorithms.commons.sorting import QuickSort 60 | 61 | a = [4, 2, 1, 23, 4, 5, 6, 7, 8, 9, 20, 11, 13, 34, 66] 62 | QuickSort.sort(a) 63 | print(a) 64 | 65 | 66 | 3-Ways Quick Sort 67 | ----------------- 68 | 69 | .. code-block:: python 70 | 71 | from pyalgs.algorithms.commons.sorting import ThreeWayQuickSort 72 | 73 | a = [4, 2, 1, 23, 4, 5, 6, 7, 8, 9, 20, 11, 13, 34, 66] 74 | ThreeWayQuickSort.sort(a) 75 | print(a) 76 | 77 | 78 | Heap Sort 79 | --------- 80 | 81 | .. code-block:: python 82 | 83 | from pyalgs.algorithms.commons.sorting import HeapSort 84 | 85 | a = [4, 2, 1, 23, 4, 5, 6, 7, 8, 9, 20, 11, 13, 34, 66] 86 | HeapSort.sort(a) 87 | print(a) 88 | 89 | 90 | 91 | -------------------------------------------------------------------------------- /tests/algorithms/graphs/search_unit_test.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | 3 | from pyalgs.algorithms.graphs.search import DepthFirstSearch, BreadthFirstSearch 4 | from tests.algorithms.graphs.util import create_graph, create_digraph 5 | 6 | 7 | class DepthFirstSearchUnitTest(unittest.TestCase): 8 | def test_dfs(self): 9 | g = create_graph() # or create_digraph 10 | s = 0 11 | dfs = DepthFirstSearch(g, s) 12 | 13 | for v in range(1, g.vertex_count()): 14 | if dfs.hasPathTo(v): 15 | print(str(s) + ' is connected to ' + str(v)) 16 | print('path is ' + ' => '.join([str(i) for i in dfs.pathTo(v)])) 17 | 18 | def test_dfs_digraph(self): 19 | g = create_digraph() 20 | s = 0 21 | dfs = DepthFirstSearch(g, s) 22 | 23 | for v in range(1, g.vertex_count()): 24 | if dfs.hasPathTo(v): 25 | print(str(s) + ' is connected to ' + str(v)) 26 | print('path is ' + ' => '.join([str(i) for i in dfs.pathTo(v)])) 27 | 28 | class BreadthFirstSearchUnitTest(unittest.TestCase): 29 | def test_dfs(self): 30 | g = create_graph() # or create_digraph 31 | s = 0 32 | dfs = BreadthFirstSearch(g, s) 33 | 34 | for v in range(1, g.vertex_count()): 35 | if dfs.hasPathTo(v): 36 | print(str(s) + ' is connected to ' + str(v)) 37 | print('path is ' + ' => '.join([str(i) for i in dfs.pathTo(v)])) 38 | 39 | def test_dfs_digraph(self): 40 | g = create_digraph() 41 | s = 0 42 | dfs = BreadthFirstSearch(g, s) 43 | 44 | for v in range(1, g.vertex_count()): 45 | if dfs.hasPathTo(v): 46 | print(str(s) + ' is connected to ' + str(v)) 47 | print('path is ' + ' => '.join([str(i) for i in dfs.pathTo(v)])) 48 | 49 | 50 | if __name__ == '__main__': 51 | unittest.main() 52 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | """ 2 | pyalgs 3 | ----- 4 | 5 | Package pyalgs implements algorithms in Robert Sedgwick's Coursera course in Python (Part I and Part II) 6 | """ 7 | 8 | import re 9 | import ast 10 | import io 11 | from setuptools import setup 12 | 13 | _version_re = re.compile(r'__version__\s+=\s+(.*)') 14 | 15 | with open('pyalgs/__init__.py', 'rb') as f: 16 | version = str(ast.literal_eval(_version_re.search( 17 | f.read().decode('utf-8')).group(1))) 18 | 19 | def long_description(): 20 | with io.open('README.rst', 'r', encoding='utf-8') as f: 21 | readme = f.read() 22 | return readme 23 | 24 | setup( 25 | name='pyalgs', 26 | version=version, 27 | url='https://github.com/chen0040/pyalgs', 28 | license='BSD', 29 | author='Xianshun Chen', 30 | author_email='xs0040@gmail.com', 31 | description='Python implementation of algorithms on string handling, data structure, graph processing, etc', 32 | long_description=long_description(), 33 | packages=['pyalgs'], 34 | include_package_data=True, 35 | zip_safe=False, 36 | platforms='any', 37 | install_requires=[], 38 | classifiers=[ 39 | 'License :: OSI Approved :: BSD License', 40 | 'Operating System :: OS Independent', 41 | 'Natural Language :: English', 42 | 'Programming Language :: Python', 43 | 'Programming Language :: Python :: 2', 44 | 'Programming Language :: Python :: 2.7', 45 | 'Programming Language :: Python :: 3', 46 | 'Programming Language :: Python :: 3.3', 47 | 'Programming Language :: Python :: 3.4', 48 | 'Programming Language :: Python :: 3.5', 49 | 'Topic :: Text Processing :: General', 50 | 'Topic :: Utilities', 51 | 'Intended Audience :: Developers', 52 | 'Intended Audience :: Education', 53 | 'Intended Audience :: Science/Research', 54 | 'Topic :: Software Development :: Libraries :: Python Modules' 55 | ] 56 | ) 57 | 58 | __author__ = 'Xianshun Chen' 59 | -------------------------------------------------------------------------------- /pyalgs/algorithms/graphs/max_flow.py: -------------------------------------------------------------------------------- 1 | from pyalgs.data_structures.commons.queue import Queue 2 | 3 | 4 | class FordFulkersonMaxFlow(object): 5 | edgeTo = None 6 | marked = None 7 | s = None 8 | t = None 9 | value = 0.0 10 | network = None 11 | 12 | def __init__(self, network, s, t): 13 | self.s = s 14 | self.t = t 15 | self.network = network 16 | self.value = 0.0 17 | while self.has_augmenting_path(): 18 | x = self.t 19 | bottle = float('inf') 20 | while x != self.s: 21 | bottle = min(bottle, self.edgeTo[x].residual_capacity_to(x)) 22 | x = self.edgeTo[x].other(x) 23 | 24 | x = self.t 25 | while x != self.s: 26 | self.edgeTo[x].add_residual_flow_to(x, bottle) 27 | x = self.edgeTo[x].other(x) 28 | 29 | self.value += bottle 30 | 31 | def has_augmenting_path(self): 32 | vertex_count = self.network.vertex_count() 33 | self.edgeTo = [None] * vertex_count 34 | self.marked = [False] * vertex_count 35 | 36 | queue = Queue.create() 37 | 38 | queue.enqueue(self.s) 39 | self.marked[self.s] = True 40 | 41 | while not queue.is_empty(): 42 | x = queue.dequeue() 43 | for e in self.network.adj(x): 44 | w = e.other(x) 45 | if e.residual_capacity_to(w) > 0: 46 | if not self.marked[w]: 47 | self.marked[w] = True 48 | self.edgeTo[w] = e 49 | queue.enqueue(w) 50 | 51 | return self.marked[self.t] 52 | 53 | def max_flow_value(self): 54 | return self.value 55 | 56 | def min_cut(self): 57 | queue = Queue.create() 58 | for edge in self.network.edges(): 59 | if edge.residual_capacity_to(edge.end()) == 0: 60 | queue.enqueue(edge) 61 | 62 | return queue.iterate() 63 | 64 | -------------------------------------------------------------------------------- /docs/graphs-shortest-path.rst: -------------------------------------------------------------------------------- 1 | Shortest Path 2 | ============= 3 | 4 | Dijkstra 5 | -------- 6 | 7 | .. code-block:: python 8 | 9 | from pyalgs.algorithms.graphs.shortest_path import DijkstraShortestPath 10 | g = create_edge_weighted_digraph() 11 | s = 0 12 | dijkstra = DijkstraShortestPath(g, s) 13 | for v in range(1, g.vertex_count()): 14 | if dijkstra.hasPathTo(v): 15 | print(str(s) + ' is connected to ' + str(v)) 16 | print('path is ' + ' .. '.join([str(i) for i in dijkstra.shortestPathTo(v)])) 17 | print('path length is ' + str(dijkstra.path_length_to(v))) 18 | 19 | 20 | Shortest Path (Topological Sort) 21 | -------------------------------- 22 | 23 | .. code-block:: python 24 | 25 | from pyalgs.algorithms.graphs.shortest_path import TopologicalSortShortestPath 26 | g = create_edge_weighted_digraph() 27 | assert not DirectedCycle(g).hasCycle() 28 | s = 0 29 | dijkstra = TopologicalSortShortestPath(g, s) 30 | for v in range(1, g.vertex_count()): 31 | if dijkstra.hasPathTo(v): 32 | print(str(s) + ' is connected to ' + str(v)) 33 | print('shortest path is ' + ' .. '.join([str(i) for i in dijkstra.shortestPathTo(v)])) 34 | print('path length is ' + str(dijkstra.path_length_to(v))) 35 | 36 | 37 | 38 | Shortest Path (Bellman-Ford for positive and negative edge graph) 39 | ----------------------------------------------------------------- 40 | 41 | .. code-block:: python 42 | 43 | from pyalgs.algorithms.graphs.shortest_path import BellmanFordShortestPath 44 | from pyalgs.algorithms.graphs.directed_cycle import DirectedCycle 45 | g = create_edge_weighted_digraph() 46 | s = 0 47 | dijkstra = BellmanFordShortestPath(g, s) 48 | for v in range(1, g.vertex_count()): 49 | if dijkstra.hasPathTo(v): 50 | print(str(s) + ' is connected to ' + str(v)) 51 | print('shortest path is ' + ' .. '.join([str(i) for i in dijkstra.shortestPathTo(v)])) 52 | print('path length is ' + str(dijkstra.path_length_to(v))) 53 | 54 | -------------------------------------------------------------------------------- /tests/data_structures/commons/stack_unit_test.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | 3 | from pyalgs.data_structures.commons.stack import LinkedListStack 4 | from pyalgs.data_structures.commons.stack import ArrayStack 5 | from pyalgs.data_structures.commons.stack import Stack 6 | 7 | 8 | class StackTest(unittest.TestCase): 9 | def test_push(self): 10 | stack = Stack.create() 11 | stack.push(10) 12 | stack.push(1) 13 | 14 | print([i for i in stack.iterate()]) 15 | 16 | self.assertFalse(stack.is_empty()) 17 | self.assertEqual(2, stack.size()) 18 | self.assertEqual(1, stack.pop()) 19 | self.assertFalse(stack.is_empty()) 20 | self.assertEqual(1, stack.size()) 21 | self.assertEqual(10, stack.pop()) 22 | self.assertTrue(stack.is_empty()) 23 | 24 | for i in range(100): 25 | stack.push(i) 26 | 27 | 28 | class LinkedListStackTest(unittest.TestCase): 29 | def test_push(self): 30 | stack = LinkedListStack() 31 | stack.push(10) 32 | stack.push(1) 33 | 34 | print([i for i in stack.iterate()]) 35 | 36 | self.assertFalse(stack.is_empty()) 37 | self.assertEqual(2, stack.size()) 38 | self.assertEqual(1, stack.pop()) 39 | self.assertFalse(stack.is_empty()) 40 | self.assertEqual(1, stack.size()) 41 | self.assertEqual(10, stack.pop()) 42 | self.assertTrue(stack.is_empty()) 43 | 44 | for i in range(100): 45 | stack.push(i) 46 | 47 | 48 | class ArrayStackTest(unittest.TestCase): 49 | def test_push(self): 50 | stack = ArrayStack() 51 | stack.push(10) 52 | 53 | print([i for i in stack.iterate()]) 54 | 55 | self.assertFalse(stack.is_empty()) 56 | self.assertEqual(1, stack.size()) 57 | self.assertEqual(10, stack.pop()) 58 | self.assertTrue(stack.is_empty()) 59 | 60 | for i in range(100): 61 | stack.push(i) 62 | 63 | for i in range(100): 64 | stack.pop() 65 | 66 | 67 | if __name__ == '__main__': 68 | unittest.main() 69 | -------------------------------------------------------------------------------- /tests/algorithms/graphs/shortest_path_unit_test.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | 3 | from pyalgs.algorithms.graphs.directed_cycle import DirectedCycle 4 | from pyalgs.algorithms.graphs.shortest_path import DijkstraShortestPath, TopologicalSortShortestPath, \ 5 | BellmanFordShortestPath 6 | from tests.algorithms.graphs.util import create_edge_weighted_digraph 7 | 8 | 9 | class DijkstraShortestPathUnitTest(unittest.TestCase): 10 | def test_shortest_path(self): 11 | g = create_edge_weighted_digraph() 12 | s = 0 13 | dijkstra = DijkstraShortestPath(g, s) 14 | for v in range(1, g.vertex_count()): 15 | if dijkstra.hasPathTo(v): 16 | print(str(s) + ' is connected to ' + str(v)) 17 | print('shortest path is ' + ' .. '.join([str(i) for i in dijkstra.shortestPathTo(v)])) 18 | print('path length is ' + str(dijkstra.path_length_to(v))) 19 | 20 | 21 | class TopologicalSortShortestPathUnitTest(unittest.TestCase): 22 | def test_shortest_path(self): 23 | g = create_edge_weighted_digraph() 24 | assert not DirectedCycle(g).hasCycle() 25 | s = 0 26 | dijkstra = TopologicalSortShortestPath(g, s) 27 | for v in range(1, g.vertex_count()): 28 | if dijkstra.hasPathTo(v): 29 | print(str(s) + ' is connected to ' + str(v)) 30 | print('shortest path is ' + ' .. '.join([str(i) for i in dijkstra.shortestPathTo(v)])) 31 | print('path length is ' + str(dijkstra.path_length_to(v))) 32 | 33 | 34 | class BellmanFordShortestPathUnitTest(unittest.TestCase): 35 | def test_shortest_path(self): 36 | g = create_edge_weighted_digraph() 37 | s = 0 38 | dijkstra = BellmanFordShortestPath(g, s) 39 | for v in range(1, g.vertex_count()): 40 | if dijkstra.hasPathTo(v): 41 | print(str(s) + ' is connected to ' + str(v)) 42 | print('shortest path is ' + ' .. '.join([str(i) for i in dijkstra.shortestPathTo(v)])) 43 | print('path length is ' + str(dijkstra.path_length_to(v))) 44 | 45 | 46 | if __name__ == '__main__': 47 | unittest.main() -------------------------------------------------------------------------------- /tests/algorithms/commons/sorting_unit_test.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | 3 | from pyalgs.algorithms.commons.sorting import SelectionSort, InsertionSort, ShellSort, MergeSort, QuickSort, \ 4 | ThreeWayQuickSort, HeapSort 5 | 6 | 7 | class SelectionSortTest(unittest.TestCase): 8 | def test_sort(self): 9 | a = [4, 2, 1] 10 | SelectionSort.sort(a) 11 | self.assertEqual(1, a[0]) 12 | self.assertEqual(2, a[1]) 13 | self.assertEqual(4, a[2]) 14 | 15 | 16 | class InsertionSortTest(unittest.TestCase): 17 | def test_sort(self): 18 | a = [4, 2, 1] 19 | InsertionSort.sort(a) 20 | self.assertEqual(1, a[0]) 21 | self.assertEqual(2, a[1]) 22 | self.assertEqual(4, a[2]) 23 | 24 | 25 | class ShellSortTest(unittest.TestCase): 26 | def test_sort(self): 27 | a = [4, 2, 1, 23, 4, 5, 6, 7, 8, 9, 20, 11, 13, 34, 66] 28 | ShellSort.sort(a) 29 | print(a) 30 | self.assertEqual(1, a[0]) 31 | self.assertEqual(2, a[1]) 32 | self.assertEqual(4, a[2]) 33 | 34 | 35 | class MergeSortTest(unittest.TestCase): 36 | def test_sort(self): 37 | a = [4, 2, 1, 23, 4, 5, 6, 7, 8, 9, 20, 11, 13, 34, 66] 38 | MergeSort.sort(a) 39 | print(a) 40 | self.assertEqual(1, a[0]) 41 | self.assertEqual(2, a[1]) 42 | self.assertEqual(4, a[2]) 43 | 44 | 45 | class QuickSortTest(unittest.TestCase): 46 | def test_sort(self): 47 | a = [4, 2, 1, 23, 4, 5, 6, 7, 8, 9, 20, 11, 13, 34, 66] 48 | QuickSort.sort(a) 49 | print(a) 50 | self.assertEqual(1, a[0]) 51 | self.assertEqual(2, a[1]) 52 | self.assertEqual(4, a[2]) 53 | 54 | 55 | class ThreeWayQuickSortTest(unittest.TestCase): 56 | def test_sort(self): 57 | a = [4, 2, 1, 23, 4, 5, 6, 7, 8, 9, 20, 11, 13, 34, 66] 58 | ThreeWayQuickSort.sort(a) 59 | print(a) 60 | self.assertEqual(1, a[0]) 61 | self.assertEqual(2, a[1]) 62 | self.assertEqual(4, a[2]) 63 | 64 | 65 | class HeapSortTest(unittest.TestCase): 66 | def test_sort(self): 67 | a = [4, 2, 1, 23, 4, 5, 6, 7, 8, 9, 20, 11, 13, 34, 66] 68 | HeapSort.sort(a) 69 | print(a) 70 | self.assertEqual(1, a[0]) 71 | self.assertEqual(2, a[1]) 72 | self.assertEqual(4, a[2]) 73 | 74 | 75 | if __name__ == '__main__': 76 | unittest.main() 77 | -------------------------------------------------------------------------------- /tests/data_structures/commons/queue_unit_test.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | 3 | from pyalgs.data_structures.commons.queue import LinkedListQueue, Queue, ArrayQueue 4 | 5 | 6 | class QueueUnitTest(unittest.TestCase): 7 | 8 | def test_Queue(self): 9 | queue = Queue.create() 10 | queue.enqueue(10) 11 | self.assertEqual(1, queue.size()) 12 | self.assertFalse(queue.is_empty()) 13 | queue.enqueue(20) 14 | self.assertEqual(2, queue.size()) 15 | queue.enqueue(30) 16 | 17 | print([i for i in queue.iterate()]) 18 | 19 | self.assertEqual(3, queue.size()) 20 | self.assertEqual(10, queue.dequeue()) 21 | self.assertEqual(2, queue.size()) 22 | self.assertEqual(20, queue.dequeue()) 23 | self.assertEqual(1, queue.size()) 24 | self.assertEqual(30, queue.dequeue()) 25 | self.assertTrue(queue.is_empty()) 26 | 27 | def test_LinkedListQueue(self): 28 | queue = LinkedListQueue() 29 | queue.enqueue(10) 30 | self.assertEqual(1, queue.size()) 31 | self.assertFalse(queue.is_empty()) 32 | queue.enqueue(20) 33 | self.assertEqual(2, queue.size()) 34 | queue.enqueue(30) 35 | 36 | print([i for i in queue.iterate()]) 37 | 38 | self.assertEqual(3, queue.size()) 39 | self.assertEqual(10, queue.dequeue()) 40 | self.assertEqual(2, queue.size()) 41 | self.assertEqual(20, queue.dequeue()) 42 | self.assertEqual(1, queue.size()) 43 | self.assertEqual(30, queue.dequeue()) 44 | self.assertTrue(queue.is_empty()) 45 | 46 | def test_ArrayQueue(self): 47 | queue = ArrayQueue() 48 | queue.enqueue(10) 49 | self.assertEqual(1, queue.size()) 50 | self.assertFalse(queue.is_empty()) 51 | queue.enqueue(20) 52 | self.assertEqual(2, queue.size()) 53 | queue.enqueue(30) 54 | 55 | print([i for i in queue.iterate()]) 56 | 57 | self.assertEqual(3, queue.size()) 58 | self.assertEqual(10, queue.dequeue()) 59 | self.assertEqual(2, queue.size()) 60 | self.assertEqual(20, queue.dequeue()) 61 | self.assertEqual(1, queue.size()) 62 | self.assertEqual(30, queue.dequeue()) 63 | self.assertTrue(queue.is_empty()) 64 | 65 | for i in range(100): 66 | queue.enqueue(i) 67 | 68 | for i in range(100): 69 | queue.dequeue() 70 | 71 | if __name__ == '__main__': 72 | unittest.main() -------------------------------------------------------------------------------- /pyalgs/algorithms/graphs/search.py: -------------------------------------------------------------------------------- 1 | from abc import ABCMeta, abstractmethod 2 | 3 | from pyalgs.data_structures.commons.queue import Queue 4 | from pyalgs.data_structures.commons.stack import Stack 5 | from pyalgs.data_structures.graphs.graph import EdgeWeightedGraph 6 | 7 | 8 | class Paths(object): 9 | __metaclass__ = ABCMeta 10 | 11 | @abstractmethod 12 | def pathTo(self, v): 13 | pass 14 | 15 | @abstractmethod 16 | def hasPathTo(self, v): 17 | pass 18 | 19 | 20 | class DepthFirstSearch(Paths): 21 | marked = None 22 | edgesTo = None 23 | s = None 24 | 25 | def __init__(self, G, s): 26 | if isinstance(G, EdgeWeightedGraph): 27 | G = G.to_graph() 28 | self.s = s 29 | vertex_count = G.vertex_count() 30 | self.marked = [False] * vertex_count 31 | self.edgesTo = [-1] * vertex_count 32 | self.dfs(G, s) 33 | 34 | def dfs(self, G, v): 35 | self.marked[v] = True 36 | for w in G.adj(v): 37 | if not self.marked[w]: 38 | self.edgesTo[w] = v 39 | self.dfs(G, w) 40 | 41 | def hasPathTo(self, v): 42 | return self.marked[v] 43 | 44 | def pathTo(self, v): 45 | x = v 46 | path = Stack.create() 47 | while x != self.s: 48 | path.push(x) 49 | x = self.edgesTo[x] 50 | path.push(self.s) 51 | return path.iterate() 52 | 53 | 54 | class BreadthFirstSearch(Paths): 55 | 56 | marked = None 57 | s = None 58 | edgeTo = None 59 | 60 | def __init__(self, G, s): 61 | if isinstance(G, EdgeWeightedGraph): 62 | G = G.to_graph() 63 | self.s = s 64 | vertex_count = G.vertex_count() 65 | self.marked = [False] * vertex_count 66 | self.edgeTo = [-1] * vertex_count 67 | 68 | queue = Queue.create() 69 | 70 | queue.enqueue(s) 71 | while not queue.is_empty(): 72 | v = queue.dequeue() 73 | self.marked[v] = True 74 | for w in G.adj(v): 75 | if not self.marked[w]: 76 | self.edgeTo[w] = v 77 | queue.enqueue(w) 78 | 79 | def hasPathTo(self, v): 80 | return self.marked[v] 81 | 82 | def pathTo(self, v): 83 | x = v 84 | path = Stack.create() 85 | while x != self.s: 86 | path.push(x) 87 | x = self.edgeTo[x] 88 | path.push(self.s) 89 | return path.iterate() -------------------------------------------------------------------------------- /pyalgs/algorithms/graphs/connectivity.py: -------------------------------------------------------------------------------- 1 | from abc import ABCMeta, abstractmethod 2 | 3 | from pyalgs.algorithms.graphs.topological_sort import DepthFirstOrder 4 | from pyalgs.data_structures.graphs.graph import EdgeWeightedGraph, Digraph, DirectedEdgeWeightedGraph 5 | 6 | 7 | class ConnectedComponents(object): 8 | marked = None 9 | _id = None 10 | _count = 0 11 | 12 | def __init__(self, G): 13 | if isinstance(G, EdgeWeightedGraph): 14 | G = G.to_graph() 15 | 16 | vertex_count = G.vertex_count() 17 | self.marked = [False] * vertex_count 18 | self._id = [-1] * vertex_count 19 | for v in range(vertex_count): 20 | if not self.marked[v]: 21 | self.dfs(G, v) 22 | self._count += 1 23 | 24 | def dfs(self, G, v): 25 | self.marked[v] = True 26 | self._id[v] = self._count 27 | for w in G.adj(v): 28 | if not self.marked[w]: 29 | self.dfs(G, w) 30 | 31 | def connected(self, v, w): 32 | return self._id[v] == self._id[w] 33 | 34 | def count(self): 35 | return self._count 36 | 37 | def id(self, v): 38 | return self._id[v] 39 | 40 | 41 | class StronglyConnectedComponents(object): 42 | marked = None 43 | _id = None 44 | _count = 0 45 | 46 | def __init__(self, G): 47 | if isinstance(G, EdgeWeightedGraph): 48 | raise ValueError('Graph must be directed graph for strongly connected components') 49 | if isinstance(G, DirectedEdgeWeightedGraph): 50 | G = G.to_digrah() 51 | if not isinstance(G, Digraph): 52 | raise ValueError('Graph must be directed graph for strongly connected components') 53 | 54 | vertex_count = G.vertex_count() 55 | self.marked = [False] * vertex_count 56 | self._id = [-1] * vertex_count 57 | 58 | dfo = DepthFirstOrder(G.reverse()) 59 | orders = dfo.postOrder() 60 | 61 | for v in orders: 62 | if not self.marked[v]: 63 | self.dfs(G, v) 64 | self._count += 1 65 | 66 | def dfs(self, G, v): 67 | self.marked[v] = True 68 | self._id[v] = self._count 69 | for w in G.adj(v): 70 | if not self.marked[w]: 71 | self.dfs(G, w) 72 | 73 | def connected(self, v, w): 74 | return self._id[v] == self._id[w] 75 | 76 | def count(self): 77 | return self._count 78 | 79 | def id(self, v): 80 | return self._id[v] 81 | -------------------------------------------------------------------------------- /docs/symbol-table.rst: -------------------------------------------------------------------------------- 1 | Symbol Table 2 | ============ 3 | 4 | Symbol Table using Binary Search Tree 5 | ------------------------------------- 6 | 7 | .. code-block:: python 8 | 9 | from pyalgs.data_structures.commons.binary_search_tree import BinarySearchTree 10 | bst = BinarySearchTree.create() 11 | 12 | bst.put("one", 1) 13 | bst.put("two", 2) 14 | bst.put("three", 3) 15 | bst.put("six", 6) 16 | bst.put("ten", 10) 17 | 18 | for key in bst.keys(): 19 | print(key) 20 | 21 | print bst.get("one")) 22 | print bst.contains_key("two") 23 | 24 | print bst.size() 25 | print bst.is_empty() 26 | 27 | bst.delete("one") 28 | 29 | 30 | Symbol Table using Left Leaning Red Black Tree 31 | ---------------------------------------------- 32 | 33 | .. code-block:: python 34 | 35 | from pyalgs.data_structures.commons.binary_search_tree import BinarySearchTree 36 | bst = BinarySearchTree.create_red_black_tree() 37 | 38 | bst.put("one", 1) 39 | bst.put("two", 2) 40 | bst.put("three", 3) 41 | bst.put("six", 6) 42 | bst.put("ten", 10) 43 | 44 | print bst.get("one")) 45 | print bst.contains_key("two") 46 | 47 | for key in bst.keys(): 48 | print(key) 49 | 50 | print bst.size() 51 | print bst.is_empty() 52 | 53 | bst.delete("one") 54 | 55 | 56 | Symbol Table using Hashed Map 57 | ----------------------------- 58 | 59 | .. code-block:: python 60 | 61 | from pyalgs.data_structures.commons.hashed_map import HashedMap 62 | map = HashedMap.create() 63 | 64 | map.put("one", 1) 65 | map.put("two", 2) 66 | map.put("three", 3) 67 | map.put("six", 6) 68 | map.put("ten", 10) 69 | 70 | print map.get("one")) 71 | print map.contains_key("two") 72 | 73 | for key in map.keys(): 74 | print(key) 75 | 76 | print map.size() 77 | print map.is_empty() 78 | 79 | map.delete("one") 80 | 81 | 82 | Symbol Table using Hashed Set 83 | ----------------------------- 84 | 85 | .. code-block:: python 86 | 87 | from pyalgs.data_structures.commons.hashed_set import HashedSet 88 | set = HashedSet.create() 89 | 90 | set.add("one") 91 | set.add("two") 92 | set.add("three") 93 | set.add("six") 94 | set.add("ten") 95 | 96 | print set.contains("two") 97 | 98 | for key in set.iterate(): 99 | print(key) 100 | 101 | print set.size() 102 | print set.is_empty() 103 | 104 | set.delete("one") 105 | 106 | 107 | -------------------------------------------------------------------------------- /pyalgs/algorithms/strings/sorting.py: -------------------------------------------------------------------------------- 1 | from pyalgs.algorithms.commons import util 2 | 3 | 4 | class LSD(object): 5 | R = 256 6 | 7 | @staticmethod 8 | def sort(a): 9 | W = len(a[0]) 10 | aux = [None] * len(a) 11 | 12 | for d in range(W - 1, -1, -1): 13 | count = [0] * (LSD.R + 1) 14 | 15 | for i in range(len(a)): 16 | count[ord(a[i][d]) + 1] += 1 17 | 18 | for r in range(0, LSD.R): 19 | count[r + 1] += count[r] 20 | 21 | for i in range(len(a)): 22 | aux[count[ord(a[i][d])]] = a[i] 23 | count[ord(a[i][d])] += 1 24 | 25 | for i in range(len(a)): 26 | a[i] = aux[i] 27 | 28 | 29 | class MSD(object): 30 | R = 256 31 | 32 | @staticmethod 33 | def sort(a): 34 | MSD._sort(a, 0, len(a) - 1, 0) 35 | 36 | @staticmethod 37 | def _sort(a, lo, hi, d): 38 | if hi - lo <= 0: 39 | return 40 | 41 | count = [0] * (MSD.R + 2) 42 | aux = [None] * (hi - lo + 1) 43 | 44 | for i in range(lo, hi + 1): 45 | count[MSD.char_at(a[i], d) + 2] += 1 46 | 47 | for r in range(0, MSD.R): 48 | count[r + 1] += count[r] 49 | 50 | for i in range(lo, hi + 1): 51 | aux[count[MSD.char_at(a[i], d) + 1]] = a[i] 52 | count[MSD.char_at(a[i], d) + 1] += 1 53 | 54 | for i in range(lo, hi + 1): 55 | a[i] = aux[i - lo] 56 | 57 | for r in range(MSD.R): 58 | MSD._sort(a, lo + count[r], lo + count[r + 1] - 1, d + 1) 59 | 60 | @staticmethod 61 | def char_at(text, d): 62 | if len(text) <= d + 1: 63 | return -1 64 | return ord(text[d]) 65 | 66 | 67 | class String3WayQuickSort(object): 68 | @staticmethod 69 | def sort(a): 70 | String3WayQuickSort._sort(a, 0, len(a) - 1, 0) 71 | 72 | @staticmethod 73 | def _sort(a, lo, hi, d): 74 | if lo >= hi: 75 | return 76 | 77 | lt = lo 78 | i = lo 79 | gt = hi 80 | 81 | c = String3WayQuickSort.char_at(a[lo], d) 82 | 83 | while i < gt: 84 | cmp = c - String3WayQuickSort.char_at(a[i], d) 85 | if cmp > 0: 86 | util.exchange(a, i, lt) 87 | i += 1 88 | lt += 1 89 | elif cmp < 0: 90 | util.exchange(a, i, gt) 91 | gt -= 1 92 | else: 93 | i += 1 94 | 95 | String3WayQuickSort._sort(a, lo, lt-1, d) 96 | if c >= 0: 97 | String3WayQuickSort._sort(a, lt, gt, d+1) 98 | String3WayQuickSort._sort(a, gt+1, hi, d) 99 | 100 | @staticmethod 101 | def char_at(text, d): 102 | if len(text) <= d + 1: 103 | return -1 104 | return ord(text[d]) 105 | -------------------------------------------------------------------------------- /pyalgs/data_structures/commons/stack.py: -------------------------------------------------------------------------------- 1 | from abc import ABCMeta, abstractmethod 2 | 3 | 4 | class StackNode(object): 5 | item = None 6 | nextNode = None 7 | 8 | def __init__(self, item): 9 | self.item = item 10 | 11 | 12 | class Stack(object): 13 | """ Stack interface which provides the API for stack data structure 14 | """ 15 | 16 | __metaclass__ = ABCMeta 17 | 18 | @abstractmethod 19 | def push(self, item): 20 | pass 21 | 22 | @abstractmethod 23 | def pop(self): 24 | pass 25 | 26 | @abstractmethod 27 | def is_empty(self): 28 | pass 29 | 30 | @abstractmethod 31 | def size(self): 32 | pass 33 | 34 | @abstractmethod 35 | def iterate(self): 36 | pass 37 | 38 | @staticmethod 39 | def create(): 40 | return LinkedListStack() 41 | 42 | 43 | class LinkedListStack(Stack): 44 | """ Linked list implementation of stack 45 | """ 46 | first = None 47 | N = 0 48 | 49 | def push(self, item): 50 | node = StackNode(item) 51 | old_first = self.first 52 | node.nextNode = old_first 53 | self.first = node 54 | self.N += 1 55 | 56 | def pop(self): 57 | if self.is_empty(): 58 | return None 59 | old_first = self.first 60 | if old_first.nextNode is None: 61 | self.first = None 62 | self.first = old_first.nextNode 63 | self.N -= 1 64 | return old_first.item 65 | 66 | def is_empty(self): 67 | return self.N == 0 68 | 69 | def size(self): 70 | return self.N 71 | 72 | def iterate(self): 73 | x = self.first 74 | while x is not None: 75 | value = x.item 76 | x = x.nextNode 77 | yield value 78 | 79 | 80 | class ArrayStack(Stack): 81 | """ Array implementation of stack 82 | """ 83 | 84 | def __init__(self, capacity=None): 85 | if capacity is None: 86 | capacity = 10 87 | self.s = [0] * capacity 88 | self.N = 0 89 | 90 | def push(self, item): 91 | self.s[self.N] = item 92 | self.N += 1 93 | if self.N == len(self.s): 94 | self.resize(len(self.s) * 2) 95 | 96 | def resize(self, new_size): 97 | tmp = [0] * new_size 98 | for i in range(min(new_size, len(self.s))): 99 | tmp[i] = self.s[i] 100 | self.s = tmp 101 | 102 | def pop(self): 103 | value = self.s[self.N-1] 104 | self.N -= 1 105 | if self.N == len(self.s) // 4: 106 | self.resize(len(self.s) // 2) 107 | return value 108 | 109 | def is_empty(self): 110 | return self.N == 0 111 | 112 | def size(self): 113 | return self.N 114 | 115 | def iterate(self): 116 | if self.is_empty(): 117 | return 118 | for i in reversed(range(self.N)): 119 | yield self.s[i] 120 | -------------------------------------------------------------------------------- /tests/data_structures/commons/binary_search_tree_unit_test.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | 3 | from pyalgs.data_structures.commons.binary_search_tree import BinarySearchTree 4 | 5 | 6 | class BinarySearchTreeUnitTest(unittest.TestCase): 7 | def test_binarySearchTree(self): 8 | bst = BinarySearchTree.create() 9 | 10 | bst.put("one", 1) 11 | bst.put("two", 2) 12 | bst.put("three", 3) 13 | bst.put("six", 6) 14 | bst.put("ten", 10) 15 | bst.put("ten", 10) 16 | 17 | self.assertEqual(1, bst.get("one")) 18 | self.assertEqual(2, bst.get("two")) 19 | self.assertEqual(3, bst.get("three")) 20 | 21 | self.assertTrue(bst.contains_key("one")) 22 | self.assertTrue(bst.contains_key("two")) 23 | 24 | self.assertEqual(5, bst.size()) 25 | self.assertFalse(bst.is_empty()) 26 | 27 | bst.delete("one") 28 | self.assertFalse(bst.contains_key("one")) 29 | self.assertEqual(4, bst.size()) 30 | 31 | bst.delete("ten") 32 | self.assertFalse(bst.contains_key("ten")) 33 | self.assertEqual(3, bst.size()) 34 | 35 | bst.delete("three") 36 | self.assertFalse(bst.contains_key("three")) 37 | self.assertEqual(2, bst.size()) 38 | 39 | for i in range(100): 40 | bst.put(str(i), i) 41 | self.assertEqual(i, bst.get(str(i))) 42 | 43 | for key in bst.keys(): 44 | print(key) 45 | 46 | for i in range(100): 47 | bst.delete(str(i)) 48 | self.assertFalse(bst.contains_key(str(i))) 49 | 50 | 51 | 52 | 53 | class RedBlackTreeUnitTest(unittest.TestCase): 54 | def test_binarySearchTree(self): 55 | bst = BinarySearchTree.create_red_black_tree() 56 | 57 | bst.put("one", 1) 58 | bst.put("two", 2) 59 | bst.put("three", 3) 60 | bst.put("six", 6) 61 | bst.put("ten", 10) 62 | bst.put("ten", 10) 63 | 64 | self.assertEqual(1, bst.get("one")) 65 | self.assertEqual(2, bst.get("two")) 66 | self.assertEqual(3, bst.get("three")) 67 | 68 | self.assertTrue(bst.contains_key("one")) 69 | self.assertTrue(bst.contains_key("two")) 70 | 71 | self.assertEqual(5, bst.size()) 72 | self.assertFalse(bst.is_empty()) 73 | 74 | bst.delete("one") 75 | self.assertFalse(bst.contains_key("one")) 76 | self.assertEqual(4, bst.size()) 77 | 78 | bst.delete("ten") 79 | self.assertFalse(bst.contains_key("ten")) 80 | self.assertEqual(3, bst.size()) 81 | 82 | bst.delete("three") 83 | self.assertFalse(bst.contains_key("three")) 84 | self.assertEqual(2, bst.size()) 85 | 86 | for i in range(100): 87 | bst.put(str(i), i) 88 | self.assertEqual(i, bst.get(str(i))) 89 | 90 | for key in bst.keys(): 91 | print(key) 92 | 93 | for i in range(100): 94 | bst.delete(str(i)) 95 | self.assertFalse(bst.contains_key(str(i))) 96 | 97 | 98 | if __name__ == '__main__': 99 | unittest.main() -------------------------------------------------------------------------------- /docs/graphs-create.rst: -------------------------------------------------------------------------------- 1 | Create Graphs 2 | ============= 3 | 4 | Create Undirected Graph 5 | ----------------------- 6 | 7 | .. code-block:: python 8 | 9 | from pyalgs.data_structures.graphs.graph import Graph 10 | def create_graph(): 11 | G = Graph(100) 12 | 13 | G.add_edge(1, 2) 14 | G.add_edge(1, 3) 15 | 16 | print([i for i in G.adj(1)]) 17 | print([i for i in G.adj(2)]) 18 | print([i for i in G.adj(3)]) 19 | 20 | print(G.vertex_count()) 21 | 22 | Create Directed Graph 23 | --------------------- 24 | 25 | .. code-block:: python 26 | 27 | from pyalgs.data_structures.graphs.graph import Digraph 28 | def create_digraph(): 29 | G = Digraph(100) 30 | 31 | G.add_edge(1, 2) 32 | G.add_edge(1, 3) 33 | 34 | print([i for i in G.adj(1)]) 35 | print([i for i in G.adj(2)]) 36 | print([i for i in G.adj(3)]) 37 | 38 | print(G.vertex_count()) 39 | 40 | Edge Weighted Graph 41 | ------------------- 42 | 43 | .. code-block:: python 44 | 45 | from pyalgs.data_structures.graphs.graph import EdgeWeightGraph, Edge 46 | def create_edge_weighted_graph(): 47 | g = EdgeWeightedGraph(8) 48 | g.add_edge(Edge(0, 7, 0.16)) 49 | g.add_edge(Edge(2, 3, 0.17)) 50 | g.add_edge(Edge(1, 7, 0.19)) 51 | g.add_edge(Edge(0, 2, 0.26)) 52 | g.add_edge(Edge(5, 7, 0.28)) 53 | 54 | print([edge for edge in G.adj(3)]) 55 | 56 | print(G.vertex_count()) 57 | print(', '.join([edge for edge in G.edges()])) 58 | return g 59 | 60 | Directed Edge Weighted Graph 61 | ---------------------------- 62 | 63 | .. code-block:: python 64 | 65 | from pyalgs.data_structures.graphs.graph import DirectedEdgeWeightedGraph, Edge 66 | def create_edge_weighted_digraph(): 67 | g = DirectedEdgeWeightedGraph(8) 68 | 69 | g.add_edge( 70 | Edge(0, 1, 5.0)) 71 | g.add_edge( 72 | Edge(0, 4, 9.0)) 73 | g.add_edge( 74 | Edge(0, 7, 8.0)) 75 | g.add_edge( 76 | Edge(1, 2, 12.0)) 77 | return g 78 | 79 | 80 | Flow Network ( for max-flow min-cut problem) 81 | -------------------------------------------- 82 | 83 | .. code-block:: python 84 | 85 | from pyalgs.data_structures.graphs.graph import FlowNetwork, FlowEdge 86 | def create_flow_network(): 87 | g = FlowNetwork(8) 88 | g.add_edge(FlowEdge(0, 1, 10)) 89 | g.add_edge(FlowEdge(0, 2, 5)) 90 | g.add_edge(FlowEdge(0, 3, 15)) 91 | g.add_edge(FlowEdge(1, 4, 9)) 92 | g.add_edge(FlowEdge(1, 5, 15)) 93 | g.add_edge(FlowEdge(1, 2, 4)) 94 | g.add_edge(FlowEdge(2, 5, 8)) 95 | g.add_edge(FlowEdge(2, 3, 4)) 96 | g.add_edge(FlowEdge(3, 6, 16)) 97 | g.add_edge(FlowEdge(4, 5, 15)) 98 | g.add_edge(FlowEdge(4, 7, 10)) 99 | g.add_edge(FlowEdge(5, 7, 10)) 100 | g.add_edge(FlowEdge(5, 6, 15)) 101 | g.add_edge(FlowEdge(6, 2, 6)) 102 | g.add_edge(FlowEdge(6, 7, 10)) 103 | 104 | return g 105 | -------------------------------------------------------------------------------- /pyalgs/data_structures/commons/queue.py: -------------------------------------------------------------------------------- 1 | from abc import abstractmethod, ABCMeta 2 | 3 | 4 | class Queue(object): 5 | """ Queue interface 6 | 7 | """ 8 | 9 | __metaclass__ = ABCMeta 10 | 11 | @abstractmethod 12 | def enqueue(self, item): 13 | pass 14 | 15 | @abstractmethod 16 | def dequeue(self): 17 | pass 18 | 19 | @abstractmethod 20 | def is_empty(self): 21 | pass 22 | 23 | @abstractmethod 24 | def size(self): 25 | pass 26 | 27 | @staticmethod 28 | def create(): 29 | return LinkedListQueue() 30 | 31 | @abstractmethod 32 | def iterate(self): 33 | pass 34 | 35 | 36 | class Node(object): 37 | 38 | value = None 39 | nextNode = None 40 | 41 | def __init__(self, value): 42 | self.value = value 43 | 44 | 45 | class LinkedListQueue(Queue): 46 | 47 | first = None 48 | last = None 49 | N = 0 50 | 51 | def size(self): 52 | return self.N 53 | 54 | def iterate(self): 55 | x = self.first 56 | while x is not None: 57 | value = x.value 58 | x = x.nextNode 59 | yield value 60 | 61 | def enqueue(self, item): 62 | old_last = self.last 63 | self.last = Node(item) 64 | if old_last is not None: 65 | old_last.nextNode = self.last 66 | if self.first is None: 67 | self.first = self.last 68 | self.N += 1 69 | 70 | def is_empty(self): 71 | return self.N == 0 72 | 73 | def dequeue(self): 74 | if self.is_empty(): 75 | return None 76 | old_first = self.first 77 | self.first = old_first.nextNode 78 | if old_first == self.last: 79 | self.last = None 80 | self.N -= 1 81 | return old_first.value 82 | 83 | 84 | class ArrayQueue(Queue): 85 | 86 | head = 0 87 | tail = 0 88 | s = [] 89 | 90 | def __init__(self, capacity=None): 91 | if capacity is None: 92 | capacity = 10 93 | self.s = [0] * capacity 94 | 95 | def iterate(self): 96 | if self.is_empty(): 97 | return 98 | for i in range(self.head, self.tail): 99 | yield self.s[i] 100 | 101 | def enqueue(self, item): 102 | self.s[self.tail] = item 103 | self.tail += 1 104 | if self.tail == len(self.s): 105 | self.resize(len(self.s) * 2) 106 | 107 | def resize(self, new_size): 108 | tmp = [0] * new_size 109 | for i in range(self.head, self.tail): 110 | tmp[i-self.head] = self.s[i] 111 | self.s = tmp 112 | self.tail = self.tail - self.head 113 | self.head = 0 114 | 115 | def size(self): 116 | return self.tail - self.head 117 | 118 | def is_empty(self): 119 | return self.size() == 0 120 | 121 | def dequeue(self): 122 | if self.is_empty(): 123 | return None 124 | 125 | deleted = self.s[self.head] 126 | self.head += 1 127 | if self.size() == len(self.s) // 4: 128 | self.resize(len(self.s) // 2) 129 | return deleted 130 | -------------------------------------------------------------------------------- /tests/data_structures/strings/search_tries_unit_test.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | 3 | from pyalgs.data_structures.strings.search_tries import RWaySearchTries, TernarySearchTries 4 | 5 | 6 | class RWaySearchTriesUnitTest(unittest.TestCase): 7 | def test_put(self): 8 | st = RWaySearchTries() 9 | 10 | st.put("one", 1) 11 | st.put("two", 2) 12 | st.put("three", 3) 13 | st.put("six", 6) 14 | st.put("ten", 10) 15 | st.put("ten", 10) 16 | 17 | self.assertEqual(1, st.get("one")) 18 | self.assertEqual(2, st.get("two")) 19 | self.assertEqual(3, st.get("three")) 20 | 21 | self.assertTrue(st.contains_key("one")) 22 | self.assertTrue(st.contains_key("two")) 23 | 24 | self.assertEqual(5, st.size()) 25 | self.assertFalse(st.is_empty()) 26 | 27 | st.delete("one") 28 | self.assertFalse(st.contains_key("one")) 29 | self.assertEqual(4, st.size()) 30 | 31 | st.delete("ten") 32 | self.assertFalse(st.contains_key("ten")) 33 | self.assertEqual(3, st.size()) 34 | 35 | st.delete("three") 36 | self.assertFalse(st.contains_key("three")) 37 | self.assertEqual(2, st.size()) 38 | 39 | for i in range(100): 40 | st.put(str(i), i) 41 | self.assertEqual(i, st.get(str(i))) 42 | 43 | for key in st.keys(): 44 | print(key) 45 | 46 | for i in range(100): 47 | st.delete(str(i)) 48 | self.assertFalse(st.contains_key(str(i))) 49 | 50 | st.put('there', 2) 51 | st.put('this', 2) 52 | print('with prefix t') 53 | for key in st.keys_with_prefix('t'): 54 | print(key) 55 | 56 | 57 | class TernarySearchTriesUnitTest(unittest.TestCase): 58 | def test_put(self): 59 | st = TernarySearchTries() 60 | 61 | st.put("one", 1) 62 | st.put("two", 2) 63 | st.put("three", 3) 64 | st.put("six", 6) 65 | st.put("ten", 10) 66 | st.put("ten", 10) 67 | 68 | self.assertEqual(1, st.get("one")) 69 | self.assertEqual(2, st.get("two")) 70 | self.assertEqual(3, st.get("three")) 71 | 72 | self.assertTrue(st.contains_key("one")) 73 | self.assertTrue(st.contains_key("two")) 74 | 75 | self.assertEqual(5, st.size()) 76 | self.assertFalse(st.is_empty()) 77 | 78 | st.delete("one") 79 | self.assertFalse(st.contains_key("one")) 80 | self.assertEqual(4, st.size()) 81 | 82 | st.delete("ten") 83 | self.assertFalse(st.contains_key("ten")) 84 | self.assertEqual(3, st.size()) 85 | 86 | st.delete("three") 87 | self.assertFalse(st.contains_key("three")) 88 | self.assertEqual(2, st.size()) 89 | 90 | for i in range(100): 91 | st.put(str(i), i) 92 | self.assertEqual(i, st.get(str(i))) 93 | 94 | for key in st.keys(): 95 | print(key) 96 | 97 | for i in range(100): 98 | st.delete(str(i)) 99 | self.assertFalse(st.contains_key(str(i))) 100 | 101 | st.put('there', 2) 102 | st.put('this', 2) 103 | print('with prefix t') 104 | for key in st.keys_with_prefix('t'): 105 | print(key) 106 | 107 | 108 | if __name__ == '__main__': 109 | unittest.main() 110 | -------------------------------------------------------------------------------- /pyalgs/algorithms/strings/substring_search.py: -------------------------------------------------------------------------------- 1 | from abc import ABCMeta, abstractmethod 2 | 3 | 4 | class SubstringSearch(object): 5 | __metaclass__ = ABCMeta 6 | 7 | @abstractmethod 8 | def search_in(self, text): 9 | pass 10 | 11 | 12 | class BruteForceSubstringSearch(SubstringSearch): 13 | def __init__(self, pattern): 14 | self.pattern = pattern 15 | 16 | def search_in(self, text): 17 | n = len(text) 18 | 19 | for i in range(n - len(self.pattern)): 20 | J = i 21 | for j in range(len(self.pattern)): 22 | k = i + j 23 | if text[k] != self.pattern[j]: 24 | J = -1 25 | break 26 | if J != -1: 27 | return J 28 | 29 | return -1 30 | 31 | 32 | def char_at(text, d): 33 | return ord(text[d]) 34 | 35 | 36 | class RabinKarp(SubstringSearch): 37 | patHash = None 38 | Q = 1573773197 39 | R = 256 40 | M = None 41 | RM = None 42 | 43 | def __init__(self, pat): 44 | h = 1 45 | self.M = len(pat) 46 | for i in range(1, self.M): 47 | h = (h * RabinKarp.R) % RabinKarp.Q 48 | 49 | self.RM = h 50 | 51 | self.patHash = self.hash(pat, self.M) 52 | 53 | def hash(self, text, M): 54 | h = 0 55 | for d in range(M): 56 | h = (h * RabinKarp.R + char_at(text, d)) % RabinKarp.Q 57 | return h 58 | 59 | def search_in(self, text): 60 | text_hash = self.hash(text, self.M) 61 | if text_hash == self.patHash: 62 | return 0 63 | for i in range(self.M, len(text)): 64 | text_hash = (text_hash + RabinKarp.Q - self.RM * char_at(text, i - self.M) % RabinKarp.Q) % RabinKarp.Q 65 | text_hash = (text_hash * RabinKarp.R + char_at(text, i)) % RabinKarp.Q 66 | if text_hash == self.patHash: 67 | return i - self.M + 1 68 | return -1 69 | 70 | 71 | class BoyerMoore(SubstringSearch): 72 | right = None 73 | R = 256 74 | pattern = None 75 | 76 | def __init__(self, pattern): 77 | self.pattern = pattern 78 | self.right = [0] * BoyerMoore.R 79 | for i in range(len(pattern)): 80 | self.right[char_at(pattern, i)] = i 81 | 82 | def search_in(self, text): 83 | n = len(text) 84 | m = len(self.pattern) 85 | 86 | skip = 1 87 | i = 0 88 | while i <= n - m: 89 | for j in range(m-1, -1, -1): 90 | if char_at(text, i+j) != char_at(self.pattern, j): 91 | skip = max(1, j - self.right[char_at(text, i+j)]) 92 | break 93 | if j == 0: 94 | return i 95 | i += skip 96 | 97 | return -1 98 | 99 | 100 | class KnuthMorrisPratt(SubstringSearch): 101 | 102 | dfs = None 103 | M = None 104 | R = 256 105 | 106 | def __init__(self, pattern): 107 | self.M = len(pattern) 108 | 109 | self.dfs = [None] * self.R 110 | for i in range(self.R): 111 | self.dfs[i] = [0] * self.M 112 | self.dfs[0][0] = 1 113 | 114 | X = 0 115 | for j in range(self.M): 116 | for i in range(self.R): 117 | self.dfs[i][j] = self.dfs[i][X] 118 | self.dfs[char_at(pattern, j)][j] = j+1 119 | X = self.dfs[char_at(pattern, j)][X] 120 | 121 | def search_in(self, text): 122 | 123 | n = len(text) 124 | 125 | j = 0 126 | for i in range(n): 127 | j = self.dfs[char_at(text, i)][j] 128 | if j == self.M: 129 | return i - self.M 130 | 131 | return -1 -------------------------------------------------------------------------------- /pyalgs/algorithms/graphs/minimum_spanning_trees.py: -------------------------------------------------------------------------------- 1 | from abc import ABCMeta, abstractmethod 2 | 3 | from pyalgs.algorithms.commons.union_find import UnionFind 4 | from pyalgs.algorithms.commons.util import less 5 | from pyalgs.data_structures.commons.bag import Bag 6 | from pyalgs.data_structures.commons.priority_queue import MinPQ, IndexMinPQ 7 | from pyalgs.data_structures.graphs.graph import EdgeWeightedGraph 8 | 9 | 10 | class MST(object): 11 | __metaclass__ = ABCMeta 12 | 13 | @abstractmethod 14 | def spanning_tree(self): 15 | pass 16 | 17 | 18 | class KruskalMST(MST): 19 | tree = None 20 | 21 | def __init__(self, G): 22 | if not isinstance(G, EdgeWeightedGraph): 23 | raise ValueError('Graph must be edge weighted and undirected to run MST') 24 | minpq = MinPQ.create() 25 | self.tree = Bag() 26 | for e in G.edges(): 27 | minpq.enqueue(e) 28 | 29 | uf = UnionFind.create(G.vertex_count()) 30 | 31 | while not minpq.is_empty() and self.tree.size() < G.vertex_count() - 1: 32 | e = minpq.del_min() 33 | v = e.either() 34 | w = e.other(v) 35 | if not uf.connected(v, w): 36 | uf.union(v, w) 37 | self.tree.add(e) 38 | 39 | def spanning_tree(self): 40 | return self.tree.iterate() 41 | 42 | 43 | class LazyPrimMST(MST): 44 | tree = None 45 | marked = None 46 | minpq = None 47 | 48 | def __init__(self, G): 49 | if not isinstance(G, EdgeWeightedGraph): 50 | raise ValueError('Graph must be edge weighted and undirected to run MST') 51 | self.minpq = MinPQ.create() 52 | self.tree = Bag() 53 | vertex_count = G.vertex_count() 54 | self.marked = [False] * vertex_count 55 | self.visit(G, 0) 56 | 57 | while not self.minpq.is_empty() and self.tree.size() < vertex_count - 1: 58 | edge = self.minpq.del_min() 59 | v = edge.either() 60 | w = edge.other(v) 61 | if self.marked[v] and self.marked[w]: 62 | continue 63 | self.tree.add(edge) 64 | if not self.marked[v]: 65 | self.visit(G, v) 66 | if not self.marked[w]: 67 | self.visit(G, w) 68 | 69 | def visit(self, G, v): 70 | self.marked[v] = True 71 | for e in G.adj(v): 72 | w = e.other(v) 73 | if not self.marked[w]: 74 | self.minpq.enqueue(e) 75 | 76 | def spanning_tree(self): 77 | return self.tree.iterate() 78 | 79 | 80 | class EagerPrimMST(MST): 81 | path = None 82 | pq = None 83 | marked = None 84 | 85 | def __init__(self, G): 86 | if not isinstance(G, EdgeWeightedGraph): 87 | raise ValueError('Graph must be edge weighted and undirected to run MST') 88 | vertex_count = G.vertex_count() 89 | self.pq = IndexMinPQ(vertex_count) 90 | self.path = Bag() 91 | self.marked = [False] * vertex_count 92 | 93 | self.visit(G, 0) 94 | 95 | while not self.pq.is_empty() and self.path.size() < vertex_count - 1: 96 | e = self.pq.min_key() 97 | w = self.pq.del_min() 98 | self.path.add(e) 99 | if not self.marked[w]: 100 | self.visit(G, w) 101 | 102 | def visit(self, G, v): 103 | self.marked[v] = True 104 | for e in G.adj(v): 105 | w = e.other(v) 106 | if not self.marked[w]: 107 | if self.pq.contains_index(w): 108 | old_e = self.pq.get(w) 109 | if less(e, old_e): 110 | self.pq.decrease_key(w, e) 111 | else: 112 | self.pq.insert(w, e) 113 | 114 | def spanning_tree(self): 115 | return self.path.iterate() 116 | -------------------------------------------------------------------------------- /tests/data_structures/commons/hashed_set_unit_test.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | 3 | from pyalgs.data_structures.commons.hashed_set import HashedSetWithSeparateChaining, HashedSet, \ 4 | HashedSetWithLinearProbing 5 | 6 | 7 | class HashedSetUnitTest(unittest.TestCase): 8 | def test_hashedset(self): 9 | set = HashedSet.create() 10 | 11 | set.add("one") 12 | set.add("two") 13 | set.add("three") 14 | set.add("six") 15 | set.add("ten") 16 | set.add("ten") 17 | 18 | 19 | self.assertTrue(set.contains("one")) 20 | self.assertTrue(set.contains("two")) 21 | 22 | self.assertEqual(5, set.size()) 23 | self.assertFalse(set.is_empty()) 24 | 25 | set.delete("one") 26 | self.assertFalse(set.contains("one")) 27 | self.assertEqual(4, set.size()) 28 | 29 | set.delete("ten") 30 | self.assertFalse(set.contains("ten")) 31 | self.assertEqual(3, set.size()) 32 | 33 | set.delete("three") 34 | self.assertFalse(set.contains("three")) 35 | self.assertEqual(2, set.size()) 36 | 37 | for i in range(100): 38 | set.add(str(i)) 39 | self.assertTrue(set.contains(str(i))) 40 | 41 | for key in set.iterate(): 42 | print(key) 43 | 44 | for i in range(100): 45 | set.delete(str(i)) 46 | self.assertFalse(set.contains(str(i))) 47 | 48 | 49 | class HashedSetWithSeparateChainingUnitTest(unittest.TestCase): 50 | def test_binarySearchTree(self): 51 | set = HashedSetWithSeparateChaining() 52 | 53 | set.add("one") 54 | set.add("two") 55 | set.add("three") 56 | set.add("six") 57 | set.add("ten") 58 | set.add("ten") 59 | 60 | self.assertTrue(set.contains("one")) 61 | self.assertTrue(set.contains("two")) 62 | 63 | self.assertEqual(5, set.size()) 64 | self.assertFalse(set.is_empty()) 65 | 66 | set.delete("one") 67 | self.assertFalse(set.contains("one")) 68 | self.assertEqual(4, set.size()) 69 | 70 | set.delete("ten") 71 | self.assertFalse(set.contains("ten")) 72 | self.assertEqual(3, set.size()) 73 | 74 | set.delete("three") 75 | self.assertFalse(set.contains("three")) 76 | self.assertEqual(2, set.size()) 77 | 78 | for i in range(100): 79 | set.add(str(i)) 80 | self.assertTrue(set.contains(str(i))) 81 | 82 | for key in set.iterate(): 83 | print(key) 84 | 85 | for i in range(100): 86 | set.delete(str(i)) 87 | self.assertFalse(set.contains(str(i))) 88 | 89 | 90 | class HashedSetWithLinearProbingUnitTest(unittest.TestCase): 91 | def test_binarySearchTree(self): 92 | set = HashedSetWithLinearProbing() 93 | 94 | set.add("one") 95 | set.add("two") 96 | set.add("three") 97 | set.add("six") 98 | set.add("ten") 99 | set.add("ten") 100 | 101 | self.assertTrue(set.contains("one")) 102 | self.assertTrue(set.contains("two")) 103 | 104 | self.assertEqual(5, set.size()) 105 | self.assertFalse(set.is_empty()) 106 | 107 | set.delete("one") 108 | self.assertFalse(set.contains("one")) 109 | self.assertEqual(4, set.size()) 110 | 111 | set.delete("ten") 112 | self.assertFalse(set.contains("ten")) 113 | self.assertEqual(3, set.size()) 114 | 115 | set.delete("three") 116 | self.assertFalse(set.contains("three")) 117 | self.assertEqual(2, set.size()) 118 | 119 | for i in range(100): 120 | set.add(str(i)) 121 | self.assertTrue(set.contains(str(i))) 122 | 123 | for key in set.iterate(): 124 | print(key) 125 | 126 | for i in range(100): 127 | set.delete(str(i)) 128 | self.assertFalse(set.contains(str(i))) 129 | 130 | 131 | if __name__ == '__main__': 132 | unittest.main() 133 | -------------------------------------------------------------------------------- /tests/data_structures/commons/hashed_map_unit_test.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | 3 | from pyalgs.data_structures.commons.hashed_map import HashedMapWithSeparateChaining, HashedMap, \ 4 | HashedMapWithLinearProbing 5 | 6 | 7 | class HashedMapUnitTest(unittest.TestCase): 8 | def test_binarySearchTree(self): 9 | map = HashedMap.create() 10 | 11 | map.put("one", 1) 12 | map.put("two", 2) 13 | map.put("three", 3) 14 | map.put("six", 6) 15 | map.put("ten", 10) 16 | map.put("ten", 10) 17 | 18 | self.assertEqual(1, map.get("one")) 19 | self.assertEqual(2, map.get("two")) 20 | self.assertEqual(3, map.get("three")) 21 | 22 | self.assertTrue(map.contains_key("one")) 23 | self.assertTrue(map.contains_key("two")) 24 | 25 | self.assertEqual(5, map.size()) 26 | self.assertFalse(map.is_empty()) 27 | 28 | map.delete("one") 29 | self.assertFalse(map.contains_key("one")) 30 | self.assertEqual(4, map.size()) 31 | 32 | map.delete("ten") 33 | self.assertFalse(map.contains_key("ten")) 34 | self.assertEqual(3, map.size()) 35 | 36 | map.delete("three") 37 | self.assertFalse(map.contains_key("three")) 38 | self.assertEqual(2, map.size()) 39 | 40 | for i in range(100): 41 | map.put(str(i), i) 42 | self.assertEqual(i, map.get(str(i))) 43 | 44 | for key in map.keys(): 45 | print(key) 46 | 47 | for i in range(100): 48 | map.delete(str(i)) 49 | self.assertFalse(map.contains_key(str(i))) 50 | 51 | 52 | class HashedMapWithSeparateChainingUnitTest(unittest.TestCase): 53 | def test_binarySearchTree(self): 54 | map = HashedMapWithSeparateChaining() 55 | 56 | map.put("one", 1) 57 | map.put("two", 2) 58 | map.put("three", 3) 59 | map.put("six", 6) 60 | map.put("ten", 10) 61 | map.put("ten", 10) 62 | 63 | self.assertEqual(1, map.get("one")) 64 | self.assertEqual(2, map.get("two")) 65 | self.assertEqual(3, map.get("three")) 66 | 67 | self.assertTrue(map.contains_key("one")) 68 | self.assertTrue(map.contains_key("two")) 69 | 70 | self.assertEqual(5, map.size()) 71 | self.assertFalse(map.is_empty()) 72 | 73 | map.delete("one") 74 | self.assertFalse(map.contains_key("one")) 75 | self.assertEqual(4, map.size()) 76 | 77 | map.delete("ten") 78 | self.assertFalse(map.contains_key("ten")) 79 | self.assertEqual(3, map.size()) 80 | 81 | map.delete("three") 82 | self.assertFalse(map.contains_key("three")) 83 | self.assertEqual(2, map.size()) 84 | 85 | for i in range(100): 86 | map.put(str(i), i) 87 | self.assertEqual(i, map.get(str(i))) 88 | 89 | for key in map.keys(): 90 | print(key) 91 | 92 | for i in range(100): 93 | map.delete(str(i)) 94 | self.assertFalse(map.contains_key(str(i))) 95 | 96 | 97 | class HashedMapWithLinearProbingUnitTest(unittest.TestCase): 98 | def test_hashedmap(self): 99 | map = HashedMapWithLinearProbing() 100 | 101 | map.put("one", 1) 102 | map.put("two", 2) 103 | map.put("three", 3) 104 | map.put("six", 6) 105 | map.put("ten", 10) 106 | map.put("ten", 10) 107 | 108 | self.assertEqual(1, map.get("one")) 109 | self.assertEqual(2, map.get("two")) 110 | self.assertEqual(3, map.get("three")) 111 | 112 | self.assertTrue(map.contains_key("one")) 113 | self.assertTrue(map.contains_key("two")) 114 | 115 | self.assertEqual(5, map.size()) 116 | self.assertFalse(map.is_empty()) 117 | 118 | map.delete("one") 119 | self.assertFalse(map.contains_key("one")) 120 | self.assertEqual(4, map.size()) 121 | 122 | map.delete("ten") 123 | self.assertFalse(map.contains_key("ten")) 124 | self.assertEqual(3, map.size()) 125 | 126 | map.delete("three") 127 | self.assertFalse(map.contains_key("three")) 128 | self.assertEqual(2, map.size()) 129 | 130 | for i in range(100): 131 | map.put(str(i), i) 132 | self.assertEqual(i, map.get(str(i))) 133 | 134 | for key in map.keys(): 135 | print(key) 136 | 137 | for i in range(100): 138 | map.delete(str(i)) 139 | self.assertFalse(map.contains_key(str(i))) 140 | 141 | 142 | if __name__ == '__main__': 143 | unittest.main() 144 | -------------------------------------------------------------------------------- /tests/algorithms/graphs/util.py: -------------------------------------------------------------------------------- 1 | from pyalgs.data_structures.graphs.graph import Graph, Digraph, EdgeWeightedGraph, Edge, EdgeWeightedGraph, \ 2 | DirectedEdgeWeightedGraph, FlowNetwork, FlowEdge 3 | 4 | 5 | def create_graph(): 6 | g = Graph(6) 7 | g.add_edge(0, 5) 8 | g.add_edge(2, 4) 9 | g.add_edge(2, 3) 10 | g.add_edge(1, 2) 11 | g.add_edge(0, 1) 12 | g.add_edge(3, 4) 13 | g.add_edge(3, 5) 14 | g.add_edge(0, 2) 15 | 16 | return g 17 | 18 | 19 | def create_graph_4_connected_components(): 20 | g = Graph(13) 21 | g.add_edge(0, 5) 22 | g.add_edge(4, 3) 23 | g.add_edge(0, 1) 24 | g.add_edge(9, 12) 25 | g.add_edge(6, 4) 26 | g.add_edge(5, 4) 27 | g.add_edge(0, 2) 28 | g.add_edge(11, 12) 29 | g.add_edge(9, 10) 30 | g.add_edge(0, 6) 31 | g.add_edge(7, 8) 32 | g.add_edge(9, 11) 33 | g.add_edge(5, 3) 34 | return g 35 | 36 | 37 | def create_digraph(): 38 | g = Digraph(13) 39 | g.add_edge(4, 2) 40 | g.add_edge(2, 3) 41 | g.add_edge(3, 2) 42 | g.add_edge(6, 0) 43 | g.add_edge(0, 1) 44 | g.add_edge(2, 0) 45 | g.add_edge(11, 12) 46 | g.add_edge(12, 9) 47 | g.add_edge(9, 10) 48 | g.add_edge(9, 11) 49 | g.add_edge(7, 9) 50 | g.add_edge(10, 12) 51 | g.add_edge(11, 4) 52 | g.add_edge(4, 3) 53 | g.add_edge(3, 5) 54 | g.add_edge(6, 8) 55 | g.add_edge(8, 6) 56 | g.add_edge(5, 4) 57 | g.add_edge(0, 5) 58 | g.add_edge(6, 4) 59 | g.add_edge(6, 9) 60 | g.add_edge(7, 6) 61 | 62 | return g 63 | 64 | 65 | def create_dag(): 66 | dag = Digraph(7) 67 | 68 | dag.add_edge(0, 5) 69 | dag.add_edge(0, 2) 70 | dag.add_edge(0, 1) 71 | dag.add_edge(3, 6) 72 | dag.add_edge(3, 5) 73 | dag.add_edge(3, 4) 74 | dag.add_edge(5, 4) 75 | dag.add_edge(6, 4) 76 | dag.add_edge(6, 0) 77 | dag.add_edge(3, 2) 78 | dag.add_edge(1, 4) 79 | 80 | return dag 81 | 82 | 83 | def create_edge_weighted_graph(): 84 | g = EdgeWeightedGraph(8) 85 | g.add_edge(Edge(0, 7, 0.16)) 86 | g.add_edge(Edge(2, 3, 0.17)) 87 | g.add_edge(Edge(1, 7, 0.19)) 88 | g.add_edge(Edge(0, 2, 0.26)) 89 | g.add_edge(Edge(5, 7, 0.28)) 90 | g.add_edge(Edge(1, 3, 0.29)) 91 | g.add_edge(Edge(1, 5, 0.32)) 92 | g.add_edge(Edge(2, 7, 0.34)) 93 | g.add_edge(Edge(4, 5, 0.35)) 94 | g.add_edge(Edge(1, 2, 0.36)) 95 | g.add_edge(Edge(4, 7, 0.37)) 96 | g.add_edge(Edge(0, 4, 0.38)) 97 | g.add_edge(Edge(6, 2, 0.4)) 98 | g.add_edge(Edge(3, 6, 0.52)) 99 | g.add_edge(Edge(6, 0, 0.58)) 100 | g.add_edge(Edge(6, 4, 0.93)) 101 | 102 | return g 103 | 104 | 105 | def create_edge_weighted_digraph(): 106 | g = DirectedEdgeWeightedGraph(8) 107 | 108 | g.add_edge( 109 | Edge(0, 1, 5.0)) 110 | g.add_edge( 111 | Edge(0, 4, 9.0)) 112 | g.add_edge( 113 | Edge(0, 7, 8.0)) 114 | g.add_edge( 115 | Edge(1, 2, 12.0)) 116 | g.add_edge( 117 | Edge(1, 3, 15.0)) 118 | g.add_edge( 119 | Edge(1, 7, 4.0)) 120 | g.add_edge( 121 | Edge(2, 3, 3.0)) 122 | g.add_edge( 123 | Edge(2, 6, 11.0)) 124 | g.add_edge( 125 | Edge(3, 6, 9.0)) 126 | g.add_edge( 127 | Edge(4, 5, 5.0)) 128 | g.add_edge( 129 | Edge(4, 6, 20.0)) 130 | g.add_edge( 131 | Edge(4, 7, 5.0)) 132 | g.add_edge( 133 | Edge(5, 2, 1.0)) 134 | g.add_edge( 135 | Edge(5, 6, 13.0)) 136 | g.add_edge( 137 | Edge(7, 5, 6.0)) 138 | g.add_edge( 139 | Edge(7, 2, 7.0)) 140 | 141 | return g 142 | 143 | 144 | def create_digraph_4_strongly_connected_components(): 145 | graph = Digraph(13) 146 | graph.add_edge(4, 2) 147 | graph.add_edge(2, 3) 148 | graph.add_edge(3, 2) 149 | graph.add_edge(6, 0) 150 | graph.add_edge(0, 1) 151 | graph.add_edge(2, 0) 152 | graph.add_edge(11, 12) 153 | graph.add_edge(12, 9) 154 | graph.add_edge(9, 10) 155 | graph.add_edge(9, 11) 156 | graph.add_edge(8, 9) 157 | graph.add_edge(10, 12) 158 | graph.add_edge(11, 4) 159 | graph.add_edge(4, 3) 160 | graph.add_edge(3, 5) 161 | graph.add_edge(7, 8) 162 | graph.add_edge(8, 7) 163 | graph.add_edge(5, 4) 164 | graph.add_edge(0, 5) 165 | graph.add_edge(6, 4) 166 | graph.add_edge(6, 9) 167 | graph.add_edge(7, 6) 168 | 169 | return graph 170 | 171 | 172 | def create_flow_network(): 173 | g = FlowNetwork(8) 174 | g.add_edge(FlowEdge(0, 1, 10)) 175 | g.add_edge(FlowEdge(0, 2, 5)) 176 | g.add_edge(FlowEdge(0, 3, 15)) 177 | g.add_edge(FlowEdge(1, 4, 9)) 178 | g.add_edge(FlowEdge(1, 5, 15)) 179 | g.add_edge(FlowEdge(1, 2, 4)) 180 | g.add_edge(FlowEdge(2, 5, 8)) 181 | g.add_edge(FlowEdge(2, 3, 4)) 182 | g.add_edge(FlowEdge(3, 6, 16)) 183 | g.add_edge(FlowEdge(4, 5, 15)) 184 | g.add_edge(FlowEdge(4, 7, 10)) 185 | g.add_edge(FlowEdge(5, 7, 10)) 186 | g.add_edge(FlowEdge(5, 6, 15)) 187 | g.add_edge(FlowEdge(6, 2, 6)) 188 | g.add_edge(FlowEdge(6, 7, 10)) 189 | 190 | return g 191 | -------------------------------------------------------------------------------- /pyalgs/data_structures/commons/hashed_set.py: -------------------------------------------------------------------------------- 1 | from abc import ABCMeta, abstractmethod 2 | 3 | from pyalgs.algorithms.commons import util 4 | from pyalgs.data_structures.commons.queue import Queue 5 | 6 | 7 | class HashedSet(object): 8 | __metaclass__ = ABCMeta 9 | 10 | @abstractmethod 11 | def add(self, key): 12 | pass 13 | 14 | @abstractmethod 15 | def delete(self, key): 16 | pass 17 | 18 | @abstractmethod 19 | def size(self): 20 | pass 21 | 22 | @abstractmethod 23 | def is_empty(self): 24 | pass 25 | 26 | @abstractmethod 27 | def iterate(self): 28 | pass 29 | 30 | @abstractmethod 31 | def contains(self, key): 32 | pass 33 | 34 | @staticmethod 35 | def create(): 36 | return HashedSetWithSeparateChaining() 37 | 38 | 39 | class Node(object): 40 | key = None 41 | next_node = None 42 | 43 | def __init__(self, key=None): 44 | self.key = key 45 | 46 | 47 | class HashedSetWithSeparateChaining(HashedSet): 48 | M = 97 49 | id = None 50 | N = 0 51 | 52 | def __init__(self, m=None): 53 | if m is None: 54 | m = 97 55 | self.M = m 56 | self.id = [None] * self.M 57 | 58 | def hash(self, key): 59 | return (hash(key) & 0x7fffffff) % self.M 60 | 61 | def is_empty(self): 62 | return self.N == 0 63 | 64 | def iterate(self): 65 | for i in range(self.M): 66 | x = self.id[i] 67 | while x is not None: 68 | key = x.key 69 | x = x.next_node 70 | yield key 71 | 72 | def size(self): 73 | return self.N 74 | 75 | def contains(self, key): 76 | i = self.hash(key) 77 | x = self.id[i] 78 | while x is not None: 79 | if util.cmp(x.key, key) == 0: 80 | return True 81 | x = x.next_node 82 | return False 83 | 84 | def delete(self, key): 85 | i = self.hash(key) 86 | x = self.id[i] 87 | prev_node = None 88 | while x is not None: 89 | if util.cmp(x.key, key) == 0: 90 | next_node = x.next_node 91 | self.N -= 1 92 | if prev_node is not None: 93 | prev_node.next_node = next_node 94 | if self.id[i] == x: 95 | self.id[i] = None 96 | return True 97 | prev_node = x 98 | x = x.next_node 99 | return False 100 | 101 | def add(self, key): 102 | if key is None: 103 | raise ValueError('key cannot be None') 104 | 105 | i = self.hash(key) 106 | x = self.id[i] 107 | while x is not None: 108 | if util.cmp(x.key, key) == 0: 109 | return 110 | x = x.next_node 111 | old_first = self.id[i] 112 | self.id[i] = Node(key) 113 | self.id[i].next_node = old_first 114 | self.N += 1 115 | 116 | 117 | class HashedSetWithLinearProbing(HashedSet): 118 | 119 | M = 97 120 | id = None 121 | N = 0 122 | 123 | def __init__(self, m=None): 124 | if m is None: 125 | m = 97 126 | self.M = m 127 | self.id = [None] * self.M 128 | 129 | def hash(self, key): 130 | return (hash(key) & 0x7fffffff) % self.M 131 | 132 | def is_empty(self): 133 | return self.N == 0 134 | 135 | def iterate(self): 136 | for i in range(self.M): 137 | x = self.id[i] 138 | if x is not None: 139 | yield x.key 140 | 141 | def size(self): 142 | return self.N 143 | 144 | def contains(self, key): 145 | i = self.hash(key) 146 | for j in range(self.M): 147 | k = (i + j) % self.M 148 | x = self.id[k] 149 | if x is None: 150 | return False 151 | if util.cmp(key, x.key) == 0: 152 | return True 153 | 154 | def delete(self, key): 155 | i = self.hash(key) 156 | for j in range(self.M): 157 | k = (i + j) % self.M 158 | x = self.id[k] 159 | if x is None: 160 | return False 161 | if util.cmp(key, x.key) == 0: 162 | self.id[k] = None 163 | self.N -= 1 164 | if self.N == self.M // 4: 165 | self.resize(self.M // 2) 166 | return True 167 | 168 | def add(self, key): 169 | i = self.hash(key) 170 | for j in range(self.M): 171 | k = (i + j) % self.M 172 | x = self.id[k] 173 | if x is None: 174 | self.id[k] = Node(key) 175 | self.N += 1 176 | if self.N == self.M // 2: 177 | self.resize(self.M * 2) 178 | break 179 | if util.cmp(x.key, key) == 0: 180 | break 181 | 182 | def resize(self, new_size): 183 | clone = HashedSetWithLinearProbing(new_size) 184 | for i in range(self.M): 185 | x = self.id[i] 186 | if x is not None: 187 | clone.add(x.key) 188 | self.M = clone.M 189 | self.id = clone.id 190 | -------------------------------------------------------------------------------- /pyalgs/data_structures/commons/hashed_map.py: -------------------------------------------------------------------------------- 1 | from abc import ABCMeta, abstractmethod 2 | 3 | from pyalgs.algorithms.commons import util 4 | 5 | 6 | class HashedMap(object): 7 | __metaclass__ = ABCMeta 8 | 9 | @abstractmethod 10 | def put(self, key, value): 11 | pass 12 | 13 | @abstractmethod 14 | def get(self, key): 15 | pass 16 | 17 | @abstractmethod 18 | def delete(self, key): 19 | pass 20 | 21 | @abstractmethod 22 | def size(self): 23 | pass 24 | 25 | @abstractmethod 26 | def is_empty(self): 27 | pass 28 | 29 | @abstractmethod 30 | def keys(self): 31 | pass 32 | 33 | @abstractmethod 34 | def contains_key(self, key): 35 | pass 36 | 37 | @staticmethod 38 | def create(): 39 | return HashedMapWithSeparateChaining() 40 | 41 | 42 | class Node(object): 43 | key = None 44 | value = None 45 | next_node = None 46 | 47 | def __init__(self, key=None, value=None): 48 | self.key = key 49 | self.value = value 50 | 51 | 52 | class HashedMapWithSeparateChaining(HashedMap): 53 | M = 97 54 | id = None 55 | N = 0 56 | 57 | def __init__(self, m=None): 58 | if m is None: 59 | m = 97 60 | self.M = m 61 | self.id = [None] * self.M 62 | 63 | def hash(self, key): 64 | return (hash(key) & 0x7fffffff) % self.M 65 | 66 | def is_empty(self): 67 | return self.N == 0 68 | 69 | def contains_key(self, key): 70 | return self.get(key) is not None 71 | 72 | def keys(self): 73 | for i in range(self.M): 74 | x = self.id[i] 75 | while x is not None: 76 | key = x.key 77 | x = x.next_node 78 | yield key 79 | 80 | def size(self): 81 | return self.N 82 | 83 | def get(self, key): 84 | i = self.hash(key) 85 | x = self.id[i] 86 | while x is not None: 87 | if util.cmp(x.key, key) == 0: 88 | return x.value 89 | x = x.next_node 90 | return None 91 | 92 | def delete(self, key): 93 | i = self.hash(key) 94 | x = self.id[i] 95 | prev_node = None 96 | while x is not None: 97 | if util.cmp(x.key, key) == 0: 98 | value = x.value 99 | next_node = x.next_node 100 | self.N -= 1 101 | if prev_node is not None: 102 | prev_node.next_node = next_node 103 | if self.id[i] == x: 104 | self.id[i] = None 105 | return value 106 | prev_node = x 107 | x = x.next_node 108 | return None 109 | 110 | def put(self, key, value): 111 | if key is None: 112 | raise ValueError('key cannot be None') 113 | 114 | i = self.hash(key) 115 | x = self.id[i] 116 | while x is not None: 117 | if util.cmp(x.key, key) == 0: 118 | x.value = value 119 | return 120 | x = x.next_node 121 | old_first = self.id[i] 122 | self.id[i] = Node(key, value) 123 | self.id[i].next_node = old_first 124 | self.N += 1 125 | 126 | 127 | class HashedMapWithLinearProbing(HashedMap): 128 | 129 | M = 97 130 | id = None 131 | N = 0 132 | 133 | def __init__(self, m=None): 134 | if m is None: 135 | m = 97 136 | self.M = m 137 | self.id = [None] * self.M 138 | 139 | def hash(self, key): 140 | return (hash(key) & 0x7fffffff) % self.M 141 | 142 | def contains_key(self, key): 143 | return self.get(key) is not None 144 | 145 | def is_empty(self): 146 | return self.N == 0 147 | 148 | def keys(self): 149 | for i in range(self.M): 150 | x = self.id[i] 151 | if x is not None: 152 | yield x.key 153 | 154 | def size(self): 155 | return self.N 156 | 157 | def get(self, key): 158 | i = self.hash(key) 159 | for j in range(self.M): 160 | k = (i + j) % self.M 161 | x = self.id[k] 162 | if x is None: 163 | return None 164 | if util.cmp(key, x.key) == 0: 165 | return x.value 166 | 167 | def delete(self, key): 168 | i = self.hash(key) 169 | for j in range(self.M): 170 | k = (i + j) % self.M 171 | x = self.id[k] 172 | if x is None: 173 | return None 174 | if util.cmp(key, x.key) == 0: 175 | self.id[k] = None 176 | self.N -= 1 177 | if self.N == self.M // 4: 178 | self.resize(self.M // 2) 179 | return x.value 180 | 181 | def put(self, key, value): 182 | i = self.hash(key) 183 | for j in range(self.M): 184 | k = (i + j) % self.M 185 | x = self.id[k] 186 | if x is None: 187 | self.id[k] = Node(key, value) 188 | self.N += 1 189 | if self.N == self.M // 2: 190 | self.resize(self.M * 2) 191 | break 192 | if util.cmp(x.key, key) == 0: 193 | self.id[k].value = value 194 | break 195 | 196 | def resize(self, new_size): 197 | clone = HashedMapWithLinearProbing(new_size) 198 | for i in range(self.M): 199 | x = self.id[i] 200 | if x is not None: 201 | clone.put(x.key, x.value) 202 | self.M = clone.M 203 | self.id = clone.id 204 | -------------------------------------------------------------------------------- /docs/conf.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # pyalgs documentation build configuration file, created by 4 | # sphinx-quickstart on Wed Apr 26 16:08:20 2017. 5 | # 6 | # This file is execfile()d with the current directory set to its 7 | # containing dir. 8 | # 9 | # Note that not all possible configuration values are present in this 10 | # autogenerated file. 11 | # 12 | # All configuration values have a default; values that are commented out 13 | # serve to show the default. 14 | 15 | # If extensions (or modules to document with autodoc) are in another directory, 16 | # add these directories to sys.path here. If the directory is relative to the 17 | # documentation root, use os.path.abspath to make it absolute, like shown here. 18 | # 19 | # import os 20 | # import sys 21 | # sys.path.insert(0, os.path.abspath('.')) 22 | 23 | 24 | # -- General configuration ------------------------------------------------ 25 | 26 | # If your documentation needs a minimal Sphinx version, state it here. 27 | # 28 | # needs_sphinx = '1.0' 29 | 30 | # Add any Sphinx extension module names here, as strings. They can be 31 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom 32 | # ones. 33 | extensions = ['sphinx.ext.autodoc', 34 | 'sphinx.ext.githubpages'] 35 | 36 | # Add any paths that contain templates here, relative to this directory. 37 | templates_path = ['_templates'] 38 | 39 | # The suffix(es) of source filenames. 40 | # You can specify multiple suffix as a list of string: 41 | # 42 | # source_suffix = ['.rst', '.md'] 43 | source_suffix = '.rst' 44 | 45 | # The master toctree document. 46 | master_doc = 'index' 47 | 48 | # General information about the project. 49 | project = u'pyalgs' 50 | copyright = u'2017, Xianshun Chen' 51 | author = u'Xianshun Chen' 52 | 53 | import re 54 | import ast 55 | 56 | _version_re = re.compile(r'__version__\s+=\s+(.*)') 57 | 58 | # The version info for the project you're documenting, acts as replacement for 59 | # |version| and |release|, also used in various other places throughout the 60 | # built documents. 61 | # 62 | # The short X.Y version. 63 | version = u'0.0.1' 64 | 65 | with open('../pyalgs/__init__.py', 'rb') as f: 66 | version = str(ast.literal_eval(_version_re.search( 67 | f.read().decode('utf-8')).group(1))) 68 | 69 | 70 | # The full version, including alpha/beta/rc tags. 71 | release = version 72 | 73 | # The language for content autogenerated by Sphinx. Refer to documentation 74 | # for a list of supported languages. 75 | # 76 | # This is also used if you do content translation via gettext catalogs. 77 | # Usually you set "language" from the command line for these cases. 78 | language = None 79 | 80 | # List of patterns, relative to source directory, that match files and 81 | # directories to ignore when looking for source files. 82 | # This patterns also effect to html_static_path and html_extra_path 83 | exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] 84 | 85 | # The name of the Pygments (syntax highlighting) style to use. 86 | pygments_style = 'sphinx' 87 | 88 | # If true, `todo` and `todoList` produce output, else they produce nothing. 89 | todo_include_todos = False 90 | 91 | 92 | # -- Options for HTML output ---------------------------------------------- 93 | 94 | # The theme to use for HTML and HTML Help pages. See the documentation for 95 | # a list of builtin themes. 96 | # 97 | html_theme = 'alabaster' 98 | 99 | # Theme options are theme-specific and customize the look and feel of a theme 100 | # further. For a list of options available for each theme, see the 101 | # documentation. 102 | # 103 | # html_theme_options = {} 104 | 105 | # Add any paths that contain custom static files (such as style sheets) here, 106 | # relative to this directory. They are copied after the builtin static files, 107 | # so a file named "default.css" will overwrite the builtin "default.css". 108 | html_static_path = ['_static'] 109 | 110 | 111 | # -- Options for HTMLHelp output ------------------------------------------ 112 | 113 | # Output file base name for HTML help builder. 114 | htmlhelp_basename = 'pyalgsdoc' 115 | 116 | 117 | # -- Options for LaTeX output --------------------------------------------- 118 | 119 | latex_elements = { 120 | # The paper size ('letterpaper' or 'a4paper'). 121 | # 122 | # 'papersize': 'letterpaper', 123 | 124 | # The font size ('10pt', '11pt' or '12pt'). 125 | # 126 | # 'pointsize': '10pt', 127 | 128 | # Additional stuff for the LaTeX preamble. 129 | # 130 | # 'preamble': '', 131 | 132 | # Latex figure (float) alignment 133 | # 134 | # 'figure_align': 'htbp', 135 | } 136 | 137 | # Grouping the document tree into LaTeX files. List of tuples 138 | # (source start file, target name, title, 139 | # author, documentclass [howto, manual, or own class]). 140 | latex_documents = [ 141 | (master_doc, 'pyalgs.tex', u'pyalgs Documentation', 142 | u'Xianshun Chen', 'manual'), 143 | ] 144 | 145 | 146 | # -- Options for manual page output --------------------------------------- 147 | 148 | # One entry per manual page. List of tuples 149 | # (source start file, name, description, authors, manual section). 150 | man_pages = [ 151 | (master_doc, 'pyalgs', u'pyalgs Documentation', 152 | [author], 1) 153 | ] 154 | 155 | 156 | # -- Options for Texinfo output ------------------------------------------- 157 | 158 | # Grouping the document tree into Texinfo files. List of tuples 159 | # (source start file, target name, title, author, 160 | # dir menu entry, description, category) 161 | texinfo_documents = [ 162 | (master_doc, 'pyalgs', u'pyalgs Documentation', 163 | author, 'pyalgs', 'One line description of project.', 164 | 'Miscellaneous'), 165 | ] 166 | 167 | 168 | 169 | -------------------------------------------------------------------------------- /pyalgs/algorithms/graphs/shortest_path.py: -------------------------------------------------------------------------------- 1 | from abc import ABCMeta, abstractmethod 2 | 3 | from pyalgs.algorithms.commons import util 4 | from pyalgs.algorithms.graphs.topological_sort import DepthFirstOrder 5 | from pyalgs.data_structures.commons.priority_queue import IndexMinPQ 6 | from pyalgs.data_structures.commons.stack import Stack 7 | from pyalgs.data_structures.graphs.graph import Digraph, Graph, EdgeWeightedGraph 8 | 9 | 10 | class ShortestPath(object): 11 | __metaclass__ = ABCMeta 12 | 13 | @abstractmethod 14 | def shortestPathTo(self, v): 15 | pass 16 | 17 | @abstractmethod 18 | def hasPathTo(self, v): 19 | pass 20 | 21 | @abstractmethod 22 | def path_length_to(self, v): 23 | pass 24 | 25 | 26 | class DijkstraShortestPath(ShortestPath): 27 | edgeTo = None 28 | s = 0 29 | pq = None 30 | cost = None 31 | 32 | def __init__(self, G, s): 33 | if isinstance(G, Graph): 34 | raise ValueError('Graph must be edge weighted') 35 | if isinstance(G, Digraph): 36 | raise ValueError('Graph must be edge weighted') 37 | if isinstance(G, EdgeWeightedGraph): 38 | G = G.to_edge_weighted_digraph() 39 | 40 | vertex_count = G.vertex_count() 41 | self.pq = IndexMinPQ(vertex_count) 42 | self.s = s 43 | 44 | self.marked = [False] * vertex_count 45 | self.edgeTo = [None] * vertex_count 46 | self.cost = [float("inf")] * vertex_count 47 | 48 | self.cost[s] = 0 49 | self.pq.insert(s, 0.0) 50 | 51 | while not self.pq.is_empty(): 52 | v = self.pq.del_min() 53 | self.marked[v] = True 54 | for e in G.adj(v): 55 | self.relax(e) 56 | 57 | def relax(self, e): 58 | v = e.start() 59 | w = e.end() 60 | if self.cost[w] > self.cost[v] + e.weight: 61 | self.cost[w] = self.cost[v] + e.weight 62 | self.edgeTo[w] = e 63 | if self.pq.contains_index(w): 64 | self.pq.decrease_key(w, self.cost[w]) 65 | else: 66 | self.pq.insert(w, self.cost[w]) 67 | 68 | def hasPathTo(self, v): 69 | return self.marked[v] 70 | 71 | def shortestPathTo(self, v): 72 | path = Stack.create() 73 | x = v 74 | while x != self.s: 75 | path.push(self.edgeTo[x]) 76 | x = self.edgeTo[x].start() 77 | 78 | return path.iterate() 79 | 80 | def path_length_to(self, v): 81 | return self.cost[v] 82 | 83 | 84 | class TopologicalSortShortestPath(ShortestPath): 85 | marked = None 86 | edgeTo = None 87 | cost = None 88 | s = 0 89 | 90 | def __init__(self, G, s): 91 | vertex_count = G.vertex_count() 92 | self.marked = [False] * vertex_count 93 | self.edgeTo = [None] * vertex_count 94 | self.cost = [float('inf')] * vertex_count 95 | self.s = s 96 | 97 | dfo = DepthFirstOrder(G) 98 | orders = dfo.postOrder() 99 | 100 | self.cost[s] = 0 101 | 102 | for v in orders: 103 | for e in G.adj(v): 104 | self.relax(e) 105 | 106 | def relax(self, e): 107 | v = e.start() 108 | w = e.end() 109 | if self.cost[w] > self.cost[v] + e.weight: 110 | self.cost[w] = self.cost[v] + e.weight 111 | self.edgeTo[w] = e 112 | self.marked[w] = True 113 | 114 | def shortestPathTo(self, v): 115 | path = Stack.create() 116 | x = v 117 | while x != self.s: 118 | path.push(self.edgeTo[x]) 119 | x = self.edgeTo[x].start() 120 | 121 | return path.iterate() 122 | 123 | def hasPathTo(self, v): 124 | return self.marked[v] 125 | 126 | def path_length_to(self, v): 127 | return self.cost[v] 128 | 129 | 130 | class BellmanFordShortestPath(ShortestPath): 131 | 132 | s = None 133 | edgeTo = None 134 | cost = None 135 | negativeDirectedCycle = False 136 | vertexCount = 0 137 | 138 | def __init__(self, G, s): 139 | self.vertexCount = G.vertex_count() 140 | self.cost = [float('inf')] * self.vertexCount 141 | self.edgeTo = [None] * self.vertexCount 142 | 143 | self.cost[s] = 0 144 | self.s = s 145 | 146 | for i in range(self.vertexCount): 147 | for v in range(self.vertexCount): 148 | for e in G.adj(v): 149 | self.relax(e) 150 | 151 | for v in range(self.vertexCount): 152 | for e in G.adj(v): 153 | if self.relax(e): 154 | self.negativeDirectedCycle = True 155 | 156 | def relax(self, e): 157 | v = e.start() 158 | w = e.end() 159 | 160 | if self.cost[w] > self.cost[v] + e.weight: 161 | self.cost[w] = self.cost[v] + e.weight 162 | self.edgeTo[w] = e 163 | return True 164 | return False 165 | 166 | def hasNegativeDirectedCycle(self): 167 | return self.negativeDirectedCycle 168 | 169 | def shortestPathTo(self, v): 170 | path = Stack.create() 171 | x = v 172 | counter = 0 173 | while x != self.s: 174 | path.push(self.edgeTo[x]) 175 | if self.edgeTo[x] is None: 176 | return None 177 | x = self.edgeTo[x].start() 178 | counter += 1 179 | if counter > self.vertexCount: 180 | return None 181 | 182 | return path.iterate() 183 | 184 | def hasPathTo(self, v): 185 | return self.shortestPathTo(v) is not None 186 | 187 | def path_length_to(self, v): 188 | return self.cost[v] 189 | -------------------------------------------------------------------------------- /pyalgs/algorithms/commons/sorting.py: -------------------------------------------------------------------------------- 1 | import pyalgs.algorithms.commons.util as util 2 | 3 | class SelectionSort(object): 4 | """ Order of Growth: N^2 5 | """ 6 | 7 | @staticmethod 8 | def sort(a): 9 | N = len(a) 10 | for i in range(N): 11 | k = i 12 | for j in range(i + 1, N): 13 | if util.less(a[j], a[k]): 14 | k = j 15 | util.exchange(a, i, k) 16 | 17 | 18 | class InsertionSort(object): 19 | @staticmethod 20 | def sort(a, lo=None, hi=None): 21 | if lo is None: 22 | lo = 0 23 | if hi is None: 24 | hi = len(a) - 1 25 | 26 | for i in range(lo, hi + 1): 27 | for j in reversed(range(1, i + 1)): 28 | if util.less(a[j], a[j - 1]): 29 | util.exchange(a, j, j - 1) 30 | else: 31 | break 32 | 33 | 34 | class ShellSort(object): 35 | @staticmethod 36 | def sort(a): 37 | N = len(a) 38 | H = 0 39 | while H < N // 3: 40 | H = 3 * H + 1 41 | 42 | for h in reversed(range(1, H)): 43 | for i in range(h, N): 44 | for j in range(i, h - 1, -h): 45 | if util.less(a[j], a[j - h]): 46 | util.exchange(a, j, j - h) 47 | else: 48 | break 49 | 50 | 51 | class MergeSort(object): 52 | CUTOFF = 7 53 | 54 | @staticmethod 55 | def sort(a, lo=None, hi=None): 56 | if lo is None: 57 | lo = 0 58 | if hi is None: 59 | hi = len(a) - 1 60 | 61 | aux = [0] * len(a) 62 | MergeSort._sort(a, aux, lo, hi) 63 | 64 | @staticmethod 65 | def _sort(a, aux, lo, hi): 66 | if lo >= hi: 67 | return 68 | 69 | if hi - lo + 1 < MergeSort.CUTOFF: 70 | InsertionSort.sort(a, lo, hi) 71 | return 72 | 73 | mid = lo + (hi - lo) // 2 74 | MergeSort._sort(a, aux, lo, mid) 75 | MergeSort._sort(a, aux, mid + 1, hi) 76 | MergeSort._merge(a, aux, lo, mid, hi) 77 | 78 | @staticmethod 79 | def _merge(a, aux, lo, mid, hi): 80 | i = lo 81 | j = mid + 1 82 | 83 | for k in range(lo, hi + 1): 84 | aux[k] = a[k] 85 | 86 | for k in range(lo, hi + 1): 87 | if i > mid: 88 | a[k] = aux[j] 89 | j += 1 90 | elif j > hi: 91 | a[k] = aux[i] 92 | i += 1 93 | elif util.less(aux[i], aux[j]): 94 | a[k] = aux[i] 95 | i += 1 96 | else: 97 | a[k] = aux[j] 98 | j += 1 99 | 100 | 101 | class QuickSort(object): 102 | CUTOFF = 7 103 | 104 | @staticmethod 105 | def sort(a, lo=None, hi=None): 106 | if lo is None: 107 | lo = 0 108 | if hi is None: 109 | hi = len(a) - 1 110 | 111 | if lo >= hi: 112 | return 113 | 114 | if hi - lo + 1 < QuickSort.CUTOFF: 115 | InsertionSort.sort(a, lo, hi) 116 | return 117 | 118 | j = QuickSort.partition(a, lo, hi) 119 | 120 | QuickSort.sort(a, lo, j - 1) 121 | QuickSort.sort(a, j + 1, hi) 122 | 123 | @staticmethod 124 | def partition(a, lo, hi): 125 | i = lo 126 | j = hi 127 | 128 | while True: 129 | while not util.less(a[lo], a[i]): 130 | i += 1 131 | if i >= hi: 132 | break 133 | 134 | while util.less(a[lo], a[j]): 135 | j -= 1 136 | if j <= lo: 137 | break 138 | 139 | if i >= j: 140 | break 141 | 142 | util.exchange(a, i, j) 143 | 144 | util.exchange(a, lo, j) 145 | return j 146 | 147 | 148 | class ThreeWayQuickSort(object): 149 | CUTOFF = 7 150 | 151 | @staticmethod 152 | def sort(a, lo=None, hi=None): 153 | if lo is None: 154 | lo = 0 155 | if hi is None: 156 | hi = len(a) - 1 157 | 158 | if lo >= hi: 159 | return 160 | 161 | if hi - lo + 1 < ThreeWayQuickSort.CUTOFF: 162 | InsertionSort.sort(a, lo, hi) 163 | return 164 | 165 | i = lo 166 | lt = lo 167 | gt = hi 168 | 169 | while i <= gt: 170 | if util.less(a[i], a[lo]): 171 | util.exchange(a, i, lt) 172 | i += 1 173 | lt += 1 174 | elif util.less(a[lo], a[i]): 175 | util.exchange(a, i, gt) 176 | gt -= 1 177 | else: 178 | i += 1 179 | 180 | ThreeWayQuickSort.sort(a, lo, lt - 1) 181 | ThreeWayQuickSort.sort(a, gt + 1, hi) 182 | 183 | 184 | class HeapSort(object): 185 | @staticmethod 186 | def sort(a): 187 | n = len(a) 188 | 189 | print(a) 190 | for k in range(n // 2, 0, -1): 191 | HeapSort.sink(a, k, n) 192 | 193 | while n > 1: 194 | util.exchange(a, HeapSort.index(1), HeapSort.index(n)) 195 | n -= 1 196 | HeapSort.sink(a, 1, n) 197 | 198 | @staticmethod 199 | def sink(a, k, n): 200 | while k * 2 <= n: 201 | child = 2 * k 202 | if child < n and util.greater(a[HeapSort.index(child+1)], a[HeapSort.index(child)]): 203 | child = child + 1 204 | if util.greater(a[HeapSort.index(child)], a[HeapSort.index(k)]): 205 | util.exchange(a, HeapSort.index(child), HeapSort.index(k)) 206 | k = child 207 | else: 208 | break 209 | 210 | @staticmethod 211 | def index(k): 212 | return k-1 213 | -------------------------------------------------------------------------------- /pyalgs/data_structures/strings/search_tries.py: -------------------------------------------------------------------------------- 1 | from abc import ABCMeta, abstractmethod 2 | 3 | from pyalgs.algorithms.commons import util 4 | from pyalgs.data_structures.commons.queue import Queue 5 | 6 | 7 | class SearchTries(object): 8 | __metaclass__ = ABCMeta 9 | 10 | @abstractmethod 11 | def put(self, key, value): 12 | pass 13 | 14 | @abstractmethod 15 | def get(self, key): 16 | pass 17 | 18 | @abstractmethod 19 | def is_empty(self): 20 | pass 21 | 22 | @abstractmethod 23 | def size(self): 24 | pass 25 | 26 | @abstractmethod 27 | def delete(self, key): 28 | pass 29 | 30 | @abstractmethod 31 | def keys(self): 32 | pass 33 | 34 | @abstractmethod 35 | def keys_with_prefix(self, prefix): 36 | pass 37 | 38 | @abstractmethod 39 | def contains_key(self, key): 40 | pass 41 | 42 | @staticmethod 43 | def create(): 44 | return RWaySearchTries() 45 | 46 | 47 | def char_at(key, d): 48 | return ord(key[d]) 49 | 50 | 51 | class RwayNode(object): 52 | nodes = None 53 | value = None 54 | R = 256 55 | 56 | def __init__(self): 57 | self.nodes = [None] * RwayNode.R 58 | 59 | 60 | class RWaySearchTries(SearchTries): 61 | 62 | root = None 63 | N = 0 64 | 65 | def contains_key(self, key): 66 | x = self._get(self.root, key, 0) 67 | if x is None: 68 | return None 69 | return x.value 70 | 71 | def keys_with_prefix(self, prefix): 72 | x = self._get(self.root, prefix, 0) 73 | if x is None: 74 | return None 75 | 76 | queue = Queue.create() 77 | self.collect(x, prefix, queue) 78 | return queue.iterate() 79 | 80 | def keys(self): 81 | queue = Queue.create() 82 | self.collect(self.root, '', queue) 83 | return queue.iterate() 84 | 85 | def collect(self, x, prefix, queue): 86 | if x is None: 87 | return 88 | 89 | if x.value is not None: 90 | queue.enqueue(prefix) 91 | return 92 | 93 | for r in range(RwayNode.R): 94 | self.collect(x.nodes[r], prefix + str(chr(r)), queue) 95 | 96 | def is_empty(self): 97 | return self.N == 0 98 | 99 | def get(self, key): 100 | x = self._get(self.root, key, 0) 101 | if x is not None: 102 | return x.value 103 | return None 104 | 105 | def _get(self, x, key, d): 106 | if x is None: 107 | return None 108 | if len(key) == d: 109 | return x 110 | 111 | return self._get(x.nodes[char_at(key, d)], key, d + 1) 112 | 113 | def delete(self, key): 114 | self.root = self._delete(self.root, key, 0) 115 | 116 | def _delete(self, x, key, d): 117 | if x is None: 118 | return None 119 | 120 | if len(key) == d: 121 | if x.value is not None: 122 | self.N -= 1 123 | return None 124 | 125 | c = char_at(key, d) 126 | x.nodes[c] = self._delete(x.nodes[c], key, d + 1) 127 | return x 128 | 129 | def put(self, key, value): 130 | self.root = self._put(self.root, key, value, 0) 131 | 132 | def _put(self, x, key, value, d): 133 | if x is None: 134 | x = RwayNode() 135 | if len(key) == d: 136 | if x.value is None: 137 | self.N += 1 138 | x.value = value 139 | return x 140 | 141 | c = char_at(key, d) 142 | x.nodes[c] = self._put(x.nodes[c], key, value, d + 1) 143 | return x 144 | 145 | def size(self): 146 | return self.N 147 | 148 | 149 | class TSTNode(object): 150 | value = None 151 | left = None 152 | mid = None 153 | right = None 154 | key = None 155 | 156 | def __init__(self, c): 157 | self.key = c 158 | 159 | 160 | class TernarySearchTries(SearchTries): 161 | 162 | root = None 163 | N = 0 164 | 165 | def contains_key(self, key): 166 | x = self._get(self.root, key, 0) 167 | return x is not None and x.value is not None 168 | 169 | def size(self): 170 | return self.N 171 | 172 | def keys(self): 173 | queue = Queue.create() 174 | self.collect(self.root, '', queue) 175 | return queue.iterate() 176 | 177 | def collect(self, x, prefix, queue): 178 | if x is None: 179 | return 180 | if x.value is not None: 181 | queue.enqueue(prefix) 182 | c = chr(x.key) 183 | self.collect(x.left, prefix, queue) 184 | self.collect(x.mid, prefix + str(c), queue) 185 | self.collect(x.right, prefix, queue) 186 | 187 | def get(self, key): 188 | x = self._get(self.root, key, 0) 189 | if x is None: 190 | return None 191 | return x.value 192 | 193 | def _get(self, x, key, d): 194 | if x is None: 195 | return None 196 | c = char_at(key, d) 197 | compared = util.cmp(c, x.key) 198 | if compared < 0: 199 | return self._get(x.left, key, d) 200 | elif compared > 0: 201 | return self._get(x.right, key, d) 202 | elif len(key)-1 == d: 203 | return x 204 | else: 205 | return self._get(x.mid, key, d+1) 206 | 207 | def put(self, key, value): 208 | self.root = self._put(self.root, key, value, 0) 209 | 210 | def _put(self, x, key, value, d): 211 | c = char_at(key, d) 212 | if x is None: 213 | x = TSTNode(c) 214 | compared = util.cmp(c, x.key) 215 | if compared < 0: 216 | x.left = self._put(x.left, key, value, d) 217 | elif compared > 0: 218 | x.right = self._put(x.right, key, value, d) 219 | elif len(key)-1 == d: 220 | if x.value is None: 221 | self.N += 1 222 | x.value = value 223 | else: 224 | x.mid = self._put(x.mid, key, value, d+1) 225 | 226 | return x 227 | 228 | def delete(self, key): 229 | x = self._get(self.root, key, 0) 230 | if x is not None: 231 | if x.value is not None: 232 | self.N -= 1 233 | x.value = None 234 | 235 | def is_empty(self): 236 | return self.N == 0 237 | 238 | def keys_with_prefix(self, prefix): 239 | x = self._get(self.root, prefix, 0) 240 | if x is None: 241 | return None 242 | queue = Queue.create() 243 | self.collect(x, prefix, queue) 244 | return queue.iterate() -------------------------------------------------------------------------------- /pyalgs/data_structures/commons/binary_search_tree.py: -------------------------------------------------------------------------------- 1 | from abc import ABCMeta 2 | from pyalgs.algorithms.commons.util import cmp 3 | from pyalgs.data_structures.commons.queue import Queue 4 | 5 | 6 | class Node(object): 7 | key = None 8 | value = None 9 | 10 | left = None 11 | right = None 12 | 13 | count = 0 14 | red = 1 15 | 16 | def __init__(self, key, value, red=None): 17 | self.key = key 18 | self.value = value 19 | self.count = 1 20 | 21 | if red is not None: 22 | self.red = red 23 | else: 24 | self.red = 0 25 | 26 | 27 | def _count(x): 28 | if x is None: 29 | return 0 30 | return x.count 31 | 32 | 33 | class BinarySearchTree(object): 34 | __metaclass__ = ABCMeta 35 | 36 | root = None 37 | 38 | def put(self, key, value): 39 | self.root = self._put(self.root, key, value) 40 | 41 | def _put(self, x, key, value): 42 | if x is None: 43 | return Node(key, value) 44 | 45 | compared = cmp(key, x.key) 46 | 47 | if compared < 0: 48 | x.left = self._put(x.left, key, value) 49 | elif compared > 0: 50 | x.right = self._put(x.right, key, value) 51 | else: 52 | x.value = value 53 | 54 | x.count = 1 + _count(x.left) + _count(x.right) 55 | 56 | return x 57 | 58 | def get(self, key): 59 | x = self._get(self.root, key) 60 | if x is None: 61 | return None 62 | return x.value 63 | 64 | def _get(self, x, key): 65 | if x is None: 66 | return None 67 | compared = cmp(key, x.key) 68 | if compared < 0: 69 | return self._get(x.left, key) 70 | elif compared > 0: 71 | return self._get(x.right, key) 72 | else: 73 | return x 74 | 75 | def size(self): 76 | return _count(self.root) 77 | 78 | def is_empty(self): 79 | return self.root is None 80 | 81 | def delete(self, key): 82 | self.root = self._delete(self.root, key) 83 | 84 | def _delete(self, x, key): 85 | if x is None: 86 | return None 87 | 88 | compared = cmp(key, x.key) 89 | if compared < 0: 90 | x.left = self._delete(x.left, key) 91 | elif compared > 0: 92 | x.right = self._delete(x.right, key) 93 | else: 94 | if x.left is None: 95 | return x.right 96 | elif x.right is None: 97 | return x.left 98 | else: 99 | m = self.min(x.right) 100 | m.right = self.del_min(x.right) 101 | m.left = x.left 102 | 103 | x = m 104 | 105 | x.count = 1 + _count(x.left) + _count(x.right) 106 | return x 107 | 108 | def min(self, x): 109 | if x.left is None: 110 | return x 111 | return self.min(x.left) 112 | 113 | def del_min(self, x): 114 | if x.left is None: 115 | return x.right 116 | x.left = self.del_min(x.left) 117 | return x 118 | 119 | def contains_key(self, x): 120 | return self.get(x) is not None 121 | 122 | def keys(self): 123 | queue = Queue.create() 124 | self.collect_keys(self.root, queue) 125 | return queue.iterate() 126 | 127 | def collect_keys(self, x, queue): 128 | if x is None: 129 | return 130 | 131 | self.collect_keys(x.left, queue) 132 | queue.enqueue(x.key) 133 | self.collect_keys(x.right, queue) 134 | 135 | @staticmethod 136 | def create(): 137 | return BinarySearchTree() 138 | 139 | @staticmethod 140 | def create_red_black_tree(): 141 | return RedBlackTree() 142 | 143 | 144 | class RedBlackTree(BinarySearchTree): 145 | def put(self, key, value): 146 | self.root = self._put2(self.root, key, value) 147 | 148 | def _put2(self, x, key, value): 149 | if x is None: 150 | return Node(key, value, 1) 151 | compared = cmp(key, x.key) 152 | if compared < 0: 153 | x.left = self._put2(x.left, key, value) 154 | elif compared > 0: 155 | x.right = self._put2(x.right, key, value) 156 | else: 157 | x.value = value 158 | 159 | if self.is_red(x.right) and not self.is_red(x.left): 160 | x = self.rotate_left(x) 161 | if self.is_red(x.left) and self.is_red(x.left.left): 162 | x = self.rotate_right(x) 163 | if self.is_red(x.left) and self.is_red(x.right): 164 | x = self.flip_colors(x) 165 | 166 | x.count = 1 + _count(x.left) + _count(x.right) 167 | return x 168 | 169 | def _delete(self, x, key): 170 | if x is None: 171 | return None 172 | 173 | compared = cmp(key, x.key) 174 | if compared < 0: 175 | x.left = self._delete(x.left, key) 176 | elif compared > 0: 177 | x.right = self._delete(x.right, key) 178 | else: 179 | if x.left is None: 180 | return x.right 181 | elif x.right is None: 182 | return x.left 183 | else: 184 | m = self.min(x.right) 185 | m.right = self.del_min(x.right) 186 | m.left = x.left 187 | 188 | x = m 189 | 190 | if self.is_red(x.right) and not self.is_red(x.left): 191 | x = self.rotate_left(x) 192 | if self.is_red(x.left) and self.is_red(x.left.left): 193 | x = self.rotate_right(x) 194 | if self.is_red(x.left) and self.is_red(x.right): 195 | x = self.flip_colors(x) 196 | 197 | x.count = 1 + _count(x.left) + _count(x.right) 198 | return x 199 | 200 | def is_red(self, x): 201 | if x is None: 202 | return False 203 | return x.red == 1 204 | 205 | def rotate_left(self, x): 206 | m = x.right 207 | x.right = m.left 208 | m.left = x 209 | 210 | m.red = x.red 211 | x.red = 1 212 | 213 | x.count = 1 + _count(x.left) + _count(x.right) 214 | return m 215 | 216 | def rotate_right(self, x): 217 | m = x.left 218 | x.left = m.right 219 | m.right = x 220 | 221 | m.red = x.red 222 | x.red = 1 223 | 224 | x.count = 1 + _count(x.left) + _count(x.right) 225 | return m 226 | 227 | def flip_colors(self, x): 228 | if x.left is not None: 229 | x.left.red = 0 230 | if x.right is not None: 231 | x.right.red = 0 232 | x.red = 1 233 | return x 234 | 235 | -------------------------------------------------------------------------------- /pyalgs/data_structures/commons/priority_queue.py: -------------------------------------------------------------------------------- 1 | from pyalgs.algorithms.commons.util import less, exchange, greater 2 | 3 | 4 | class MinPQ(object): 5 | pq = None 6 | N = 0 7 | 8 | def __init__(self, capacity=None): 9 | if capacity is None: 10 | capacity = 10 11 | self.pq = [0] * capacity 12 | 13 | def enqueue(self, key): 14 | self.N += 1 15 | if self.N == len(self.pq): 16 | self.resize(len(self.pq) * 2) 17 | 18 | self.pq[self.N] = key 19 | self.swim(self.N) 20 | 21 | def swim(self, k): 22 | while k > 1: 23 | parent = k // 2 24 | if less(self.pq[k], self.pq[parent]): 25 | exchange(self.pq, k, parent) 26 | k = parent 27 | else: 28 | break 29 | 30 | def del_min(self): 31 | if self.is_empty(): 32 | return None 33 | 34 | key = self.pq[1] 35 | exchange(self.pq, 1, self.N) 36 | self.N -= 1 37 | if self.N == len(self.pq) // 4: 38 | self.resize(len(self.pq) // 2) 39 | 40 | self.sink(self.pq, 1, self.N) 41 | return key 42 | 43 | @staticmethod 44 | def sink(tmp, k, n): 45 | 46 | while k * 2 <= n: 47 | child = k * 2 48 | if child < n and less(tmp[child + 1], tmp[child]): 49 | child = child + 1 50 | if less(tmp[child], tmp[k]): 51 | exchange(tmp, child, k) 52 | k = child 53 | else: 54 | break 55 | 56 | def resize(self, new_size): 57 | tmp = [0] * new_size 58 | for k in range(min(new_size, len(self.pq))): 59 | tmp[k] = self.pq[k] 60 | self.pq = tmp 61 | 62 | def is_empty(self): 63 | return self.N == 0 64 | 65 | def size(self): 66 | return self.N 67 | 68 | def iterate(self): 69 | tmp = [0] * (self.N + 1) 70 | for k in range(self.N + 1): 71 | tmp[k] = self.pq[k] 72 | 73 | n = self.N 74 | while n >= 1: 75 | key = tmp[1] 76 | exchange(tmp, 1, n) 77 | n -= 1 78 | self.sink(tmp, 1, n) 79 | yield key 80 | 81 | @staticmethod 82 | def create(): 83 | return MinPQ() 84 | 85 | 86 | class MaxPQ(object): 87 | pq = None 88 | N = 0 89 | 90 | def __init__(self, capacity=None): 91 | if capacity is None: 92 | capacity = 10 93 | self.pq = [0] * capacity 94 | 95 | def enqueue(self, key): 96 | if self.N == len(self.pq): 97 | self.resize(len(self.pq) * 2) 98 | self.N += 1 99 | self.pq[self.N] = key 100 | self.swim(self.N) 101 | 102 | def swim(self, k): 103 | while k > 1: 104 | parent = k // 2 105 | if greater(self.pq[k], self.pq[parent]): 106 | exchange(self.pq, k, parent) 107 | k = parent 108 | else: 109 | break 110 | 111 | def del_max(self): 112 | if self.is_empty(): 113 | return None 114 | 115 | key = self.pq[1] 116 | exchange(self.pq, 1, self.N) 117 | self.N -= 1 118 | if self.N == len(self.pq) // 4: 119 | self.resize(len(self.pq) // 2) 120 | 121 | self.sink(self.pq, 1, self.N) 122 | return key 123 | 124 | @staticmethod 125 | def sink(tmp, k, n): 126 | 127 | while k * 2 <= n: 128 | child = k * 2 129 | if child < n and greater(tmp[child + 1], tmp[child]): 130 | child = child + 1 131 | if greater(tmp[child], tmp[k]): 132 | exchange(tmp, child, k) 133 | k = child 134 | else: 135 | break 136 | 137 | def resize(self, new_size): 138 | tmp = [0] * new_size 139 | for k in range(min(new_size, len(self.pq))): 140 | tmp[k] = self.pq[k] 141 | self.pq = tmp 142 | 143 | def is_empty(self): 144 | return self.N == 0 145 | 146 | def size(self): 147 | return self.N 148 | 149 | def iterate(self): 150 | tmp = [0] * (self.N + 1) 151 | for k in range(self.N + 1): 152 | tmp[k] = self.pq[k] 153 | 154 | n = self.N 155 | while n >= 1: 156 | key = tmp[1] 157 | exchange(tmp, 1, n) 158 | n -= 1 159 | self.sink(tmp, 1, n) 160 | yield key 161 | 162 | @staticmethod 163 | def create(): 164 | return MaxPQ() 165 | 166 | 167 | class IndexMinPQ(object): 168 | 169 | keys = None 170 | pq = None 171 | qp = None 172 | N = 0 173 | 174 | def __init__(self, size): 175 | self.keys = [None] * (size+1) 176 | self.pq = [0] * (size+1) 177 | self.qp = [0] * (size+1) 178 | 179 | def insert(self, index, key): 180 | self.keys[index] = key 181 | self.N += 1 182 | self.pq[self.N] = index 183 | self.qp[index] = self.N 184 | 185 | self.swim(self.N) 186 | 187 | def decrease_key(self, index, key): 188 | self.keys[index] = key 189 | k = self.qp[index] 190 | self.swim(k) 191 | 192 | def contains_index(self, index): 193 | return self.keys[index] is not None 194 | 195 | def swim(self, k): 196 | while k > 1: 197 | parent = k // 2 198 | if less(self.keys[self.pq[k]], self.keys[self.pq[parent]]): 199 | exchange(self.pq, k, parent) 200 | self.qp[self.pq[k]] = k 201 | self.qp[self.pq[parent]] = parent 202 | k = parent 203 | else: 204 | break 205 | 206 | def get(self, index): 207 | return self.keys[index] 208 | 209 | def min_key(self): 210 | return self.keys[self.pq[1]] 211 | 212 | def del_min(self): 213 | index = self.pq[1] 214 | exchange(self.pq, 1, self.N) 215 | self.qp[self.pq[1]] = 1 216 | self.qp[self.pq[self.N]] = self.N 217 | 218 | self.N -= 1 219 | self.sink(1) 220 | 221 | return index 222 | 223 | def sink(self, k): 224 | while 2 * k <= self.N: 225 | child = 2 * k 226 | if child < self.N and less(self.keys[self.pq[child]], self.keys[self.pq[child+1]]): 227 | child = child+1 228 | if less(self.keys[self.pq[child]], self.keys[self.pq[k]]): 229 | exchange(self.pq, k, child) 230 | self.qp[self.pq[k]] = k 231 | self.qp[self.pq[child]] = child 232 | k = child 233 | else: 234 | break 235 | 236 | def is_empty(self): 237 | return self.N == 0 238 | 239 | def size(self): 240 | return self.N 241 | -------------------------------------------------------------------------------- /pyalgs/data_structures/graphs/graph.py: -------------------------------------------------------------------------------- 1 | from pyalgs.algorithms.commons import util 2 | from pyalgs.data_structures.commons.bag import Bag 3 | 4 | 5 | class Graph(object): 6 | V = 0 7 | adjList = None 8 | 9 | def __init__(self, V): 10 | self.V = V 11 | self.adjList = [None] * V 12 | for v in range(V): 13 | self.adjList[v] = Bag() 14 | 15 | def vertex_count(self): 16 | return self.V 17 | 18 | def adj(self, v): 19 | return self.adjList[v].iterate() 20 | 21 | def add_edge(self, v, w): 22 | self.adjList[v].add(w) 23 | self.adjList[w].add(v) 24 | 25 | 26 | class Digraph(object): 27 | V = 0 28 | adjList = None 29 | 30 | def __init__(self, V): 31 | self.V = V 32 | self.adjList = [None] * V 33 | for v in range(V): 34 | self.adjList[v] = Bag() 35 | 36 | def vertex_count(self): 37 | return self.V 38 | 39 | def adj(self, v): 40 | return self.adjList[v].iterate() 41 | 42 | def add_edge(self, v, w): 43 | self.adjList[v].add(w) 44 | 45 | def reverse(self): 46 | g = Digraph(self.V) 47 | for v in range(self.V): 48 | for w in self.adjList[v].iterate(): 49 | g.add_edge(w, v) 50 | 51 | return g 52 | 53 | 54 | class Edge(object): 55 | v = None 56 | w = None 57 | weight = 0 58 | 59 | def __init__(self, v=None, w=None, weight=None): 60 | if weight is None: 61 | weight = 0 62 | self.v = v 63 | self.w = w 64 | self.weight = weight 65 | 66 | def start(self): 67 | return self.v 68 | 69 | def end(self): 70 | return self.w 71 | 72 | def reverse(self): 73 | return Edge(self.w, self.v, self.weight) 74 | 75 | def either(self): 76 | return self.v 77 | 78 | def other(self, v): 79 | if self.v == v: 80 | return self.w 81 | elif self.w == v: 82 | return self.v 83 | else: 84 | raise ValueError('mismatched vertex detected') 85 | 86 | # use for python 2 comparison 87 | def __cmp__(self, other): 88 | return util.cmp(self.weight, other.weight) 89 | 90 | # user for python 3 comparison 91 | def __lt__(self, other): 92 | return util.less(self.weight, other.weight) 93 | 94 | # user for python 3 comparison 95 | def __gt__(self, other): 96 | return util.greater(self.weight, other.weight) 97 | 98 | def __str__(self): 99 | return str(self.v) + ' =(' + str(self.weight) + ')=> ' + str(self.w) 100 | 101 | 102 | class EdgeWeightedGraph(object): 103 | adjList = None 104 | V = 0 105 | 106 | def __init__(self, vertex_count): 107 | self.V = vertex_count 108 | self.adjList = [None] * vertex_count 109 | for v in range(vertex_count): 110 | self.adjList[v] = Bag() 111 | 112 | def add_edge(self, edge): 113 | v = edge.either() 114 | w = edge.other(v) 115 | self.adjList[v].add(edge) 116 | self.adjList[w].add(edge) 117 | 118 | def adj(self, v): 119 | return self.adjList[v].iterate() 120 | 121 | def vertex_count(self): 122 | return self.V 123 | 124 | def edges(self): 125 | for v in range(self.V): 126 | for e in self.adj(v): 127 | if e.either() == v: 128 | yield e 129 | 130 | def to_graph(self): 131 | g = Graph() 132 | 133 | for e in self.edges(): 134 | g.add_edge(e.start(), e.end()) 135 | return g 136 | 137 | def to_edge_weighted_digraph(self): 138 | g = DirectedEdgeWeightedGraph(self.V) 139 | 140 | for e in self.edges(): 141 | g.add_edge(e) 142 | g.add_edge(e.reverse()) 143 | return g 144 | 145 | 146 | class DirectedEdgeWeightedGraph(object): 147 | adjList = None 148 | V = 0 149 | 150 | def __init__(self, vertex_count): 151 | self.V = vertex_count 152 | self.adjList = [None] * vertex_count 153 | for v in range(vertex_count): 154 | self.adjList[v] = Bag() 155 | 156 | def add_edge(self, edge): 157 | v = edge.start() 158 | self.adjList[v].add(edge) 159 | 160 | def adj(self, v): 161 | return self.adjList[v].iterate() 162 | 163 | def vertex_count(self): 164 | return self.V 165 | 166 | def edges(self): 167 | for v in range(self.V): 168 | for e in self.adj(v): 169 | yield e 170 | 171 | def to_graph(self): 172 | g = Graph() 173 | 174 | for e in self.edges(): 175 | g.add_edge(e.start(), e.end()) 176 | return g 177 | 178 | def to_digraph(self): 179 | g = Digraph(self.V) 180 | 181 | for e in self.edges(): 182 | g.add_edge(e.start(), e.end()) 183 | 184 | return g 185 | 186 | 187 | class FlowEdge(object): 188 | v = 0 189 | w = 0 190 | capacity = 0 191 | flow = 0 192 | 193 | def __init__(self, v, w, capacity): 194 | self.v = v 195 | self.w = w 196 | self.capacity = capacity 197 | 198 | def residual_capacity_to(self, end_v): 199 | if self.w == end_v: 200 | return self.capacity - self.flow 201 | elif self.v == end_v: 202 | return self.flow 203 | else: 204 | raise ValueError('end point not belong to flow edge') 205 | 206 | def add_residual_flow_to(self, end_v, delta): 207 | if self.v == end_v: 208 | self.flow -= delta 209 | elif self.w == end_v: 210 | self.flow += delta 211 | else: 212 | raise ValueError('end point not belong to flow edge') 213 | 214 | def start(self): 215 | return self.v 216 | 217 | def end(self): 218 | return self.w 219 | 220 | def other(self, x): 221 | if x == self.v: 222 | return self.w 223 | else: 224 | return self.v 225 | 226 | def __str__(self): 227 | return str(self.v) + ' =(' + str(self.capacity) + ', ' + str(self.flow) + ')=> ' + str(self.w) 228 | 229 | 230 | class FlowNetwork(object): 231 | 232 | V = None 233 | adjList = None 234 | 235 | def __init__(self, V): 236 | self.V = V 237 | self.adjList = [None] * V 238 | 239 | for v in range(self.V): 240 | self.adjList[v] = Bag() 241 | 242 | def adj(self, v): 243 | return self.adjList[v].iterate() 244 | 245 | def add_edge(self, edge): 246 | self.adjList[edge.start()].add(edge) 247 | self.adjList[edge.end()].add(edge) 248 | 249 | def vertex_count(self): 250 | return self.V 251 | 252 | def edges(self): 253 | for v in range(self.V): 254 | for e in self.adjList[v].iterate(): 255 | if e.start() == v: 256 | yield e 257 | 258 | 259 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | pyalgs 2 | ====== 3 | 4 | Package pyalgs implements algorithms in the "Algorithms" book (http://algs4.cs.princeton.edu/home/) and Robert Sedgwick's Algorithms course using Python (Part I and Part II) 5 | 6 | .. image:: https://travis-ci.org/chen0040/pyalgs.svg?branch=master 7 | :target: https://travis-ci.org/chen0040/pyalgs 8 | 9 | .. image:: https://coveralls.io/repos/github/chen0040/pyalgs/badge.svg?branch=master 10 | :target: https://coveralls.io/github/chen0040/pyalgs?branch=master 11 | 12 | .. image:: https://readthedocs.org/projects/pyalgs/badge/?version=latest 13 | :target: http://pyalgs.readthedocs.org/en/latest/?badge=latest 14 | 15 | .. image:: https://scrutinizer-ci.com/g/chen0040/pyalgs/badges/quality-score.png?b=master 16 | :target: https://scrutinizer-ci.com/g/chen0040/pyalgs/?branch=master 17 | 18 | 19 | More details are provided in the `docs`_ for implementation, complexities and further info. 20 | 21 | Install pyalgs 22 | ============== 23 | 24 | Run the following command to install pyalgs using pip 25 | 26 | .. code-block:: bash 27 | 28 | $ pip install pyalgs 29 | 30 | Features: 31 | ========= 32 | 33 | * Data Structure 34 | 35 | - Stack 36 | 37 | + Linked List 38 | + Array 39 | 40 | - Queue 41 | 42 | + Linked List 43 | + Array 44 | 45 | - Bag 46 | - HashSet 47 | - HashMap 48 | 49 | + Separate Chaining 50 | + Linear Probing 51 | 52 | - Binary Search Tree 53 | - Red Black Tree 54 | - Priority Queue 55 | 56 | + MinPQ 57 | + MaxPQ 58 | + IndexMinPQ 59 | 60 | - Graph 61 | 62 | + Simple graph 63 | + Edge weighted graph 64 | + Directed graph (digraph) 65 | + Directed edge weight graph 66 | 67 | - Search Tries (Symbol table with string-based keys) 68 | 69 | + R-way search tries 70 | + Ternary search tries 71 | 72 | * Algorithms 73 | 74 | - Sorting 75 | 76 | + Selection Sort 77 | + Insertion Sort 78 | + Shell Sort 79 | + Merge Sort 80 | + Quick Sort 81 | + 3-Ways Quick Sort 82 | + Heap Sort 83 | 84 | - Selection 85 | 86 | + Binary Search 87 | 88 | - Shuffling 89 | 90 | + Knuth 91 | 92 | - Union Find 93 | 94 | + Quick Find 95 | + Weighted Quick Union with path compression 96 | 97 | * Graph Algorithms 98 | 99 | - Search 100 | 101 | + Depth First Search 102 | + Breadth First Search 103 | 104 | - Connectivity 105 | 106 | + Connected Components 107 | + Strongly Connected Components 108 | 109 | - Topological Sorting 110 | 111 | + Depth First Reverse Post Order 112 | 113 | - Directed Cycle Detection 114 | 115 | - Minimum Spanning Tree 116 | 117 | + Kruskal 118 | + Prim (Lazy) 119 | + Prim (Eager) 120 | 121 | - Shortest Path 122 | 123 | + Dijkstra 124 | + Topological Sort (for directed acyclic graph, namely dag) 125 | + Bellman-Ford (for graph with negative weight as well) 126 | 127 | - MaxFlow MinCut 128 | 129 | + Ford-Fulkerson 130 | 131 | * Strings 132 | 133 | - Longest Repeated Substring 134 | - String Sorting 135 | 136 | + LSD (Least Significant Digit first radix sorting) 137 | + MSD (Most Significant Digit first radix sorting) 138 | + 3-Ways String Quick Sort 139 | 140 | - String Search 141 | 142 | + Brute force 143 | + Rabin Karp 144 | + Boyer Moore 145 | + Knuth Morris Pratt 146 | 147 | Usage: 148 | ====== 149 | 150 | Data Structure 151 | -------------- 152 | 153 | Stack 154 | 155 | 156 | .. code-block:: python 157 | 158 | from pyalgs.data_structures.commons.stack import Stack 159 | 160 | stack = Stack.create() 161 | stack.push(10) 162 | stack.push(1) 163 | 164 | print [i for i in stack.iterate()] 165 | 166 | print stack.is_empty() 167 | print stack.size() 168 | 169 | popped_item = stack.pop() 170 | print popped_item 171 | 172 | 173 | 174 | Queue 175 | 176 | 177 | .. code-block:: python 178 | 179 | from pyalgs.data_structures.commons.queue import Queue 180 | 181 | queue = Queue.create() 182 | queue.enqueue(10) 183 | queue.enqueue(20) 184 | queue.enqueue(30) 185 | 186 | print [i for i in queue.iterate()] 187 | 188 | print queue.size() 189 | print queue.is_empty() 190 | 191 | deleted_item = queue.dequeue()) 192 | print deleted_item 193 | 194 | 195 | 196 | Bag 197 | 198 | 199 | .. code-block:: python 200 | 201 | from pyalgs.data_structures.commons.bag import Bag 202 | 203 | bag = Bag.create() 204 | 205 | bag.add(10) 206 | bag.add(20) 207 | bag.add(30) 208 | 209 | print [i for i in bag.iterate()] 210 | 211 | print bag.size() 212 | print bag.is_empty() 213 | 214 | 215 | Minimum Priority Queue 216 | 217 | .. code-block:: python 218 | 219 | from pyalgs.data_structures.commons.priority_queue import MinPQ 220 | 221 | pq = MinPQ.create() 222 | pq.enqueue(10) 223 | pq.enqueue(5) 224 | pq.enqueue(12) 225 | pq.enqueue(14) 226 | pq.enqueue(2) 227 | 228 | print pq.is_empty() 229 | print pq.size() 230 | 231 | print [i for i in pq.iterate()] 232 | 233 | deleted = pq.del_min() 234 | print(deleted) 235 | 236 | 237 | Maximum Priority Queue 238 | 239 | 240 | .. code-block:: python 241 | 242 | from pyalgs.data_structures.commons.priority_queue import MaxPQ 243 | 244 | pq = MaxPQ.create() 245 | pq.enqueue(10) 246 | pq.enqueue(5) 247 | pq.enqueue(12) 248 | pq.enqueue(14) 249 | pq.enqueue(2) 250 | 251 | print pq.is_empty() 252 | print pq.size() 253 | 254 | print [i for i in pq.iterate()] 255 | 256 | deleted = pq.del_max() 257 | print deleted 258 | 259 | 260 | Symbol Table using Binary Search Tree 261 | 262 | 263 | .. code-block:: python 264 | 265 | from pyalgs.data_structures.commons.binary_search_tree import BinarySearchTree 266 | bst = BinarySearchTree.create() 267 | 268 | bst.put("one", 1) 269 | bst.put("two", 2) 270 | bst.put("three", 3) 271 | bst.put("six", 6) 272 | bst.put("ten", 10) 273 | 274 | for key in bst.keys(): 275 | print(key) 276 | 277 | print bst.get("one")) 278 | print bst.contains_key("two") 279 | 280 | print bst.size() 281 | print bst.is_empty() 282 | 283 | bst.delete("one") 284 | 285 | 286 | Symbol Table using Left Leaning Red Black Tree 287 | 288 | 289 | .. code-block:: python 290 | 291 | from pyalgs.data_structures.commons.binary_search_tree import BinarySearchTree 292 | bst = BinarySearchTree.create_red_black_tree() 293 | 294 | bst.put("one", 1) 295 | bst.put("two", 2) 296 | bst.put("three", 3) 297 | bst.put("six", 6) 298 | bst.put("ten", 10) 299 | 300 | print bst.get("one")) 301 | print bst.contains_key("two") 302 | 303 | for key in bst.keys(): 304 | print(key) 305 | 306 | print bst.size() 307 | print bst.is_empty() 308 | 309 | bst.delete("one") 310 | 311 | 312 | Symbol Table using Hashed Map 313 | 314 | 315 | .. code-block:: python 316 | 317 | from pyalgs.data_structures.commons.hashed_map import HashedMap 318 | map = HashedMap.create() 319 | 320 | map.put("one", 1) 321 | map.put("two", 2) 322 | map.put("three", 3) 323 | map.put("six", 6) 324 | map.put("ten", 10) 325 | 326 | print map.get("one")) 327 | print map.contains_key("two") 328 | 329 | for key in map.keys(): 330 | print(key) 331 | 332 | print map.size() 333 | print map.is_empty() 334 | 335 | map.delete("one") 336 | 337 | 338 | Symbol Table using Hashed Set 339 | 340 | 341 | .. code-block:: python 342 | 343 | from pyalgs.data_structures.commons.hashed_set import HashedSet 344 | set = HashedSet.create() 345 | 346 | set.add("one") 347 | set.add("two") 348 | set.add("three") 349 | set.add("six") 350 | set.add("ten") 351 | 352 | print set.contains("two") 353 | 354 | for key in set.iterate(): 355 | print(key) 356 | 357 | print set.size() 358 | print set.is_empty() 359 | 360 | set.delete("one") 361 | 362 | 363 | Undirected Graph 364 | 365 | 366 | .. code-block:: python 367 | 368 | from pyalgs.data_structures.graphs.graph import Graph 369 | def create_graph(): 370 | G = Graph(100) 371 | 372 | G.add_edge(1, 2) 373 | G.add_edge(1, 3) 374 | 375 | print([i for i in G.adj(1)]) 376 | print([i for i in G.adj(2)]) 377 | print([i for i in G.adj(3)]) 378 | 379 | print(G.vertex_count()) 380 | return G 381 | 382 | 383 | Directed Graph 384 | 385 | 386 | .. code-block:: python 387 | 388 | from pyalgs.data_structures.graphs.graph import Digraph 389 | def create_digraph(): 390 | G = Digraph(100) 391 | 392 | G.add_edge(1, 2) 393 | G.add_edge(1, 3) 394 | 395 | print([i for i in G.adj(1)]) 396 | print([i for i in G.adj(2)]) 397 | print([i for i in G.adj(3)]) 398 | 399 | print(G.vertex_count()) 400 | return G 401 | 402 | 403 | Edge Weighted Graph 404 | 405 | .. code-block:: python 406 | 407 | from pyalgs.data_structures.graphs.graph import EdgeWeightGraph, Edge 408 | def create_edge_weighted_graph(): 409 | g = EdgeWeightedGraph(8) 410 | g.add_edge(Edge(0, 7, 0.16)) 411 | g.add_edge(Edge(2, 3, 0.17)) 412 | g.add_edge(Edge(1, 7, 0.19)) 413 | g.add_edge(Edge(0, 2, 0.26)) 414 | g.add_edge(Edge(5, 7, 0.28)) 415 | 416 | print([edge for edge in G.adj(3)]) 417 | 418 | print(G.vertex_count()) 419 | print(', '.join([edge for edge in G.edges()])) 420 | return g 421 | 422 | 423 | Directed Edge Weighted Graph 424 | 425 | .. code-block:: python 426 | 427 | from pyalgs.data_structures.graphs.graph import DirectedEdgeWeightedGraph, Edge 428 | def create_edge_weighted_digraph(): 429 | g = DirectedEdgeWeightedGraph(8) 430 | 431 | g.add_edge(Edge(0, 1, 5.0)) 432 | g.add_edge(Edge(0, 4, 9.0)) 433 | g.add_edge(Edge(0, 7, 8.0)) 434 | g.add_edge(Edge(1, 2, 12.0)) 435 | return g 436 | 437 | 438 | Flow Network ( for max-flow min-cut problem) 439 | 440 | .. code-block:: python 441 | 442 | from pyalgs.data_structures.graphs.graph import FlowNetwork, FlowEdge 443 | def create_flow_network(): 444 | g = FlowNetwork(8) 445 | g.add_edge(FlowEdge(0, 1, 10)) 446 | g.add_edge(FlowEdge(0, 2, 5)) 447 | g.add_edge(FlowEdge(0, 3, 15)) 448 | g.add_edge(FlowEdge(1, 4, 9)) 449 | g.add_edge(FlowEdge(1, 5, 15)) 450 | g.add_edge(FlowEdge(1, 2, 4)) 451 | g.add_edge(FlowEdge(2, 5, 8)) 452 | g.add_edge(FlowEdge(2, 3, 4)) 453 | g.add_edge(FlowEdge(3, 6, 16)) 454 | g.add_edge(FlowEdge(4, 5, 15)) 455 | g.add_edge(FlowEdge(4, 7, 10)) 456 | g.add_edge(FlowEdge(5, 7, 10)) 457 | g.add_edge(FlowEdge(5, 6, 15)) 458 | g.add_edge(FlowEdge(6, 2, 6)) 459 | g.add_edge(FlowEdge(6, 7, 10)) 460 | 461 | return g 462 | 463 | 464 | Symbol Table using R-ways Search Tries 465 | 466 | 467 | .. code-block:: python 468 | 469 | from pyalgs.data_structures.strings.search_tries import RWaySearchTries 470 | st = RWaySearchTries() 471 | 472 | st.put("one", 1) 473 | st.put("two", 2) 474 | st.put("three", 3) 475 | st.put("six", 6) 476 | st.put("ten", 10) 477 | 478 | for key in st.keys(): 479 | print(key) 480 | 481 | print st.get("one")) 482 | print st.contains_key("two") 483 | 484 | print st.size() 485 | print st.is_empty() 486 | 487 | st.delete("one") 488 | 489 | for key in st.keys_with_prefix('t'): 490 | print(key) 491 | 492 | 493 | Symbol Table using Ternary Search Tries 494 | 495 | 496 | .. code-block:: python 497 | 498 | from pyalgs.data_structures.strings.search_tries import TernarySearchTries 499 | st = TernarySearchTries() 500 | 501 | st.put("one", 1) 502 | st.put("two", 2) 503 | st.put("three", 3) 504 | st.put("six", 6) 505 | st.put("ten", 10) 506 | 507 | for key in st.keys(): 508 | print(key) 509 | 510 | print st.get("one")) 511 | print st.contains_key("two") 512 | 513 | print st.size() 514 | print st.is_empty() 515 | 516 | st.delete("one") 517 | 518 | for key in st.keys_with_prefix('t'): 519 | print(key) 520 | 521 | 522 | Algorithms 523 | ---------- 524 | 525 | Union Find 526 | 527 | 528 | .. code-block:: python 529 | 530 | from pyalgs.algorithms.commons.union_find import UnionFind 531 | 532 | uf = UnionFind.create(10) 533 | 534 | uf.union(1, 3) 535 | uf.union(2, 4) 536 | uf.union(1, 5) 537 | 538 | print(uf.connected(1, 3)) 539 | print(uf.connected(3, 5)) 540 | print(uf.connected(1, 2)) 541 | print(uf.connected(1, 4)) 542 | 543 | 544 | Sorting 545 | 546 | 547 | The sorting algorithms sort an array in ascending order 548 | 549 | Selection Sort 550 | 551 | .. code-block:: python 552 | 553 | from pyalgs.algorithms.commons.sorting import SelectionSort 554 | 555 | a = [4, 2, 1] 556 | SelectionSort.sort(a) 557 | print(a) 558 | 559 | 560 | Insertion Sort 561 | 562 | .. code-block:: python 563 | 564 | from pyalgs.algorithms.commons.sorting import InsertionSort 565 | 566 | a = [4, 2, 1] 567 | InsertionSort.sort(a) 568 | print(a) 569 | 570 | 571 | Shell Sort 572 | 573 | .. code-block:: python 574 | 575 | from pyalgs.algorithms.commons.sorting import ShellSort 576 | 577 | a = [4, 2, 1, 23, 4, 5, 6, 7, 8, 9, 20, 11, 13, 34, 66] 578 | ShellSort.sort(a) 579 | print(a) 580 | 581 | 582 | Merge Sort 583 | 584 | .. code-block:: python 585 | 586 | from pyalgs.algorithms.commons.sorting import MergeSort 587 | 588 | a = [4, 2, 1, 23, 4, 5, 6, 7, 8, 9, 20, 11, 13, 34, 66] 589 | MergeSort.sort(a) 590 | print(a) 591 | 592 | 593 | Quick Sort 594 | 595 | .. code-block:: python 596 | 597 | from pyalgs.algorithms.commons.sorting import QuickSort 598 | 599 | a = [4, 2, 1, 23, 4, 5, 6, 7, 8, 9, 20, 11, 13, 34, 66] 600 | QuickSort.sort(a) 601 | print(a) 602 | 603 | 604 | 3-Ways Quick Sort 605 | 606 | .. code-block:: python 607 | 608 | from pyalgs.algorithms.commons.sorting import ThreeWayQuickSort 609 | 610 | a = [4, 2, 1, 23, 4, 5, 6, 7, 8, 9, 20, 11, 13, 34, 66] 611 | ThreeWayQuickSort.sort(a) 612 | print(a) 613 | 614 | 615 | Heap Sort 616 | 617 | .. code-block:: python 618 | 619 | from pyalgs.algorithms.commons.sorting import HeapSort 620 | 621 | a = [4, 2, 1, 23, 4, 5, 6, 7, 8, 9, 20, 11, 13, 34, 66] 622 | HeapSort.sort(a) 623 | print(a) 624 | 625 | 626 | 627 | Selection 628 | 629 | 630 | Binary Selection 631 | 632 | .. code-block:: python 633 | 634 | from pyalgs.algorithms.commons.selecting import BinarySelection 635 | from pyalgs.algorithms.commons.util import is_sorted 636 | 637 | 638 | a = [1, 2, 13, 22, 123] 639 | assert is_sorted(a) 640 | print BinarySelection.index_of(a, 13) 641 | 642 | 643 | Shuffle 644 | 645 | 646 | Knuth Shuffle 647 | 648 | .. code-block:: python 649 | 650 | from pyalgs.algorithms.commons.shuffling import KnuthShuffle 651 | 652 | a = [1, 2, 13, 22, 123] 653 | KnuthShuffle.shuffle(a) 654 | print(a) 655 | 656 | 657 | Graph 658 | ----- 659 | 660 | Depth First Search 661 | 662 | .. code-block:: python 663 | 664 | from pyalgs.algorithms.graphs.search import DepthFirstSearch 665 | g = create_graph() 666 | s = 0 667 | dfs = DepthFirstSearch(g, s) 668 | 669 | for v in range(1, g.vertex_count()): 670 | if dfs.hasPathTo(v): 671 | print(str(s) + ' is connected to ' + str(v)) 672 | print('path is ' + ' => '.join([str(i) for i in dfs.pathTo(v)])) 673 | 674 | 675 | Breadth First Search 676 | 677 | .. code-block:: python 678 | 679 | from pyalgs.algorithms.graphs.search import BreadthFirstSearch 680 | g = create_graph() 681 | s = 0 682 | dfs = BreadthFirstSearch(g, s) 683 | 684 | for v in range(1, g.vertex_count()): 685 | if dfs.hasPathTo(v): 686 | print(str(s) + ' is connected to ' + str(v)) 687 | print('path is ' + ' => '.join([str(i) for i in dfs.pathTo(v)])) 688 | 689 | 690 | Connected Components 691 | 692 | This is for undirected graph 693 | 694 | .. code-block:: python 695 | 696 | from pyalgs.algorithms.graphs.connectivity import ConnectedComponents 697 | G = create_graph() 698 | 699 | cc = ConnectedComponents(G) 700 | print('connected component count: ' + str(cc.count())) 701 | 702 | 703 | for v in range(G.vertex_count()): 704 | print('id[' + str(v) + ']: ' + str(cc.id(v))) 705 | for v in range(G.vertex_count()): 706 | r = randint(0, G.vertex_count()-1) 707 | if cc.connected(v, r): 708 | print(str(v) + ' is connected to ' + str(r)) 709 | 710 | 711 | Strongly Connected Components 712 | 713 | This is for directed graph 714 | 715 | .. code-block:: python 716 | 717 | from pyalgs.algorithms.graphs.connectivity import StronglyConnectedComponents 718 | G = create_graph() 719 | 720 | cc = StronglyConnectedComponents(G) 721 | print('strongly connected component count: ' + str(cc.count())) 722 | 723 | 724 | for v in range(G.vertex_count()): 725 | print('id[' + str(v) + ']: ' + str(cc.id(v))) 726 | for v in range(G.vertex_count()): 727 | r = randint(0, G.vertex_count()-1) 728 | if cc.connected(v, r): 729 | print(str(v) + ' is connected to ' + str(r)) 730 | 731 | 732 | Topological Sort 733 | 734 | .. code-block:: python 735 | 736 | from pyalgs.algorithms.graphs.topological_sort import DepthFirstOrder 737 | G = create_graph() 738 | topological_sort = DepthFirstOrder(G) 739 | print(' => '.join([str(i) for i in topological_sort.postOrder()])) 740 | 741 | 742 | Minimum Spanning Tree (Kruskal) 743 | 744 | .. code-block:: python 745 | 746 | from pyalgs.algorithms.graphs.minimum_spanning_trees import KruskalMST 747 | g = create_edge_weighted_graph() 748 | mst = KruskalMST(g) 749 | 750 | tree = mst.spanning_tree() 751 | 752 | for e in tree: 753 | print(e) 754 | 755 | Minimum Spanning Tree (Prim Lazy) 756 | 757 | .. code-block:: python 758 | 759 | from pyalgs.algorithms.graphs.minimum_spanning_trees import LazyPrimMST 760 | g = create_edge_weighted_graph() 761 | mst = LazyPrimMST(g) 762 | 763 | tree = mst.spanning_tree() 764 | 765 | for e in tree: 766 | print(e) 767 | 768 | 769 | Minimum Spanning Tree (Prim Eager) 770 | 771 | .. code-block:: python 772 | 773 | from pyalgs.algorithms.graphs.minimum_spanning_trees import EagerPrimMST 774 | g = create_edge_weighted_graph() 775 | mst = EagerPrimMST(g) 776 | 777 | tree = mst.spanning_tree() 778 | 779 | for e in tree: 780 | print(e) 781 | 782 | 783 | Directed Cycle Detection: 784 | 785 | .. code-block:: python 786 | 787 | from pyalgs.algorithms.graphs.directed_cycle import DirectedCycle 788 | dag = create_dag() 789 | dc = DirectedCycle(dag) 790 | assertFalse(dc.hasCycle()) 791 | 792 | 793 | Shortest Path (Dijkstra) 794 | 795 | .. code-block:: python 796 | 797 | from pyalgs.algorithms.graphs.shortest_path import DijkstraShortestPath 798 | g = create_edge_weighted_digraph() 799 | s = 0 800 | dijkstra = DijkstraShortestPath(g, s) 801 | for v in range(1, g.vertex_count()): 802 | if dijkstra.hasPathTo(v): 803 | print(str(s) + ' is connected to ' + str(v)) 804 | print('shortest path is ' + ' .. '.join([str(i) for i in dijkstra.shortestPathTo(v)])) 805 | print('path length is ' + str(dijkstra.path_length_to(v))) 806 | 807 | 808 | Shortest Path (Topological Sort) 809 | 810 | .. code-block:: python 811 | 812 | from pyalgs.algorithms.graphs.shortest_path import TopologicalSortShortestPath 813 | from pyalgs.algorithms.graphs.directed_cycle import DirectedCycle 814 | g = create_edge_weighted_digraph() 815 | assert not DirectedCycle(g).hasCycle() 816 | s = 0 817 | dijkstra = TopologicalSortShortestPath(g, s) 818 | for v in range(1, g.vertex_count()): 819 | if dijkstra.hasPathTo(v): 820 | print(str(s) + ' is connected to ' + str(v)) 821 | print('shortest path is ' + ' .. '.join([str(i) for i in dijkstra.shortestPathTo(v)])) 822 | print('path length is ' + str(dijkstra.path_length_to(v))) 823 | 824 | 825 | Shortest Path (Bellman-Ford for positive and negative edge graph) 826 | 827 | .. code-block:: python 828 | 829 | from pyalgs.algorithms.graphs.shortest_path import BellmanFordShortestPath 830 | from pyalgs.algorithms.graphs.directed_cycle import DirectedCycle 831 | g = create_edge_weighted_digraph() 832 | s = 0 833 | dijkstra = BellmanFordShortestPath(g, s) 834 | for v in range(1, g.vertex_count()): 835 | if dijkstra.hasPathTo(v): 836 | print(str(s) + ' is connected to ' + str(v)) 837 | print('shortest path is ' + ' .. '.join([str(i) for i in dijkstra.shortestPathTo(v)])) 838 | print('path length is ' + str(dijkstra.path_length_to(v))) 839 | 840 | 841 | MaxFlow MinCut (Ford-Fulkerson) 842 | 843 | .. code-block:: python 844 | 845 | from pyalgs.algorithms.graphs.max_flow import FordFulkersonMaxFlow 846 | network = create_flow_network() 847 | ff = FordFulkersonMaxFlow(network, 0, 7) 848 | print('max-flow: '+str(ff.max_flow_value())) 849 | 850 | 851 | Strings 852 | ------- 853 | 854 | Longest Repeated Substring 855 | 856 | .. code-block:: python 857 | 858 | from pyalgs.algorithms.strings.longest_repeated_substring import LongestRepeatedSubstringSearch 859 | start, len = LongestRepeatedSubstringSearch.lrs('Hello World', 'World Record') 860 | print('Hello World'[start:(start+len+1)]) 861 | 862 | 863 | Sort (LSD) 864 | 865 | .. code-block:: python 866 | 867 | from pyalgs.algorithms.strings.sorting import LSD 868 | LSD.sort(['good', 'cool', 'done', 'come']) 869 | 870 | 871 | Sort (MSD) 872 | 873 | .. code-block:: python 874 | 875 | from pyalgs.algorithms.strings.sorting import MSD 876 | words = 'more details are provided in the docs for implementation, complexities and further info'.split(' ') 877 | print(words) 878 | MSD.sort(words) 879 | print(words) 880 | 881 | 882 | Sort (3-Ways String Quick Sort) 883 | 884 | .. code-block:: python 885 | 886 | from pyalgs.algorithms.strings.sorting import String3WayQuickSort 887 | words = 'more details are provided in the docs for implementation, complexities and further info'.split(' ') 888 | print(words) 889 | String3WayQuickSort.sort(words) 890 | print(words) 891 | 892 | 893 | Substring Search (Brute force) 894 | 895 | .. code-block:: python 896 | 897 | from pyalgs.algorithms.strings.substring_search import BruteForceSubstringSearch 898 | ss = BruteForceSubstringSearch('find') 899 | print(ss.search_in('I can find it here')) 900 | print(ss.search_in('It is not here')) 901 | 902 | 903 | Substring Search (Rabin Karp) 904 | 905 | .. code-block:: python 906 | 907 | from pyalgs.algorithms.strings.substring_search import RabinKarp 908 | ss = RabinKarp('find') 909 | print(ss.search_in('I can find it here')) 910 | print(ss.search_in('It is not here')) 911 | 912 | 913 | Substring Search (Boyer Moore) 914 | 915 | .. code-block:: python 916 | 917 | from pyalgs.algorithms.strings.substring_search import BoyerMoore 918 | ss = BoyerMoore('find') 919 | print(ss.search_in('I can find it here')) 920 | print(ss.search_in('It is not here')) 921 | 922 | 923 | Substring Search (Knuth Morris Pratt) 924 | 925 | .. code-block:: python 926 | 927 | from pyalgs.algorithms.strings.substring_search import KnuthMorrisPratt 928 | ss = KnuthMorrisPratt('find') 929 | print(ss.search_in('I can find it here')) 930 | print(ss.search_in('It is not here')) 931 | 932 | 933 | .. _`docs`: http://pyalgs.readthedocs.org/en/latest/ 934 | .. _`documentation`: http://pyalgs.readthedocs.org/en/latest/ --------------------------------------------------------------------------------