Renamed top-level folders to use underscores instead of dashes.

This commit is contained in:
Donne Martin
2015-06-28 06:39:24 -04:00
parent d44ed31ef9
commit 7573730e39
53 changed files with 0 additions and 0 deletions

View File

View File

View File

@@ -0,0 +1,269 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"<small><i>This notebook was prepared by [Donne Martin](http://donnemartin.com). Source and license info is on [GitHub](https://bit.ly/code-notes).</i></small>"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Problem: Compress a string such that 'AAABCCDDDD' becomes 'A3B1C2D4'\n",
"\n",
"* [Constraints and Assumptions](#Constraints-and-Assumptions)\n",
"* [Test Cases](#Test-Cases)\n",
"* [Algorithm: List](#Algorithm:-List)\n",
"* [Code: List](#Code:-List)\n",
"* [Algorithm: Byte Array](#Algorithm:-Byte-Array)\n",
"* [Code: Byte array](#Code:-Byte-Array)\n",
"* [Unit Test](#Unit-Test)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Constraints and Assumptions\n",
"\n",
"*Problem statements are often intentionally ambiguous. Identifying constraints and stating assumptions can help to ensure you code the intended solution.*\n",
"\n",
"* Can I assume the string is ASCII?\n",
" * Yes\n",
" * Note: Unicode strings could require special handling depending on your language\n",
"* Can you use additional data structures? \n",
" * Yes\n",
"* Is this case sensitive?\n",
" * Yes\n",
"* Do you compress even if it doesn't save space?\n",
" * No"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Test Cases\n",
"\n",
"* NULL\n",
"* '' -> ''\n",
"* 'ABC' -> 'ABC'\n",
"* 'AAABCCDDDD' -> 'A3B1C2D4'"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Algorithm: List\n",
"\n",
"Since Python strings are immutable, we'll use a list of characters instead to exercise in-place string manipulation as you would get with a C string (which is null terminated, as seen in the diagram below). Python does not use a null-terminator.\n",
"\n",
"![alt text](https://raw.githubusercontent.com/donnemartin/algorithms-data-structures/master/images/compress_string.jpg)\n",
"\n",
"* Calculate the size of the compressed string\n",
"* If the compressed string size is >= string size, return string\n",
"* Create compressed_string\n",
" * For each char in string\n",
" * If char is the same as last_char, increment count\n",
" * Else\n",
" * Append last_char to compressed_string\n",
" * append count to compressed_string\n",
" * count = 1\n",
" * last_char = char\n",
" * Append last_char to compressed_string\n",
" * Append count to compressed_string\n",
" * Return compressed_string\n",
"\n",
"Complexity:\n",
"* Time: O(n)\n",
"* Space: O(2m) where m is the size of the compressed list and the resulting string copied from the list"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Code: List"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"def compress_string(string):\n",
" if string is None or len(string) == 0:\n",
" return string\n",
" \n",
" # Calculate the size of the compressed string\n",
" size = 0\n",
" last_char = string[0]\n",
" for char in string:\n",
" if char != last_char:\n",
" size += 2\n",
" last_char = char\n",
" size += 2\n",
" \n",
" # If the compressed string size is greater than \n",
" # or equal to string size, return string\n",
" if size >= len(string):\n",
" return string\n",
"\n",
" # Create compressed_string\n",
" compressed_string = list()\n",
" count = 0\n",
" last_char = string[0]\n",
" for char in string:\n",
" if char == last_char:\n",
" count += 1\n",
" else:\n",
" compressed_string.append(last_char)\n",
" compressed_string.append(str(count))\n",
" count = 1\n",
" last_char = char\n",
" compressed_string.append(last_char)\n",
" compressed_string.append(str(count))\n",
" return \"\".join(compressed_string)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Algorithm: Byte Array\n",
"\n",
"The byte array algorithm similar when using a list, except we will need to work with the bytearray's character codes instead of the characters as we did above when we implemented this solution with a list.\n",
"\n",
"Complexity:\n",
"* Time: O(n)\n",
"* Space: O(m) where m is the size of the compressed bytearray"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Code: Byte Array"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"def compress_string_alt(string):\n",
" if string is None or len(string) == 0:\n",
" return string\n",
" \n",
" # Calculate the size of the compressed string\n",
" size = 0\n",
" last_char_code = string[0]\n",
" for char_code in string:\n",
" if char_code != last_char_code:\n",
" size += 2\n",
" last_char_code = char_code\n",
" size += 2\n",
" \n",
" # If the compressed string size is greater than \n",
" # or equal to string size, return string \n",
" if size >= len(string):\n",
" return string\n",
" \n",
" # Create compressed_string\n",
" compressed_string = bytearray(size)\n",
" pos = 0\n",
" count = 0\n",
" last_char_code = string[0]\n",
" for char_code in string:\n",
" if char_code == last_char_code:\n",
" count += 1\n",
" else:\n",
" compressed_string[pos] = last_char_code\n",
" compressed_string[pos+1] = ord(str(count))\n",
" pos += 2\n",
" count = 1\n",
" last_char_code = char_code\n",
" compressed_string[pos] = last_char_code\n",
" compressed_string[pos+1] = ord(str(count))\n",
" return compressed_string"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Unit Test"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"*It is important to identify and run through general and edge cases from the [Test Cases](#Test-Cases) section by hand. You generally will not be asked to write a unit test like what is shown below.*"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Success: test_compress\n",
"Success: test_compress\n"
]
}
],
"source": [
"from nose.tools import assert_equal\n",
"\n",
"class Test(object):\n",
" def test_compress(self, func):\n",
" assert_equal(func(None), None)\n",
" assert_equal(func(''), '')\n",
" assert_equal(func('ABC'), 'ABC')\n",
" assert_equal(func('AAABCCDDDD'), 'A3B1C2D4')\n",
" print('Success: test_compress')\n",
"\n",
"if __name__ == '__main__':\n",
" test = Test()\n",
" test.test_compress(compress_string)\n",
" test.test_compress(compress_string_alt)"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 2",
"language": "python",
"name": "python2"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 2
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython2",
"version": "2.7.10"
}
},
"nbformat": 4,
"nbformat_minor": 0
}

