├── Chapter 1.md
├── Chapter 2.md
├── Chapter 4.md
├── Chapter 5.md
├── README.md
├── fig
├── C-4.14_1.gif
├── C-4.14_2.png
└── R-3.1.png
├── scr
├── Ch1and2.py
├── Ch4.py
└── Ch5.py
└── 习题参考答案
├── ch01.pdf
└── ch03.pdf
/Chapter 1.md:
--------------------------------------------------------------------------------
1 | - **R-1.1** 实现函数 is_multiple(n, m), n和m为整数,如果n是m的倍数则返回True,否则返回False.
2 |
3 | ~~~python
4 | def is_multiple(n, m):
5 | if n % m == 0:
6 | return True
7 | else:
8 | return False
9 | ~~~
10 |
11 | - **R-1.2** 实现函数 is_even(k), 判断整数k是否为偶数,不能使用乘法,除法或者取余数操作.
12 |
13 | 思路:使用位运算
14 |
15 | ~~~python
16 | def is_even(k):
17 | if k & 1 == 0:
18 | return True
19 | else:
20 | return False
21 | ~~~
22 |
23 | - **R-1.3** 实现函数 minmax(data), 找出序列data中的最大数和最小数,并以长度为2的元组返回。不能使用内置函数max和min。
24 |
25 | ```python
26 | def maxmin(data):
27 | if data:
28 | m = n = data[0]
29 | for i in data:
30 | if i > m:
31 | m = i
32 | if i < n:
33 | n = i
34 | return (m,n)
35 | else:
36 | print("序列为空!")
37 | ```
38 |
39 | - **R-1.4** 编写函数,用来接受正整数n,返回1~n的平方和.
40 |
41 | ~~~python
42 | def square_sum(n):
43 | result = 0
44 | for i in range(n+1):
45 | result += i**2
46 | return result
47 | ~~~
48 |
49 | - **R-1.5** 使用解析语法和sum函数实现R-1.4
50 |
51 | ~~~python
52 | def square_sum2(n):
53 | return sum([i**2 for i in range(n+1)])
54 | ~~~
55 |
56 | - **R-1.6** 编写函数,实现接受正整数n,返回1~n中所有奇数平方和.
57 |
58 | ~~~python
59 | def odd_square_sum(n):
60 | return sum([i**2 for i in range(n+1) if i&1==1])
61 | ~~~
62 |
63 | - **R-1.7** 同上
64 |
65 | - **R-1.8** 长度为n的字符串s, 当索引值为 -n ≤ k ≤ 0 时,所指元素为s[k],那么求一个正整数索引值 j⩾ 0, 使得s[j]指向的也是相同的元素、
66 |
67 | j = n-k
68 |
69 | - **R-1.9** 生成50,60,70,80的排列。
70 |
71 | ~~~python
72 | a = list(range(50,90,10))
73 | ~~~
74 |
75 | - **R-1.10** 生成8,6,4,2,0,-2,-4,-6,-8的排列。
76 |
77 | ~~~python
78 | a = list(range(8,-10,-2))
79 | ~~~
80 |
81 | - **R-1.11** 使用列表解析生成列表[1,2,4,8,16,32,64,128,256].
82 |
83 | ~~~python
84 | a = [2**i for i in range(10)]
85 | ~~~
86 |
87 | - **R-1.12** 使用randrange函数,实现自己的choice函数.
88 |
89 | ~~~python
90 | def my_choice(data):
91 | return data[random.randrange(len(data))]
92 | ~~~
93 |
94 | - **C-1.13** 逆置n个整数的列表
95 |
96 | ~~~python
97 | a = [1,12,31,4,13,4]
98 | b=[]
99 | for i in range(len(a)-1,-1,-1):
100 | print(i)
101 | b.append(a[i])
102 | a.reverse()
103 | print(b)
104 | print(a)
105 | ~~~
106 |
107 | - **C-1.14** 编写函数,判断一个整数序列中是否存在一对乘积是奇数的互不相同的数。
108 |
109 | ~~~python
110 | def fun14(data):
111 | for i in data:
112 | for j in data:
113 | if i != j and i*j % 2 != 0:
114 | return True
115 | return False
116 | ~~~
117 |
118 | - **C-1.15** 编写函数,判断一个数字序列是否所有数字都互不相同。
119 |
120 | ~~~python
121 | def fun15(data):
122 | n = len(data)
123 | m = len(set(data))
124 | if n==m:
125 | return True
126 | else:
127 | return False
128 | ~~~
129 |
130 | - **C-1.16** 如下scale函数,循环体内执行命令data[j] *= factor。我们已经说过这个数字类型是不可变的,操作符 *= 在这种背景下使用时创建了一个新实例(而不是现有实例的变化)。那么scale函数是如何改变调用者发送的实际参数呢?
131 |
132 | ~~~python
133 | def scale(data, factor):
134 | for j in range(len(data)):
135 | data[j] *= factor
136 | grade = [1,2,3]
137 | scale(grade, 2)
138 | print(grade)
139 |
140 | OUT:[2,4,6]
141 | ~~~
142 |
143 | [参考:python中list作函数形参,如何防止被实参修改](https://blog.csdn.net/qq_17753903/article/details/82886625)
144 |
145 | 在python中数据有两种类型,mutable(可变) 和 immutable (不可变)st ,dict是mutable的;int , string , float ,tuple是inmutable 的。在函数参数的传递过程中:对于inmutable object ,函数参数传递是值对于mutable object,函数参数传递是指针。因此当我们把grade传入scale函数时,实际上把它的指针传给了data变量,所以grade的值会随着data的值变化。**如果传递给函数的是可变序列,并且在函数内部使用下标或可变序列自身的方法增加、删除元素或修改元素时,修改后的结果是可以反映到函数之外的,实参也得到相应的修改。**但如果,scale函数内写成data = data * factor, 这时grade并不会被改变。
146 |
147 | - **C-1.17**
148 |
149 | 如果上题中的scale函数如下,能正常工作吗?
150 |
151 | ~~~python
152 | def scale(data, factor):
153 | for val in data:
154 | val *= factor
155 | ~~~
156 |
157 | 不能。理由见上题。
158 |
159 | - **C-1.18** 使用列表解析产生[0,2,6,12,20,30,42,56,72,90]
160 |
161 | ~~~python
162 | [i*(i-1) for i in range(1,11)]
163 | ~~~
164 |
165 | - **C-1.19** 在不输入26个英文字母的情况下产生列表['a','b',...,'z']
166 |
167 | ~~~python
168 | [i*(i-1) for i in range(97,123)]
169 | ~~~
170 |
171 | - **C-1-20** 只使用randint()函数实现shuffle()函数。
172 |
173 | 解法1:Fisher-Yates Shuffle: 从原始数据中随机抽取一个新的数字到新的数组中。O(n^2^)
174 |
175 | ~~~python
176 | def shuffe1(data):
177 | result = []
178 | while data:
179 | p = random.randint(0,len(data)-1)
180 | result.append(data[p])
181 | data.pop(p)
182 | return result
183 | ~~~
184 |
185 | 解法2:Knuth-Durstenfeld Shuffle:原地打乱顺序算法,每次从未处理的数字中取一个放到尾部。 O(n)
186 |
187 | ```python
188 | def shuffle2(data):
189 | for i in range(len(data)-1,0,-1):
190 | p = random.randint(0,i)
191 | data[i], data[p] = data[p], data[i]
192 | return data
193 | ```
194 |
195 | 解法3:Inside-Out:解法2改变了原始数据,这种方法不会改变原始数据。
196 |
197 | ~~~python
198 | def shuffle3(data):
199 | result = data[:]
200 | for i in range(1,len(data)):
201 | j = random.randint(0,i)
202 | result[i] = result[j]
203 | result[j] = data[i]
204 | return result
205 | ~~~
206 |
207 | - **C-1.21** 反复从标准输入读取一行直到抛出EOFerror异常,然后以相反的顺序输出这些行。
208 |
209 | ~~~python
210 | input_line = []
211 | while True:
212 | try:
213 | input_line.append(input())
214 | except EOFError:
215 | input_line.reverse()
216 | print(input_line)
217 | break
218 | ~~~
219 |
220 | 注意:windows下输入ctrl+z触发EOFerror异常
221 |
222 | - **C-1.22** 求长度都为n的两个数组的点积
223 |
224 | ~~~python
225 | def dot(a, b):
226 | if len(a)!=len(b):
227 | print("两个数组长度不相等!")
228 | return
229 | else:
230 | return [a[i]*b[i] for i in range(len(a))]
231 | ~~~
232 |
233 | - **C-1.23** 列表索引越界异常捕捉。
234 |
235 | ~~~python
236 | a = [1,2,3]
237 | for i in range(len(a)+1):
238 | try:
239 | print(a[i])
240 | except IndexError:
241 | print("Don't try buffer overflow attacks in Python")
242 | ~~~
243 |
244 | - **C-1.24** 编写函数,求所给字符串中元音字母的个数。
245 |
246 | ~~~python
247 | vowel = ['a', 'e', 'i', 'o', 'u']
248 | line = "haiafjaneawdiuhawoehoiasjdoihbjkfanowij"
249 | num = 0
250 | for i in line:
251 | if i in vowel:
252 | num += 1
253 | print(num)
254 | ~~~
255 |
256 | - **C-1.25** 删除句中标点。
257 |
258 | 参考:[Python文本处理——中文标点符号处理](https://www.cnblogs.com/arkenstone/p/6092255.html)
259 |
260 | ~~~python
261 | punctuation = string.punctuation
262 | sentence = "how are you? ,. I'm fine!"
263 | result = re.sub("[%s]+" % punctuation, "", sentence)
264 | print(result)
265 | ~~~
266 |
267 | - **C-1.26** 在控制台输入3个整数,并确定他们是否可以在一个正确的算法公式(给定顺序)下成立。
268 |
269 | ~~~python
270 | a,b,c = input("请输入三个整数:")
271 | a,b,c = int(a), int(b), int(c)
272 | if a + b == c:
273 | print("{}+{}={}".format(a,b,c))
274 | if a - b == c:
275 | print("{}-{}={}".format(a,b,c))
276 | if a / b == c:
277 | print("{}/{}={}".format(a,b,c))
278 | if a * b == c:
279 | print("{}*{}={}".format(a,b,c))
280 | ~~~
281 |
282 | - **C-1.27** 使用生成器求整数因子。
283 |
284 | ~~~python
285 | def funC27(n):
286 | k = 1
287 | temp = []
288 | while k * k < n:
289 | if n % k == 0:
290 | yield k
291 | temp.append(n//k)
292 | k += 1
293 | if k * k == n:
294 | yield k
295 | for i in reversed(temp):
296 | yield i
297 | ~~~
298 |
299 | - **C-1.28** 编写函数,求向量的p范数
300 |
301 | ~~~python
302 | def norm(v ,p):
303 | return sum([i**p for i in v])**(1/p)
304 | ~~~
305 |
306 | - **P-1.29** 排列问题
307 |
308 | [参考](https://blog.csdn.net/Deeven123/article/details/82970560)
309 |
310 | ~~~python
311 | def Perm_k(arrs, k):
312 | # 若输入 [1,2,3],则先取出1这个元素,将剩余的 [2,3]中取出另一个元素得到 [[1,2],[1,3]]
313 | # 同理,取出2或者3时,得到的分别是 [[2,1],[2,3]]和 [[3,1],[3,2]]
314 | if len(arrs)==1:
315 | return [arrs]
316 | if k==1:
317 | return list(map(lambda s:[s], arrs)) # 当 k 为 1 时,每(单)个元素都可以被选取
318 | result = [] # 最终的结果(即全排列的各种情况)
319 | for i in range(len(arrs)):
320 | rest_arrs = arrs[:i]+arrs[i+1:] # 取出arrs中的第 i个元素后剩余的元素
321 | rest_lists = Perm_k(rest_arrs, k-1) # 剩余的元素选取 k-1元素
322 | lists = []
323 | for term in rest_lists:
324 | lists.append(arrs[i:i+1]+term) # 将取出的第 i个元素加到剩余全排列的前面
325 | result += lists
326 | return result
327 |
328 | result = []
329 | data = ['c', 'a', 't', 'd', 'o', 'g']
330 | for i in range(1, len(data)+1):
331 | temp = Perm_k(data, i)
332 | result.append([''.join(item) for item in temp])
333 | print([j for i in result for j in i])
334 | ~~~
335 |
336 |
337 |
338 | - **P-1.30** 编写程序,输入一个大于2的正整数,求将该数反复被2整除直到商小于2为止的次数。
339 |
340 | ```python
341 | def funC30(n):
342 | if n <= 0:
343 | print("请输入一个正整数!")
344 | return
345 | i = 0
346 | while n >= 2:
347 | n = n // 2
348 | i += 1
349 | return i
350 | ```
351 |
352 | - **P-1.31** 编写找零钱的程序。
353 |
354 | [舍入误差](https://stackoverflow.com/questions/14763722/python-modulo-on-floats)
355 |
356 | ~~~python
357 | def funC31(price, pay):
358 | money = [100, 50, 20, 10, 5, 1, 0.5, 0.1]
359 | cash = {}
360 | # 由于python存在舍入误差(rounding errors),所以弄成整数以后再算
361 | temp = (pay - price) * 10
362 | if price < pay:
363 | for i in money:
364 | quotient = temp // (i*10)
365 | remainder = round(temp % (i*10))
366 | cash[i] = quotient
367 | if remainder == 0:
368 | break
369 | else:
370 | temp = remainder
371 | for key, value in cash.items():
372 | if value != 0:
373 | print("找零 {} 张 {} 元人民币".format(int(value), key))
374 | elif price == pay:
375 | print("刚好支付!")
376 | else:
377 | print("请支付足够金额!")
378 | ~~~
379 |
380 | - ***P-1.32** 计算器
381 |
382 | ~~~python
383 | while True:
384 | a = int(input())
385 | symbol = input()
386 | b = int(input())
387 | if symbol=="+":
388 | c = a + b
389 | if symbol=="-":
390 | c = a - b
391 | if symbol=="*":
392 | c = a * b
393 | if symbol=="÷":
394 | c = a / b
395 | equal = input()
396 | if equal=="=":
397 | print(c)
398 | continue
399 | ~~~
400 |
401 | - **P1-1.35** 生日悖论
402 |
403 | ~~~python
404 | def funP35(n):
405 | t = 0
406 | f = 0
407 | for p in range(100):
408 | birthdays = [random.choice([j for j in range(365)]) for i in range(n)]
409 | if len(birthdays) != len(set(birthdays)):
410 | t += 1
411 | else:
412 | f += 1
413 | print(t/100)
414 | ~~~
415 |
416 | - **P-1.36** 词频统计
417 |
418 | ~~~python
419 | def funP36():
420 | words = "is a word like it is the the is word banana apple word is apple"
421 | count = {}
422 | words_list = words.split(" ")
423 | for word in words_list:
424 | count[word] = 1 + count.get(word, 0)
425 | print(count)
426 | ~~~
427 |
428 |
429 |
430 |
--------------------------------------------------------------------------------
/Chapter 2.md:
--------------------------------------------------------------------------------
1 | - **R-3.1** 画出函数8n, 4nlog n, 2n^2^ , n^3^ , 2^n^ 的函数图形。
2 |
3 | 
4 |
5 |
--------------------------------------------------------------------------------
/Chapter 4.md:
--------------------------------------------------------------------------------
1 | # 递归
2 |
3 | ## 例题
4 |
5 | > 递归是一种技术,这种技术通过一个函数在执行过程中一次或者多次调用其本身,或者通过一种数据结构在其表示中依赖于相同类型的结构更小的实例
6 |
7 | 注:函数调用时局部变量和执行位置信息压栈,调用结束后恢复。
8 |
9 |
10 |
11 | 例1:阶乘函数的递归实现
12 |
13 | ~~~python
14 | def factoria(n):
15 | if n==0:
16 | return 1
17 | else:
18 | return n * factoria(n-1)
19 | ~~~
20 |
21 | 例2:英式标尺 (二路递归)
22 |
23 | ~~~python
24 | def draw_line(tick_length, tick_label=''):
25 | line = '-' * tick_length
26 | if tick_label:
27 | line += ' ' + tick_label
28 | print(line)
29 |
30 | def draw_interval(center_length):
31 | if center_length > 0:
32 | draw_interval(center_length - 1)
33 | draw_line(center_length)
34 | draw_interval(center_length - 1)
35 |
36 | def draw_ruler(num_inches, major_length):
37 | draw_line(major_length, '0')
38 | for i in range(1, num_inches+1):
39 | draw_interval(major_length - 1)
40 | draw_line(major_length, str(i))
41 |
42 | if __name__ == "__main__":
43 | draw_ruler(1,5)
44 | ~~~
45 |
46 | 例3:二分查找(尾递归)
47 |
48 | ~~~python
49 | def binary_search(data, target, low, high):
50 | if low > high:
51 | return False
52 | else:
53 | mid = (low + high) // 2
54 | if target == data[mid]:
55 | return True
56 | elif target < data[mid]:
57 | return binary_search(data, target, low, mid - 1)
58 | else:
59 | return binary_search(data, target, mid + 1, high)
60 | ~~~
61 |
62 | 例4:磁盘使用情况 (多重递归)
63 |
64 | ~~~python
65 | def disk_usage(path):
66 | total = os.path.getsize(path)
67 | if os.path.isdir(path):
68 | for filename in os.listdir(path):
69 | childpath = os.path.join(path, filename)
70 | total += disk_usage(childpath)
71 | print('{0:<7}'.format(total), path)
72 | ~~~
73 |
74 | 例5:斐波拉契数列的递归实现
75 |
76 | ~~~python
77 | def good_fibonacci(n):
78 | if n <= 1:
79 | print(0)
80 | return (0, n)
81 | else:
82 | (a, b) = good_fibonacci(n - 1)
83 | print(b)
84 | return (b, a+b)
85 | ~~~
86 |
87 | 例6:递归求幂
88 |
89 | ~~~python
90 | def power(x, n):0
91 | if n == 0:
92 | return 1
93 | else:
94 | partial = power(x, n//2)
95 | total = partial * partial
96 | if n % 2 == 1:
97 | total *= x
98 | return total
99 | ~~~
100 |
101 |
102 |
103 | > 修改递归深度的方法:
104 | >
105 | > ~~~python
106 | > import sys
107 | > old = sys.getrecursionlimit()
108 | > sys.setrecursionlimit(100000)
109 | > ~~~
110 |
111 | ## 递归算法的形式
112 |
113 | - *对于基本情况的测试* 。首先测试一组基本情况(至少应该有一个)。每个可能的递归调用链最终都会达到一种基本情况,即终止条件,并且每个基本情况的处理都不应该使用递归。
114 | - *递归*。 如果不是一种基本情况,则执行一个或者多个递归调用。这个递归步骤可能包括一个测试,该测试决定执行哪几种可能的递归调用。这些递归调用会向一种基本情况靠近。
115 |
116 | ## 递归问题时间、空间复杂度估算
117 |
118 | - 时间复杂度
119 |
120 | Master公式:
121 |
122 | 这个方法针对形如:T(n) = aT(n/b) + f(n^d^)的递归方程。这种递归方程是分治法的时间复杂性所满足的递归关系,即一个规模为n的问题被分成规模。**均为**n/b的a个子问题,递归地求解这a个子问题,然后通过对这a个子问题的解的综合,得到原问题的解。这种方法是对于分治问题最好的解法,我们先给出如下的公式:
123 |
124 | ①当dlogb a时,时间复杂度为O(n^d)
127 |
128 | 公式记忆:我们实际上是比较n^logb a和f(n)的阶,如果他们不等,那么T(n)取他们中的较大者,如果他们的阶相等,那么我们就将他们的任意一个乘以logn就可以了。
129 |
130 | 空间复杂度
131 |
132 | - 空间复杂度:**不是计算实际占用的空间,而是计算整个算法的辅助空间单元的个数,与问题的规模没有关系**(简单理解就是算法执行时创建的变量(包括临时变量)个数)
133 |
134 | ①忽略常数,用O(1)表示
135 | ②递归算法的空间复杂度=递归深度N*每次递归所要的辅助空间
136 | ③对于单线程来说,递归有运行时堆栈,求的是递归最深的那一次压栈所耗费的空间的个数,因为递归最深的那一次所耗费的空间足以容纳它所有递归过程。递归是要返回上一层的,所以它所需要的空间不是一直累加起来的
137 |
138 | [参考][https://blog.csdn.net/w_y_x_y/article/details/78733667]
139 |
140 | ## 习题
141 |
142 | **R-4.1** 对于一个含有n个元素的序列S, 描述一个递归算法查找其最大值。所给算法的时间复杂度和空间复杂度各是多少?
143 |
144 | ~~~python
145 | def find_max(data):
146 | if len(data) == 1:
147 | return data[0]
148 | elif len(data) == 2:
149 | return data[0] if data[0] > data[1] else data[1]
150 | else:
151 | mid = len(data) // 2
152 | max_left = find_max(data[:mid])
153 | max_right = find_max(data[mid:])
154 | return max_left if max_left > max_right else max_right
155 | ~~~
156 |
157 | 根据master公式,该算法的时间复杂度为 O(n)
158 |
159 | **R-4.6** 递归求第n个调和数 $H_{n}=\sum_{i=1}^{n}1/i$
160 |
161 | ~~~python
162 | def harmonic(n):
163 | if n == 1:
164 | return 1
165 | else:
166 | return 1/n + harmonic(n-1)
167 | ~~~
168 |
169 | **R-4.7** 使用递归的方法,将一串数字转换成对应的整数。
170 |
171 | ~~~python
172 | def string_to_integer(string):
173 | if len(string) == 1:
174 | return int(string[0])
175 | else:
176 | return int(string[0]) * 10 ** (len(string) - 1) + string_to_integer(string[1:])
177 |
178 | if __name__ == "__main__":
179 | string = ['1', '3', '4', '3']
180 | print(string_to_integer(string))
181 | ~~~
182 |
183 | **R-4.8**
184 |
185 | 此方法将规模为n的计算,均分为 规模为2的子问题,一共n/2个,根据master公式,时间复杂度为O(n)
186 |
187 |
188 |
189 | **C-4.9** 递归的方法查找一个序列中的最大值和最小值
190 |
191 | ~~~python
192 | def find_min_and_max(data):
193 | if len(data) == 1: # 只有一个元素
194 | return (data[0], data[0])
195 | elif len(data) == 2:
196 | max_num = data[0] if data[0] > data[1] else data[1]
197 | min_num = data[0] if data[0] < data[1] else data[1]
198 | return (max_num, min_num)
199 | else:
200 | mid = len(data) // 2
201 | max_num_left, min_num_left = find_min_and_max(data[:mid])
202 | max_num_right, min_num_right = find_min_and_max(data[mid:])
203 | max_ = max_num_left if max_num_left > max_num_right else max_num_right
204 | min_ = min_num_left if min_num_left < min_num_right else min_num_right
205 | return (max_, min_)
206 | ~~~
207 |
208 | **C-4.10** 在只使用加法和整除的情况下,使用递归计算以2为底的n的对数的整数部分。
209 |
210 | ~~~python
211 | def funR10(n, i=0):
212 | if 2**i >= n:
213 | return i
214 | else:
215 | return funR10(n, i+1)
216 | ~~~
217 |
218 | **C-4.11** 使用递归函数求解元素的唯一性,在不使用排序的最坏的情况下运行时间最多是O(n^2^)
219 |
220 | ~~~python
221 | def funC11(data):
222 | if len(data) == 1:
223 | return True
224 | else:
225 | for j in data[1:]:
226 | if data[0] == j:
227 | return False
228 | return funC11(data[1:])
229 | ~~~
230 |
231 | **C-4.12** 在只使用加减法的情况下,递归求两个正整数m和n的乘积。
232 |
233 | ~~~python
234 | def funC12(m, n):
235 | if m == 1:
236 | return n
237 | else:
238 | return n + funC12(m - 1, n)
239 | ~~~
240 |
241 | **C-4.14** 汉罗塔问题
242 |
243 | ~~~python
244 | def hanoi_tower(n, a, b, c):
245 | """
246 | :param n: 碟子数
247 | :param a: 碟子摞
248 | :param b: 辅助摞
249 | :param c: 目标摞
250 | """
251 | def move(a, x, c):
252 | print("{}>>>_{}_>>>{}".format(a, x, c))
253 | if n == 1:
254 | move(a, n,c)
255 | else:
256 | hanoi_tower(n-1, a, c, b)
257 | move(a, n, c)
258 | hanoi_tower(n -1, b, a, c)
259 |
260 | if __name__ == "__main__":
261 | hanoi_tower(4, 'a', 'b', 'c')
262 | ~~~
263 |
264 | 
265 |
266 | 
267 |
268 | **C-4.15** 递归实现,输出一个含有n个元素的集合的所有子集(没有任何重复的子集)
269 |
270 | ~~~python
271 | def subset(data, k):
272 | if len(data) == 1:
273 | return [data]
274 | if k == 1:
275 | return [[i] for i in data]
276 | result = []
277 | for j in range(len(data)):
278 | if len(data[j+1:]) >= k-1:
279 | rest_data = subset(data[j+1:], k-1)
280 | lists = []
281 | for r in rest_data:
282 | lists.append(data[j:j+1] + r)
283 | result += lists
284 | return result
285 | if __name__ == "__main__":
286 | data = [1,2,3,4]
287 | result = []
288 | for k in range(1, len(data)+1):
289 | lists = subset(data, k)
290 | result += lists
291 | print(result)
292 | ~~~
293 |
294 | **C-4.16** 递归实现,逆置字符串。
295 |
296 | ~~~python
297 | def reverse_string(string, start, stop):
298 | if start > stop:
299 | return
300 | else:
301 | string[start], string[stop] = string[stop], string[start]
302 | reverse_string(string, start+1, stop-1)
303 | return string
304 |
305 | if __name__ == "__main__":
306 | string = list('snap&stop')
307 | print(''.join(reverse_string(string, 0 , len(string)-1)))
308 | ~~~
309 |
310 | **C-4.17** 递归实现,确定一个字符串s是否是它的一个回文字符串。
311 |
312 | ~~~python
313 | def is_huiwen(string, start, stop):
314 | if string[start] != string[stop]:
315 | return False
316 | elif start == stop or start == stop -1:
317 | return True
318 | else:
319 | return is_huiwen(string, start+1, stop-1)
320 | if __name__ == "__main__":
321 | string = list('abccba')
322 | print(is_huiwen(string,0,len(string)-1))
323 | ~~~
324 |
325 | **C-4.18** 递归实现,确定字符串s中是否元音字母比辅音字母多。
326 |
327 | ~~~python
328 | def C18(s, start, stop):
329 | if start == stop - 1:
330 | if s[start] in vowels:
331 | return 1
332 | elif s[start] in consonants:
333 | return -1
334 | else:
335 | return 0
336 | else:
337 | mid = (start + stop) // 2
338 | return C18(s, start, mid) + C18(s, mid, stop)
339 |
340 |
341 | if __name__ == "__main__":
342 | letters = {chr(i) for i in range(65, 91)} | {chr(i) for i in range(97, 123)}
343 | vowels = {'a', 'e', 'i', 'o', 'u'} | {'A', 'E', 'I', 'O', 'U'}
344 | consonants = letters - vowels
345 | s = list('FAIOJIOWAHFOgioqoinnaoanfoe&87*12JEIQEOQNFN')
346 | result = C17(s, 0, len(s))
347 | if result > 0:
348 | print("元音字母多")
349 | elif result == 0:
350 | print("一样多")
351 | else:
352 | print("辅音字母多")
353 | ~~~
354 |
355 | **C-4.19** 递归实现,重新排列一个整数值序列,使得所有欧数值出现在所有奇数值前面。
356 |
357 | ~~~python
358 | def separate_odd_even(data):
359 | if len(data) == 1:
360 | return [data[0]]
361 |
362 | for i in range(len(data)):
363 | if data[i] % 2 == 0:
364 | return [data[i]] + separate_odd_even(data[i+1:])
365 | else:
366 | return separate_odd_even(data[i+1:]) + [data[i]]
367 |
368 | if __name__ == "__main__":
369 | data = [1,2,3,10,4,3,4,5,6,7]
370 | print(separate_odd_even(data))
371 | ~~~
372 |
373 | **C-4.20** 递归实现,对于未排序的整数序列S和整数k,使得所有小于等于k的元素在所有大于k的元素之前。并求时间复杂度。
374 |
375 | ~~~python
376 | def funC20(data, k):
377 | if len(data) == 1:
378 | return [data[0]]
379 |
380 | for i in range(len(data)):
381 | if data[i] <= k:
382 | return [data[i]] + funC20(data[i+1:], k)
383 | else:
384 | return funC20(data[i+1:], k) + [data[i]]
385 | if __name__ == "__main__":
386 | data = [1,2,3,10,4,3,4,5,6,7,-4,-1,-3]
387 | print(funC20(data, 3))
388 | ~~~
389 |
390 | 时间复杂度 O(n^2^)
391 |
392 | **C-4.21** 给出一个含有n各元素的序列S,其中元素升序排列,递归找到S中总和为k得两个整数(假设一对整数存在)。算法复杂度是多少?
393 |
394 | ~~~python
395 | def funC21(data, k):
396 | def binary_search(data, target, start, end):
397 | if start > end:
398 | return False
399 | mid = (start + end) // 2
400 | if data[mid] == target:
401 | return data[mid]
402 | elif data[mid] > target:
403 | return binary_search(data, target, start, mid - 1)
404 | else:
405 | return binary_search(data, target, mid + 1, end)
406 | for j in range(len(data)-1):
407 | target = k - data[j]
408 | temp = binary_search(data[j+1:], target, 0, len(data[j+1:]))
409 | if temp:
410 | print('{}+{}={}'.format(data[j], temp, k))
411 |
412 |
413 | if __name__ == "__main__":
414 | data = [1,2,3,4,5,6,10,11]
415 | funC21(data, 11)
416 | ~~~
417 |
418 | 时间复杂度 O(nlogn)
419 |
420 |
--------------------------------------------------------------------------------
/Chapter 5.md:
--------------------------------------------------------------------------------
1 | # 基于数组的序列
2 |
3 | - 引用数组:Python使用数组内部存储机制(即对象引用),来表示一列或者元组实例。在最底层,存储的事一块连续的内存地址序列,这些地址指向一组元素序列。列表和元组就是用引用数组实现的。
4 | - 紧凑数组:字符串是用字符数组表示的(而不是用数组的引用)。相对于引用结构的优势:使用更少的内存;原始数据在内存中连续存放的。
5 | - 动态数组:列表在被构造的时候已经有了确定的长度,但该类允许对列表增添元素,对列表的总体大小没有明显的大小限制,这是使用了“动态数组”这种算法技巧。假如用户持续添加列表元素,所有预留单元将被耗尽,这时,列表就会向系统请求新的更大的数组,并初始化该数组,使其前面部分能与原来的小数组一样。
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | 《数据结构与算法:Python语言实现》笔记与习题解答
2 | 《Data Structures Algorithms in Python 》
--------------------------------------------------------------------------------
/fig/C-4.14_1.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wildwind0/Data-Structures-Algorithms-in-Python-notes-and-solutions-/5f40d27af9dc4217eda0b4aa8b0428ba6bd2ea06/fig/C-4.14_1.gif
--------------------------------------------------------------------------------
/fig/C-4.14_2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wildwind0/Data-Structures-Algorithms-in-Python-notes-and-solutions-/5f40d27af9dc4217eda0b4aa8b0428ba6bd2ea06/fig/C-4.14_2.png
--------------------------------------------------------------------------------
/fig/R-3.1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wildwind0/Data-Structures-Algorithms-in-Python-notes-and-solutions-/5f40d27af9dc4217eda0b4aa8b0428ba6bd2ea06/fig/R-3.1.png
--------------------------------------------------------------------------------
/scr/Ch1and2.py:
--------------------------------------------------------------------------------
1 | import random
2 | import string
3 | import re
4 | import sys
5 | import copy
6 |
7 |
8 | # R-1.1
9 | def is_multiple(n, m):
10 | if n % m == 0:
11 | return True
12 | else:
13 | return False
14 |
15 |
16 | # R-1,2
17 | def is_even(k):
18 | if k & 1 == 0:
19 | return True
20 | else:
21 | return False
22 |
23 |
24 | # R-1.3
25 | def maxmin(data):
26 | if data:
27 | m = n = data[0]
28 | for i in data:
29 | if i > m:
30 | m = i
31 | if i < n:
32 | n = i
33 | return (m, n)
34 | else:
35 | print("序列为空!")
36 |
37 |
38 | # R-1.4
39 | def square_sum(n):
40 | result = 0
41 | for i in range(n + 1):
42 | result += i ** 2
43 | return result
44 |
45 |
46 | # R-1.5
47 | def square_sum2(n):
48 | return sum([i ** 2 for i in range(n + 1)])
49 |
50 |
51 | # R-1.6
52 | def odd_square_sum(n):
53 | return sum([i ** 2 for i in range(n + 1) if i & 1 == 1])
54 |
55 |
56 | # R-1.12
57 | def my_choice(data):
58 | return data[random.randrange(len(data))]
59 |
60 |
61 | # C-1.14
62 | def funC14(data):
63 | for i in data:
64 | for j in data:
65 | if i != j and i * j % 2 != 0:
66 | return True
67 | return False
68 |
69 |
70 | # C-1.15
71 | def funC15(data):
72 | n = len(data)
73 | m = len(set(data))
74 | if n == m:
75 | return True
76 | else:
77 | return False
78 |
79 |
80 | # C-1.20
81 | def shuff1e1(data):
82 | result = []
83 | while data:
84 | p = random.randint(0, len(data) - 1)
85 | result.append(data[p])
86 | data.pop(p)
87 | return result
88 |
89 |
90 | def shuffle2(data):
91 | for i in range(len(data) - 1, 0, -1):
92 | p = random.randint(0, i)
93 | data[i], data[p] = data[p], data[i]
94 | return data
95 |
96 |
97 | def shuffle3(data):
98 | result = data[:]
99 | for i in range(1, len(data)):
100 | j = random.randint(0, i)
101 | result[i] = result[j]
102 | result[j] = data[i]
103 | return result
104 |
105 |
106 | # C-1.22
107 | def funC22(a, b):
108 | if len(a) != len(b):
109 | print("两个数组长度不相等!")
110 | return
111 | else:
112 | return [a[i] * b[i] for i in range(len(a))]
113 |
114 |
115 | # C-1.27
116 | def funC27(n):
117 | k = 1
118 | temp = []
119 | while k * k < n:
120 | if n % k == 0:
121 | yield k
122 | temp.append(n // k)
123 | k += 1
124 | if k * k == n:
125 | yield k
126 | for i in reversed(temp):
127 | yield i
128 |
129 |
130 | # C-1.28
131 | def norm(v, p):
132 | return sum([i ** p for i in v]) ** (1 / p)
133 |
134 |
135 | # C-1.29
136 | def Perm_k(arrs, k):
137 | # 若输入 [1,2,3],则先取出1这个元素,将剩余的 [2,3]中取出另一个元素得到 [[1,2],[1,3]]
138 | # 同理,取出2或者3时,得到的分别是 [[2,1],[2,3]]和 [[3,1],[3,2]]
139 | if len(arrs) == 1:
140 | return [arrs]
141 | if k == 1:
142 | return list(map(lambda s: [s], arrs)) # 当 k 为 1 时,每(单)个元素都可以被选取
143 | result = [] # 最终的结果(即全排列的各种情况)
144 | for i in range(len(arrs)):
145 | rest_arrs = arrs[:i] + arrs[i + 1:] # 取出arrs中的第 i个元素后剩余的元素
146 | rest_lists = Perm_k(rest_arrs, k - 1) # 剩余的元素选取 k-1元素
147 | lists = []
148 | for term in rest_lists:
149 | lists.append(arrs[i:i + 1] + term) # 将取出的第 i个元素加到剩余全排列的前面
150 | result += lists
151 | return result
152 |
153 |
154 | # C-1.30
155 | def funC30(n):
156 | if n <= 0:
157 | print("请输入一个正整数!")
158 | return
159 | i = 0
160 | while n >= 2:
161 | n = n // 2
162 | i += 1
163 | return i
164 |
165 |
166 | # C-1.31
167 | def funC31(price, pay):
168 | money = [100, 50, 20, 10, 5, 1, 0.5, 0.1]
169 | cash = {}
170 | # 由于python存在舍入误差(rounding errors),所以弄成整数以后再算
171 | temp = (pay - price) * 10
172 | if price < pay:
173 | for i in money:
174 | quotient = temp // (i * 10)
175 | remainder = round(temp % (i * 10))
176 | cash[i] = quotient
177 | if remainder == 0:
178 | break
179 | else:
180 | temp = remainder
181 | for key, value in cash.items():
182 | if value != 0:
183 | print("找零 {} 张 {} 元人民币".format(int(value), key))
184 | elif price == pay:
185 | print("刚好支付!")
186 | else:
187 | print("请支付足够金额!")
188 |
189 |
190 | # P-1.35
191 | def funP35(n):
192 | t = 0
193 | f = 0
194 | for p in range(100):
195 | birthdays = [random.choice([j for j in range(365)]) for i in range(n)]
196 | if len(birthdays) != len(set(birthdays)):
197 | t += 1
198 | else:
199 | f += 1
200 | print(t / 100)
201 |
202 |
203 | # P-1.36
204 | def funP36():
205 | words = "is a word like it is the the is word banana apple word is apple"
206 | count = {}
207 | words_list = words.split(" ")
208 | for word in words_list:
209 | count[word] = 1 + count.get(word, 0)
210 | print(count)
211 |
212 |
213 | if __name__ == "__main__":
214 | a = [1, 2, 3, [2, 1, 1]]
215 | c = copy.deepcopy(a)
216 | b = list(a)
217 | b[3][0] = 0
218 | print(a)
219 | print(b)
220 | print(c)
221 |
--------------------------------------------------------------------------------
/scr/Ch4.py:
--------------------------------------------------------------------------------
1 | import os
2 | import random
3 | # eg.1
4 |
5 |
6 | def factoria(n):
7 | if n == 0:
8 | return 1
9 | else:
10 | return n * factoria(n - 1)
11 |
12 |
13 | # eg.2
14 | def draw_line(tick_length, tick_label=''):
15 | line = '-' * tick_length
16 | if tick_label:
17 | line += ' ' + tick_label
18 | print(line)
19 |
20 |
21 | def draw_interval(center_length):
22 | if center_length > 0:
23 | draw_interval(center_length - 1)
24 | draw_line(center_length)
25 | draw_interval(center_length - 1)
26 |
27 |
28 | def draw_ruler(num_inches, major_length):
29 | draw_line(major_length, '0')
30 | for i in range(1, num_inches + 1):
31 | draw_interval(major_length - 1)
32 | draw_line(major_length, str(i))
33 |
34 | # eg.3
35 |
36 |
37 | def binary_search(data, target, low, high):
38 | if low > high:
39 | return False
40 | else:
41 | mid = (low + high) // 2
42 | if target == data[mid]:
43 | return True
44 | elif target < data[mid]:
45 | return binary_search(data, target, low, mid - 1)
46 | else:
47 | return binary_search(data, target, mid + 1, high)
48 |
49 | # eg.4
50 |
51 |
52 | def disk_usage(path):
53 | total = os.path.getsize(path)
54 | if os.path.isdir(path):
55 | for filename in os.listdir(path):
56 | childpath = os.path.join(path, filename)
57 | total += disk_usage(childpath)
58 | print('{0:<7}'.format(total), path)
59 |
60 | # eg.5
61 |
62 |
63 | def good_fibonacci(n):
64 | if n <= 1:
65 | print(0)
66 | return (n, 0)
67 | else:
68 | (a, b) = good_fibonacci(n - 1)
69 | print(a)
70 | return (a + b, a)
71 | # eg.6
72 |
73 |
74 | def power(x, n):
75 | if n == 0:
76 | return 1
77 | else:
78 | partial = power(x, n // 2)
79 | total = partial * partial
80 | if n % 2 == 1:
81 | total *= x
82 | return total
83 |
84 | # R-4.1
85 |
86 |
87 | def find_max(data):
88 | if len(data) == 1: # 只有一个元素
89 | return data[0]
90 | elif len(data) == 2:
91 | return data[0] if data[0] > data[1] else data[1]
92 | else:
93 | mid = len(data) // 2
94 | max_left = find_max(data[:mid])
95 | max_right = find_max(data[mid:])
96 | return max_left if max_left > max_right else max_right
97 | # R-4.6
98 |
99 |
100 | def harmonic(n):
101 | if n == 1:
102 | return 1
103 | else:
104 | return 1 / n + harmonic(n - 1)
105 |
106 | # R-4.7
107 |
108 |
109 | def string_to_integer(string):
110 | if len(string) == 1:
111 | return int(string[0])
112 | else:
113 | return int(string[0]) * 10 ** (len(string) - 1) + \
114 | string_to_integer(string[1:])
115 |
116 | # C4.9
117 | def find_min_and_max(data):
118 | if len(data) == 1: # 只有一个元素
119 | return (data[0], data[0])
120 | elif len(data) == 2:
121 | max_num = data[0] if data[0] > data[1] else data[1]
122 | min_num = data[0] if data[0] < data[1] else data[1]
123 | return (max_num, min_num)
124 | else:
125 | mid = len(data) // 2
126 | max_num_left, min_num_left = find_min_and_max(data[:mid])
127 | max_num_right, min_num_right = find_min_and_max(data[mid:])
128 | max_ = max_num_left if max_num_left > max_num_right else max_num_right
129 | min_ = min_num_left if min_num_left < min_num_right else min_num_right
130 | return (max_, min_)
131 |
132 | # C-4.10
133 | def funC10(n, i=0):
134 | if 2**i >= n:
135 | return i
136 | else:
137 | return funC10(n, i+1)
138 | # C-4.11
139 | def funC11(data):
140 | if len(data) == 1:
141 | return True
142 | else:
143 | for j in data[1:]:
144 | if data[0] == j:
145 | return False
146 | return funC11(data[1:])
147 |
148 | # C-4.12
149 | def funC12(m, n):
150 | if m == 1:
151 | return n
152 | else:
153 | return n + funC12(m - 1, n)
154 |
155 | # C-4.14
156 | def hanoi_tower(n, a, b, c):
157 | """
158 | :param n: 碟子数
159 | :param a: 碟子摞
160 | :param b: 辅助摞
161 | :param c: 目标摞
162 | """
163 | def move(a, x, c):
164 | print("{}>>>_{}_>>>{}".format(a, x, c))
165 | if n == 1:
166 | move(a, n,c)
167 | else:
168 | hanoi_tower(n-1, a, c, b)
169 | move(a, n, c)
170 | hanoi_tower(n -1, b, a, c)
171 | # C-4.15
172 | def subset(data, k):
173 | if len(data) == 1:
174 | return [data]
175 | if k == 1:
176 | return [[i] for i in data]
177 | result = []
178 | for j in range(len(data)):
179 | if len(data[j+1:]) >= k-1:
180 | rest_data = subset(data[j+1:], k-1)
181 | lists = []
182 | for r in rest_data:
183 | lists.append(data[j:j+1] + r)
184 | result += lists
185 | return result
186 | # C-4.16
187 | def reverse_string(string, start, stop):
188 | if start > stop:
189 | return
190 | else:
191 | string[start], string[stop] = string[stop], string[start]
192 | reverse_string(string, start+1, stop-1)
193 | return string
194 | # C-4.17
195 | def is_huiwen(string, start, stop):
196 | if string[start] != string[stop]:
197 | return False
198 | elif start == stop or start == stop -1:
199 | return True
200 | else:
201 | return is_huiwen(string, start+1, stop-1)
202 |
203 | # C-4.18
204 | def C18(s, start, stop):
205 | if start == stop - 1:
206 | if s[start] in vowels:
207 | return 1
208 | elif s[start] in consonants:
209 | return -1
210 | else:
211 | return 0
212 | else:
213 | mid = (start + stop) // 2
214 | return C18(s, start, mid) + C18(s, mid, stop)
215 |
216 | # C-4.19
217 | def separate_odd_even(data):
218 | if len(data) == 1:
219 | return [data[0]]
220 |
221 | for i in range(len(data)):
222 | if data[i] % 2 == 0:
223 | return [data[i]] + separate_odd_even(data[i+1:])
224 | else:
225 | return separate_odd_even(data[i+1:]) + [data[i]]
226 |
227 | # C-4.20
228 | def funC20(data, k):
229 | if len(data) == 1:
230 | return [data[0]]
231 |
232 | for i in range(len(data)):
233 | if data[i] <= k:
234 | return [data[i]] + funC20(data[i+1:], k)
235 | else:
236 | return funC20(data[i+1:], k) + [data[i]]
237 |
238 | # C-4.21
239 | def funC21(data, k):
240 | def binary_search(data, target, start, end):
241 | if start > end:
242 | return False
243 | mid = (start + end) // 2
244 | if data[mid] == target:
245 | return data[mid]
246 | elif data[mid] > target:
247 | return binary_search(data, target, start, mid - 1)
248 | else:
249 | return binary_search(data, target, mid + 1, end)
250 | for j in range(len(data)-1):
251 | target = k - data[j]
252 | temp = binary_search(data[j+1:], target, 0, len(data[j+1:]))
253 | if temp:
254 | print('{}+{}={}'.format(data[j], temp, k))
255 |
256 |
257 | if __name__ == "__main__":
258 | data = 'aeeqe'
259 | print(data[0])
260 |
--------------------------------------------------------------------------------
/scr/Ch5.py:
--------------------------------------------------------------------------------
1 | # 插入排序
2 | def insertion_sort(A):
3 | for k in range(1, len(A)):
4 | cur = A[k]
5 | j = k
6 | while j > 0 and A[j-1] > cur:
7 | A[j] = A[j-1]
8 | j -= 1
9 | A[j] = cur
10 |
11 | if __name__ == "__main__":
12 | A = [4,3,2,1]
13 | insertion_sort(A)
14 | print(A)
--------------------------------------------------------------------------------
/习题参考答案/ch01.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wildwind0/Data-Structures-Algorithms-in-Python-notes-and-solutions-/5f40d27af9dc4217eda0b4aa8b0428ba6bd2ea06/习题参考答案/ch01.pdf
--------------------------------------------------------------------------------
/习题参考答案/ch03.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wildwind0/Data-Structures-Algorithms-in-Python-notes-and-solutions-/5f40d27af9dc4217eda0b4aa8b0428ba6bd2ea06/习题参考答案/ch03.pdf
--------------------------------------------------------------------------------