mirror of
https://github.com/donnemartin/interactive-coding-challenges.git
synced 2024-03-22 13:11:13 +08:00
c6677586d3
Update input check logic to check for an empty but not None entry.
316 lines
7.8 KiB
Python
316 lines
7.8 KiB
Python
{
|
|
"cells": [
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"This notebook was prepared by [Donne Martin](http://donnemartin.com). Source and license info is on [GitHub](https://github.com/donnemartin/interactive-coding-challenges)."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"# Solution Notebook"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"## Problem: Implement a function to reverse a string (a list of characters), in-place.\n",
|
|
"\n",
|
|
"* [Constraints](#Constraints)\n",
|
|
"* [Test Cases](#Test-Cases)\n",
|
|
"* [Algorithm](#Algorithm)\n",
|
|
"* [Code](#Code)\n",
|
|
"* [Pythonic-Code](#Pythonic-Code)\n",
|
|
"* [Unit Test](#Unit-Test)\n",
|
|
"* [Bonus C Algorithm](#Bonus-C-Algorithm)\n",
|
|
"* [Bonus C Code](#Bonus-C-Code)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"## Constraints\n",
|
|
"\n",
|
|
"* Can we assume the string is ASCII?\n",
|
|
" * Yes\n",
|
|
" * Note: Unicode strings could require special handling depending on your language\n",
|
|
"* Since we need to do this in-place, it seems we cannot use the slice operator or the reversed function?\n",
|
|
" * Correct\n",
|
|
"* Since Python string are immutable, can we use a list of characters instead?\n",
|
|
" * Yes"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"## Test Cases\n",
|
|
"\n",
|
|
"* None -> None\n",
|
|
"* [''] -> ['']\n",
|
|
"* ['f', 'o', 'o', ' ', 'b', 'a', 'r'] -> ['r', 'a', 'b', ' ', 'o', 'o', 'f']"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"## Algorithm\n",
|
|
"\n",
|
|
"Since Python strings are immutable, we'll use a list of chars instead to exercise in-place string manipulation as you would get with a C string.\n",
|
|
"\n",
|
|
"* Iterate len(string)/2 times, starting with i = 0:\n",
|
|
" * Swap i and len(string) - 1 - i\n",
|
|
" * Increment i\n",
|
|
"\n",
|
|
"Complexity:\n",
|
|
"* Time: O(n)\n",
|
|
"* Space: O(1)\n",
|
|
"\n",
|
|
"Note:\n",
|
|
"* You could use a byte array instead of a list to do in-place string operations"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"## Code"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 1,
|
|
"metadata": {
|
|
"collapsed": false
|
|
},
|
|
"outputs": [],
|
|
"source": [
|
|
"from __future__ import division\n",
|
|
"\n",
|
|
"\n",
|
|
"class ReverseString(object):\n",
|
|
"\n",
|
|
" def reverse(self, chars):\n",
|
|
" if chars is None or not chars:\n",
|
|
" return chars\n",
|
|
" size = len(chars)\n",
|
|
" for i in range(size//2):\n",
|
|
" chars[i], chars[size-1-i] = \\\n",
|
|
" chars[size-1-i], chars[i]\n",
|
|
" return chars"
|
|
]
|
|
},
|
|
{
|
|
"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. Note these solutions are not in-place."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 2,
|
|
"metadata": {
|
|
"collapsed": false
|
|
},
|
|
"outputs": [],
|
|
"source": [
|
|
"class ReverseStringAlt(object):\n",
|
|
"\n",
|
|
" def reverse_string_alt(string):\n",
|
|
" if string is None or not string:\n",
|
|
" return string\n",
|
|
" return string[::-1]\n",
|
|
"\n",
|
|
" def reverse_string_alt2(string):\n",
|
|
" if string is None or not string:\n",
|
|
" return string\n",
|
|
" return ''.join(reversed(string))"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"## Unit Test"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 3,
|
|
"metadata": {
|
|
"collapsed": false
|
|
},
|
|
"outputs": [
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"Overwriting test_reverse_string.py\n"
|
|
]
|
|
}
|
|
],
|
|
"source": [
|
|
"%%writefile test_reverse_string.py\n",
|
|
"from nose.tools import assert_equal\n",
|
|
"\n",
|
|
"\n",
|
|
"class TestReverse(object):\n",
|
|
"\n",
|
|
" def test_reverse(self, func):\n",
|
|
" assert_equal(func(None), None)\n",
|
|
" assert_equal(func(['']), [''])\n",
|
|
" assert_equal(func(\n",
|
|
" ['f', 'o', 'o', ' ', 'b', 'a', 'r']),\n",
|
|
" ['r', 'a', 'b', ' ', 'o', 'o', 'f'])\n",
|
|
" print('Success: test_reverse')\n",
|
|
"\n",
|
|
" def test_reverse_inplace(self, func):\n",
|
|
" target_list = ['f', 'o', 'o', ' ', 'b', 'a', 'r']\n",
|
|
" func(target_list)\n",
|
|
" assert_equal(target_list, ['r', 'a', 'b', ' ', 'o', 'o', 'f'])\n",
|
|
" print('Success: test_reverse_inplace')\n",
|
|
"\n",
|
|
"\n",
|
|
"def main():\n",
|
|
" test = TestReverse()\n",
|
|
" reverse_string = ReverseString()\n",
|
|
" test.test_reverse(reverse_string.reverse)\n",
|
|
" test.test_reverse_inplace(reverse_string.reverse)\n",
|
|
"\n",
|
|
"\n",
|
|
"if __name__ == '__main__':\n",
|
|
" main()"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 4,
|
|
"metadata": {
|
|
"collapsed": false
|
|
},
|
|
"outputs": [
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"Success: test_reverse\n",
|
|
"Success: test_reverse_inplace\n"
|
|
]
|
|
}
|
|
],
|
|
"source": [
|
|
"%run -i test_reverse_string.py"
|
|
]
|
|
},
|
|
{
|
|
"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 3",
|
|
"language": "python",
|
|
"name": "python3"
|
|
},
|
|
"language_info": {
|
|
"codemirror_mode": {
|
|
"name": "ipython",
|
|
"version": 3
|
|
},
|
|
"file_extension": ".py",
|
|
"mimetype": "text/x-python",
|
|
"name": "python",
|
|
"nbconvert_exporter": "python",
|
|
"pygments_lexer": "ipython3",
|
|
"version": "3.5.0"
|
|
}
|
|
},
|
|
"nbformat": 4,
|
|
"nbformat_minor": 0
|
|
}
|