View File

View File

@@ -0,0 +1,248 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"<small><i>This notebook was prepared by [Donne Martin](http://donnemartin.com). Source and license info is on [GitHub](https://bit.ly/code-notes).</i></small>"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Problem: Implement a hash table with set, get, and remove methods.\n",
"\n",
"* [Constraints and Assumptions](#Constraints-and-Assumptions)\n",
"* [Test Cases](#Test-Cases)\n",
"* [Algorithm](#Algorithm)\n",
"* [Code](#Code)\n",
"* [Pythonic-Code](#Pythonic-Code)\n",
"* [Unit Test](#Unit-Test)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Constraints and Assumptions\n",
"\n",
"*Problem statements are often intentionally ambiguous. Identifying constraints and stating assumptions can help to ensure you code the intended solution.*\n",
"\n",
"* For simplicity, are the keys integers only?\n",
" * Yes\n",
"* For collision resolution, can we use linked lists?\n",
" * Yes\n",
"* Do we have to worry about load factors?\n",
" * No"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Test Cases\n",
"\n",
"* get on an empty hash table index\n",
"* set on an empty hash table index\n",
"* set on a non empty hash table index\n",
"* set on a key that already exists\n",
"* remove on a key with an entry\n",
"* remove on a key without an entry"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Algorithm\n",
"\n",
"### Hash Function\n",
"\n",
"* Return key % table size\n",
"\n",
"Complexity:\n",
"* Time: O(1)\n",
"* Space: O(1)\n",
"\n",
"### Set\n",
"\n",
"* Get hash index for lookup\n",
"* If key exists, replace\n",
"* Else, add\n",
"\n",
"Complexity:\n",
"* Time: O(1) average and best, O(n) worst\n",
"* Space: O(1) space for newly added element\n",
"\n",
"### Get\n",
"\n",
"* Get hash index for lookup\n",
"* If key exists, return value\n",
"* Else, return NULL\n",
"\n",
"Complexity:\n",
"* Time: O(1) average and best, O(n) worst\n",
"* Space: O(1)\n",
"\n",
"### Remove\n",
"\n",
"* Get hash index for lookup\n",
"* If key exists, delete the item\n",
"\n",
"Complexity:\n",
"* Time: O(1) average and best, O(n) worst\n",
"* Space: O(1)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Code"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"class Item(object):\n",
" def __init__(self, key, value):\n",
" self.key = key\n",
" self.value = value\n",
"\n",
"class HashTable(object):\n",
" def __init__(self, size):\n",
" self.size = size\n",
" self.table = [[] for _ in xrange(self.size)]\n",
"\n",
" def hash_function(self, key):\n",
" return key % self.size\n",
"\n",
" def set(self, key, value):\n",
" hash_index = self.hash_function(key)\n",
" for item in self.table[hash_index]:\n",
" if item.key == key:\n",
" item.value = value\n",
" return\n",
" self.table[hash_index].append(Item(key, value))\n",
"\n",
" def get(self, key):\n",
" hash_index = self.hash_function(key)\n",
" for item in self.table[hash_index]:\n",
" if item.key == key:\n",
" return item.value\n",
" return None\n",
"\n",
" def remove(self, key):\n",
" hash_index = self.hash_function(key)\n",
" for i, item in enumerate(self.table[hash_index]):\n",
" if item.key == key:\n",
" del self.table[hash_index][i]"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Unit Test"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"*It is important to identify and run through general and edge cases from the [Test Cases](#Test-Cases) section by hand. You generally will not be asked to write a unit test like what is shown below.*"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Test: get on an empty hash table index\n",
"Test: set on an empty hash table index\n",
"Test: set on a non empty hash table index\n",
"Test: set on a key that already exists\n",
"Test: remove on a key that already exists\n",
"Test: remove on a key that doesn't exist\n",
"Success: test_end_to_end\n"
]
}
],
"source": [
"from nose.tools import assert_equal\n",
"\n",
"class Test(object):\n",
" # TODO: It would be better if we had unit tests for each\n",
" # method in addition to the following end-to-end test\n",
" def test_end_to_end(self):\n",
" hash_table = HashTable(10)\n",
" \n",
" print(\"Test: get on an empty hash table index\")\n",
" assert_equal(hash_table.get(0), None)\n",
" \n",
" print(\"Test: set on an empty hash table index\")\n",
" hash_table.set(0, 'foo')\n",
" assert_equal(hash_table.get(0), 'foo')\n",
" hash_table.set(1, 'bar')\n",
" assert_equal(hash_table.get(1), 'bar')\n",
" \n",
" print(\"Test: set on a non empty hash table index\")\n",
" hash_table.set(10, 'foo2')\n",
" assert_equal(hash_table.get(0), 'foo')\n",
" assert_equal(hash_table.get(10), 'foo2')\n",
" \n",
" print(\"Test: set on a key that already exists\")\n",
" hash_table.set(10, 'foo3')\n",
" assert_equal(hash_table.get(0), 'foo')\n",
" assert_equal(hash_table.get(10), 'foo3')\n",
" \n",
" print(\"Test: remove on a key that already exists\")\n",
" hash_table.remove(10)\n",
" assert_equal(hash_table.get(0), 'foo')\n",
" assert_equal(hash_table.get(10), None)\n",
" \n",
" print(\"Test: remove on a key that doesn't exist\")\n",
" hash_table.remove(-1)\n",
" \n",
" print('Success: test_end_to_end')\n",
"\n",
"if __name__ == '__main__':\n",
" test = Test()\n",
" test.test_end_to_end()"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 2",
"language": "python",
"name": "python2"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 2
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython2",
"version": "2.7.10"
}
},
"nbformat": 4,
"nbformat_minor": 0
}

View File

View File

@@ -0,0 +1,220 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"<small><i>This notebook was prepared by [Donne Martin](http://donnemartin.com). Source and license info is on [GitHub](https://bit.ly/code-notes).</i></small>"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Problem: Determine if a string is a permutation of another string\n",
"\n",
"* [Constraints and Assumptions](#Constraints-and-Assumptions)\n",
"* [Test Cases](#Test-Cases)\n",
"* [Algorithm: Compare Sorted Strings](#Algorithm:-Compare-Sorted-Strings)\n",
"* [Code: Compare Sorted Strings](#Code:-Compare-Sorted-Strings)\n",
"* [Algorithm: Hashmap Lookup](#Algorithm:-Hash-Map-Lookup)\n",
"* [Code: Hashmap Lookup](#Code:-Hash-Map-Lookup)\n",
"* [Unit Test](#Unit-Test)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Constraints and Assumptions\n",
"\n",
"*Problem statements are often intentionally ambiguous. Identifying constraints and stating assumptions can help to ensure you code the intended solution.*\n",
"\n",
"* Can I assume the string is ASCII?\n",
" * Yes\n",
" * Note: Unicode strings could require special handling depending on your language\n",
"* Is whitespace important?\n",
" * Yes\n",
"* Is this case sensitive? 'Nib', 'bin' is not a match?\n",
" * Yes"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Test Cases\n",
"\n",
"* One or more empty strings -> False\n",
"* 'Nib', 'bin' -> False\n",
"* 'act', 'cat' -> True\n",
"* 'a ct', 'ca t' -> True"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Algorithm: Compare Sorted Strings\n",
"\n",
"Anagrams contain the same strings but in different orders. This approach could be slow for large strings due to sorting.\n",
"\n",
"* Sort both strings\n",
"* If both sorted strings are equal\n",
" * return True\n",
"* Else\n",
" * return False\n",
"\n",
"Complexity:\n",
"* Time: O(n log n) from the sort, in general\n",
"* Space: Additional O(l + m) is created by the sorting algorithm, where l is the length of one string and m is the length of the other"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Code: Compare Sorted Strings"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"def permutations(str1, str2):\n",
" return sorted(str1) == sorted(str2)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Algorithm: Hash Map Lookup\n",
"\n",
"We'll keep a hash map (dict) to keep track of characters we encounter. \n",
"\n",
"Steps:\n",
"* Scan each character\n",
"* For each character in each string:\n",
" * If the character does not exist in a hash map, add the character to a hash map\n",
" * Else, increment the character's count\n",
"* If the hash maps for each string are equal\n",
" * Return True\n",
"* Else\n",
" * Return False\n",
"\n",
"Notes:\n",
"* Since the characters are in ASCII, we could potentially use an array of size 128 (or 256 for extended ASCII), where each array index is equivalent to an ASCII value\n",
"* Instead of using two hash maps, you could use one hash map and increment character values based on the first string and decrement based on the second string\n",
"* You can short circuit if the lengths of each string are not equal, len() in Python is generally O(1)\n",
"\n",
"Complexity:\n",
"* Time: O(n)\n",
"* Space: Additional O(m), where m is the number of unique characters in the hash map"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Code: Hash Map Lookup"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"from collections import defaultdict\n",
"\n",
"def unique_counts(string):\n",
" dict_chars = defaultdict(int)\n",
" for char in string:\n",
" dict_chars[char] += 1\n",
" return dict_chars\n",
"\n",
"def permutations_alt(str1, str2):\n",
" if len(str1) != len(str2):\n",
" return False\n",
" unique_counts1 = unique_counts(str1)\n",
" unique_counts2 = unique_counts(str2)\n",
" return unique_counts1 == unique_counts2"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Unit Test"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"*It is important to identify and run through general and edge cases from the [Test Cases](#Test-Cases) section by hand. You generally will not be asked to write a unit test like what is shown below.*"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Success: test_permutation\n",
"Success: test_permutation\n"
]
}
],
"source": [
"from nose.tools import assert_equal\n",
"\n",
"class Test(object):\n",
" def test_permutation(self, func):\n",
" assert_equal(func('', 'foo'), False)\n",
" assert_equal(func('Nib', 'bin'), False)\n",
" assert_equal(func('act', 'cat'), True)\n",
" assert_equal(func('a ct', 'ca t'), True)\n",
" print('Success: test_permutation')\n",
"\n",
"if __name__ == '__main__':\n",
" test = Test()\n",
" test.test_permutation(permutations)\n",
" test.test_permutation(permutations_alt)"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 2",
"language": "python",
"name": "python2"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 2
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython2",
"version": "2.7.10"
}
},
"nbformat": 4,
"nbformat_minor": 0
}

View File

View File

@@ -0,0 +1,210 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"<small><i>This notebook was prepared by [Donne Martin](http://donnemartin.com). Source and license info is on [GitHub](https://bit.ly/code-notes).</i></small>"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Problem: Given a string, replace in-place all spaces with '%20'\n",
"\n",
"* [Constraints and Assumptions](#Constraints-and-Assumptions)\n",
"* [Test Cases](#Test-Cases)\n",
"* [Algorithm](#Algorithm)\n",
"* [Code](#Code)\n",
"* [Pythonic-Code: Not In-Place](#Pythonic-Code:-Not-In-Place)\n",
"* [Unit Test](#Unit-Test)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Constraints and Assumptions\n",
"\n",
"*Problem statements are often intentionally ambiguous. Identifying constraints and stating assumptions can help to ensure you code the intended solution.*\n",
"\n",
"* Can I assume the string is ASCII?\n",
" * Yes\n",
" * Note: Unicode strings could require special handling depending on your language\n",
"* Is there enough space in the data structure for this operation?\n",
" * Yes"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Test Cases\n",
"\n",
"* NULL->NULL\n",
"* ' ' -> '%20'\n",
"* ' foo bar ' -> '%20foo%20bar%20'\n",
"* 'foo' -> 'foo'"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Algorithm\n",
"\n",
"Since Python strings are immutable, we'll use a bytearray instead to exercise in-place string manipulation as you would get with a C string (which is null terminated, as seen in the diagram below). Python does not use a null-terminator.\n",
"\n",
"![alt text](https://raw.githubusercontent.com/donnemartin/algorithms-data-structures/master/images/replace_string.jpg)\n",
"\n",
"* Count the number of spaces in the bytearray\n",
"* Determine the new bytearray length\n",
"* For each character code in the bytearray, starting from the end of the string\n",
" * If the character code is a space\n",
" * bytearray[new length] = '0',\n",
" * bytearray[new length - 1] = '2',\n",
" * bytearray[new length - 2] = '%',\n",
" * new length -= 3\n",
" * Else\n",
" * bytearray[new length] = character code,\n",
" * new length -= 1\n",
"\n",
"Complexity:\n",
"* Time: O(n)\n",
"* Space: In-place"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Code"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"def encode_spaces(string, length):\n",
" if string is None:\n",
" return\n",
" num_spaces = string.count(' ')\n",
" new_length = length + 2 * num_spaces - 1\n",
" for i in xrange(length-1, -1, -1):\n",
" if chr(string[i]) == ' ':\n",
" string[new_length] = '0'\n",
" string[new_length-1] = '2'\n",
" string[new_length-2] = '%'\n",
" new_length -= 3\n",
" else:\n",
" string[new_length] = string[i]\n",
" new_length -= 1"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Pythonic-Code: Not In-Place\n",
"\n",
"The following code is Pythonic, but requires using additional data structures as Python strings are immutable. You could use a bytearray or a list instead of a string to simulate manipulating an array of characters."
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"import re\n",
"\n",
"def encode_spaces_alt(string):\n",
" return re.sub(' ', '%20', string)\n",
"\n",
"def encode_spaces_alt2(string):\n",
" return string.replace(' ', '%20')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Unit Test"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"*It is important to identify and run through general and edge cases from the [Test Cases](#Test-Cases) section by hand. You generally will not be asked to write a unit test like what is shown below.*"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Success: test_replace_char\n"
]
}
],
"source": [
"from nose.tools import assert_equal\n",
"\n",
"class Test(object):\n",
" def test_replace_char(self, func):\n",
" str0 = None\n",
" str1 = bytearray(' ||')\n",
" str2 = bytearray(' foo bar ||||||')\n",
" str3 = bytearray('foo')\n",
" func(str0, 0)\n",
" func(str1, 1)\n",
" func(str2, 9)\n",
" func(str3, 3)\n",
" assert_equal(str0, None)\n",
" assert_equal(str1, '%20')\n",
" assert_equal(str2, '%20foo%20bar%20')\n",
" assert_equal(str3, 'foo')\n",
" print('Success: test_replace_char')\n",
"\n",
"if __name__ == '__main__':\n",
" test = Test()\n",
" test.test_replace_char(encode_spaces)"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 2",
"language": "python",
"name": "python2"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 2
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython2",
"version": "2.7.10"
}
},
"nbformat": 4,
"nbformat_minor": 0
}

View File

@@ -0,0 +1,36 @@
#include <stdio.h>
void Reverse(char* str) {
if (str) {
char* i = str; // first letter
char* j = str; // last letter
// find the end of the string
while (*j) {
j++;
}
// don't point to the null terminator
j--;
char tmp;
// swap chars to reverse the string
while (i < j) {
tmp = *i;
*i++ = *j;
*j-- = tmp;
}
}
}
int main() {
char test0[] = "";
char test1[] = "foo";
Reverse(NULL);
Reverse(test0);
Reverse(test1);
printf("%s \n", test0);
printf("%s \n", test1);
return 0;
}

View File

@@ -0,0 +1,274 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"<small><i>This notebook was prepared by [Donne Martin](http://donnemartin.com). Source and license info is on [GitHub](https://bit.ly/code-notes).</i></small>"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Problem: Implement a function to reverse a string.\n",
"\n",
"* [Constraints and Assumptions](#Constraints-and-Assumptions)\n",
"* [Test Cases](#Test-Cases)\n",
"* [Algorithm](#Algorithm)\n",
"* [Code](#Code)\n",
"* [Pythonic-Code](#Pythonic-Code)\n",
"* [Unit Test](#Unit-Test)\n",
"* [C Algorithm](C-Algorithm)\n",
"* [C Code]()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Constraints and Assumptions\n",
"\n",
"*Problem statements are often intentionally ambiguous. Identifying constraints and stating assumptions can help to ensure you code the intended solution.*\n",
"\n",
"* Can I assume the string is ASCII?\n",
" * Yes\n",
" * Note: Unicode strings could require special handling depending on your language\n",
"* Can I use the slice operator or the reversed function?\n",
" * No\n",
"* Since Python string are immutable, can I use a list instead?\n",
" * Yes"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Test Cases\n",
"\n",
"* NULL input\n",
"* '' -> ''\n",
"* 'foo bar' -> 'rab oof'"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Algorithm\n",
"\n",
"* Convert the string to a list\n",
"* Iterate len(string)/2 times, starting with i = 0:\n",
" * Swap i and len(string) - 1 - i\n",
" * Sncrement i\n",
"* Convert the list to a string and return it\n",
"\n",
"Complexity:\n",
"* Time: O(n)\n",
"* Space: O(n), additional space converting to/from a list"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Code"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"def reverse_string(string):\n",
" if string is None:\n",
" return None\n",
" string_list = list(string)\n",
" string_length = len(string_list)\n",
" for i in xrange(string_length/2):\n",
" string_list[i], string_list[string_length-1-i] = \\\n",
" string_list[string_length-1-i], string_list[i]\n",
" return ''.join(string_list)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Pythonic-Code\n",
"\n",
"This question has an artificial constraint that prevented the use of the slice operator and the reversed method. For completeness, the solutions for these are provided below."
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"def reverse_string_alt(string):\n",
" if string is None:\n",
" return None\n",
" return string[::-1]\n",
"\n",
"def reverse_string_alt2(string):\n",
" if string is None:\n",
" return None \n",
" return ''.join(reversed(string))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Unit Test"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"*It is important to identify and run through general and edge cases from the [Test Cases](#Test-Cases) section by hand. You generally will not be asked to write a unit test like what is shown below.*"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Success: test_reversed\n",
"Success: test_reversed\n",
"Success: test_reversed\n"
]
}
],
"source": [
"from nose.tools import assert_equal\n",
"\n",
"class Test(object):\n",
" def test_reversed(self, func):\n",
" assert_equal(func(None), None)\n",
" assert_equal(func(''), '')\n",
" assert_equal(func('foo bar'), 'rab oof')\n",
" print('Success: test_reversed')\n",
"\n",
"if __name__ == '__main__':\n",
" test = Test()\n",
" test.test_reversed(reverse_string)\n",
" test.test_reversed(reverse_string_alt)\n",
" test.test_reversed(reverse_string_alt2)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## C Algorithm\n",
"\n",
"This is a classic problem in C/C++\n",
"\n",
"We'll want to keep two pointers:\n",
"* i is a pointer to the first char\n",
"* j is a pointer to the last char\n",
"\n",
"To get a pointer to the last char, we need to loop through all characters, take note of null terminator.\n",
"\n",
"* while i < j\n",
" * swap i and j\n",
"\n",
"Complexity:\n",
"* Time: O(n)\n",
"* Space: In-place\n",
"\n",
"Note:\n",
"* Instead of using i, you can use str instead, although this might not be as intuitive."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## C Code"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"# %load reverse_string.cpp\n",
"#include <stdio.h>\n",
"\n",
"void Reverse(char* str) {\n",
" if (str) {\n",
" char* i = str;\t// first letter\n",
" char* j = str;\t// last letter\n",
" \n",
" // find the end of the string\n",
" while (*j) {\n",
" j++;\n",
" }\n",
" \n",
" // don't point to the null terminator\n",
" j--;\n",
" \n",
" char tmp;\n",
" \n",
" // swap chars to reverse the string\n",
" while (i < j) {\n",
" tmp = *i;\n",
" *i++ = *j;\n",
" *j-- = tmp;\n",
" }\n",
" }\n",
"}\n",
"\n",
"int main() {\n",
" char test0[] = \"\";\n",
" char test1[] = \"foo\";\n",
" Reverse(NULL);\n",
" Reverse(test0);\n",
" Reverse(test1);\n",
" printf(\"%s \\n\", test0);\n",
" printf(\"%s \\n\", test1);\n",
" return 0;\n",
"}"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 2",
"language": "python",
"name": "python2"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 2
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython2",
"version": "2.7.10"
}
},
"nbformat": 4,
"nbformat_minor": 0
}

View File

View File

@@ -0,0 +1,167 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"<small><i>This notebook was prepared by [Donne Martin](http://donnemartin.com). Source and license info is on [GitHub](https://bit.ly/code-notes).</i></small>"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Problem: Determine if a string s1 is a rotation of another string s2. Also write a function is_substring which you can only call once to determine whether a rotation occurs\n",
"\n",
"* [Constraints and Assumptions](#Constraints-and-Assumptions)\n",
"* [Test Cases](#Test-Cases)\n",
"* [Algorithm](#Algorithm)\n",
"* [Code](#Code)\n",
"* [Unit Test](#Unit-Test)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Constraints and Assumptions\n",
"\n",
"*Problem statements are often intentionally ambiguous. Identifying constraints and stating assumptions can help to ensure you code the intended solution.*\n",
"\n",
"* Can I assume the string is ASCII?\n",
" * Yes\n",
" * Note: Unicode strings could require special handling depending on your language\n",
"* Can you use additional data structures? \n",
" * Yes\n",
"* Is this case sensitive?\n",
" * Yes"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Test Cases\n",
"\n",
"* Any strings that differ in size results in False\n",
"* NULL, 'foo' -> False (any NULL results in False)\n",
"* ' ', 'foo' -> False\n",
"* ' ', ' ' -> True\n",
"* 'foobarbaz', 'barbazfoo' -> True"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Algorithm\n",
"\n",
"Using the following test case:\n",
"* s1 = 'barbazfoo'\n",
"* s2 = 'foobarbaz'\n",
"\n",
"We see that if we can use the given is_substring method if we take compare s2 with s1 + s1:\n",
"* s2 = 'foobarbaz'\n",
"* s3 = 'barbaz*foobarbaz*foo'\n",
"\n",
"Complexity:\n",
"* Time: O(n)\n",
"* Space: O(2n) of additional space for s3, which reduces to O(n)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Code"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"def is_substring(s1, s2):\n",
" return s1 in s2\n",
"\n",
"def is_rotation(s1, s2):\n",
" if s1 is None or s2 is None:\n",
" return False\n",
" if len(s1) != len(s2):\n",
" return False\n",
" s3 = s1 + s1\n",
" return is_substring(s2, s3)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Unit Test"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"*It is important to identify and run through general and edge cases from the [Test Cases](#Test-Cases) section by hand. You generally will not be asked to write a unit test like what is shown below.*"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Success: test_is_rotation\n"
]
}
],
"source": [
"from nose.tools import assert_equal\n",
"\n",
"class Test(object):\n",
" def test_is_rotation(self, func):\n",
" assert_equal(func('o', 'oo'), False)\n",
" assert_equal(func(None, 'foo'), False)\n",
" assert_equal(func('', 'foo'), False)\n",
" assert_equal(func('', ''), True)\n",
" assert_equal(func('foobarbaz', 'barbazfoo'), True)\n",
" print('Success: test_is_rotation')\n",
"\n",
"if __name__ == '__main__':\n",
" test = Test()\n",
" test.test_is_rotation(is_rotation)"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 2",
"language": "python",
"name": "python2"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 2
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython2",
"version": "2.7.10"
}
},
"nbformat": 4,
"nbformat_minor": 0
}

View File

View File

View File

@@ -0,0 +1,250 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"<small><i>This notebook was prepared by [Donne Martin](http://donnemartin.com). Source and license info is on [GitHub](https://bit.ly/code-notes).</i></small>"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Problem: Implement an algorithm to determine if a string has all unique characters\n",
"\n",
"* [Constraints and Assumptions](#Constraints-and-Assumptions)\n",
"* [Test Cases](#Test-Cases)\n",
"* [Algorithm 1: Sets and Length Comparison](#Algorithm-1:-Sets-and-Length-Comparison)\n",
"* [Code: Sets and Length Comparison](#Code:-Sets-and-Length-Comparison)\n",
"* [Algorithm 2: Hash Map Lookup](#Algorithm-2:-Hash-Map-Lookup)\n",
"* [Code: Hash Map Lookup](#Code:-Hash-Map-Lookup)\n",
"* [Algorithm 3: In-Place](#Algorithm-3:-In-Place)\n",
"* [Code: In-Place](#Code:-In-Place)\n",
"* [Unit Test](#Unit-Test)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Constraints and Assumptions\n",
"\n",
"*Problem statements are often intentionally ambiguous. Identifying constraints and stating assumptions can help to ensure you code the intended solution.*\n",
"\n",
"* Can I assume the string is ASCII?\n",
" * Yes\n",
" * Note: Unicode strings could require special handling depending on your language\n",
"* Can you use additional data structures? \n",
" * Yes"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Test Cases\n",
"\n",
"* '' -> True\n",
"* 'foo' -> False\n",
"* 'bar' -> True"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Algorithm 1: Sets and Length Comparison\n",
"\n",
"A set is an unordered collection of unique elements. \n",
"\n",
"* If the length of the set(string) equals the length of the string\n",
" * Return True\n",
"* Else\n",
" * Return False\n",
" \n",
"Complexity:\n",
"* Time: O(n)\n",
"* Space: Additional O(m), where m is the number of unique characters in the set"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Code: Sets and Length Comparison"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"def unique_chars(string):\n",
" return len(set(string)) == len(string)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Algorithm 2: Hash Map Lookup\n",
"\n",
"We'll keep a hash map (set) to keep track of unique characters we encounter. \n",
"\n",
"Steps:\n",
"* Scan each character\n",
"* For each character:\n",
" * If the character does not exist in a hash map, add the character to a hash map\n",
" * Else, return False\n",
"* Return True\n",
"\n",
"Notes:\n",
"* We could also use a dictionary, but it seems more logical to use a set as it does not contain duplicate elements\n",
"* Since the characters are in ASCII, we could potentially use an array of size 128 (or 256 for extended ASCII)\n",
"\n",
"Complexity:\n",
"* Time: O(n)\n",
"* Space: Additional O(m), where m is the number of unique characters in the hash map"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Code: Hash Map Lookup"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"def unique_chars_hash(string):\n",
" chars_set = set()\n",
" for char in string:\n",
" if char in chars_set:\n",
" return False\n",
" else:\n",
" chars_set.add(char)\n",
" return True"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Algorithm 3: In-Place\n",
"\n",
"Assume we cannot use additional data structures, which will eliminate the fast lookup O(1) time provided by our hash map. \n",
"* Scan each character\n",
"* For each character:\n",
" * Scan all [other] characters in the array\n",
" * Exluding the current character from the scan is rather tricky in Python and results in a non-Pythonic solution\n",
" * If there is a match, return False\n",
"* Return True\n",
"\n",
"Algorithm Complexity:\n",
"* Time: O(n^2)\n",
"* Space: In-place"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Code: In-Place"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"def unique_chars_inplace(string):\n",
" for char in string:\n",
" if string.count(char) > 1:\n",
" return False\n",
" return True"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Unit Test"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"*It is important to identify and run through general and edge cases from the [Test Cases](#Test-Cases) section by hand. You generally will not be asked to write a unit test like what is shown below.*"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Success: test_unique_chars\n",
"Success: test_unique_chars\n",
"Success: test_unique_chars\n"
]
}
],
"source": [
"from nose.tools import assert_equal\n",
"\n",
"class Test(object):\n",
" def test_unique_chars(self, func):\n",
" assert_equal(func(''), True)\n",
" assert_equal(func('foo'), False)\n",
" assert_equal(func('bar'), True)\n",
" print('Success: test_unique_chars')\n",
"\n",
"if __name__ == '__main__':\n",
" test = Test()\n",
" test.test_unique_chars(unique_chars)\n",
" test.test_unique_chars(unique_chars_hash)\n",
" test.test_unique_chars(unique_chars_inplace)"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 2",
"language": "python",
"name": "python2"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 2
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython2",
"version": "2.7.10"
}
},
"nbformat": 4,
"nbformat_minor": 0
}