看书还得写笔记,文字的写不来,还是写电子的,自己的字跟狗爬一样,打出来的字好多了。
后续把自己看的基本关于网络的书也写点博客,一便于查寻,二便于加强记忆,要不然跟小说一样,看了就忘了。
第1章:算法介绍
理解大O表示法,并非以秒为单位。大O表示法让你能够比较操作数,它指出了算法运行时间的增速。
大O表示法说的是在查找情况中最糟的情形。
从快到慢晕倒的5种大O运行时间。
O(log n),也叫对数时间,这样的算法包括二分查找。
O(n), 也叫线性时间,这样的算法包括简单查找。
O(n * log n),这样的算法包括快速排序---一种比较快的排序算法
O(n2)【表示n的平方】, 这样的算法包括选择排序---一种速度较慢的排序算法
O(n!), 这样的算法包括旅行商的解决方案---一种非常慢的算法
第一章主要理解:
算法的速度指的并非时间,而是操作数的增速。
讨论算法的速度时,我们说的是随着输入的增加,其运行时间将以什么样的速度增加。
算法的运行时间用大O表示法表示。
O(log n)比O(n)快,当需要搜索的元素越多时,前者比后者快很多。
小结:
二分查找的速度比简单查找快很多。
O(logn)比O(n)快。需要搜索的元素越多,前者比后者就快得更多
算法运行时间并不以秒为单位。
算法运行时间是从其增速的角度度量的。
算法运行时间用大O表示法表示
最后上书中的二分查找代码
def binary_search(list, item): # 初始化序列的开始序列号,为末尾的序列号 low = 0 high = len(list) - 1 # 只有在开始的序列号小于等于结束的序列号,才执行2分,否则就是找不到元素 while low <= high: # 地板除取出中间值 middle = (low + high) // 2 # 取出中间值的值 guess = list[middle] # 如果是的话,就返回这个索引 if guess == item: return middle # 当取出来的中间值比要帅选的值大,按取出来中间值的前一位索引就是下一次寻找的结尾。 elif guess > item: high = middle - 1 # 反之,下一次查找的开始索引中间值的后一位索引,这里我还是比较容易搞混的 else: low = middle + 1 return None if __name__ == '__main__': print(binary_search('123456', '2'))
第2章 选择排序
主要学习数组与链表
数组与链表的运行时间
数组 链表
读取 O(1) O(n)
插入 O(n) O(1)
删除 O(n) O(1)
这里指出一下,仅当能够立即访问要删除的元素时,删除操作的运行时间才为O(1)。通常我们都记录了链表的第一个元素和最后一个元素,因此删除这些元素时运行时间为O(1)
选择排序的时间:O(n2)【表示n的平方】
小结:
计算机的内存犹如一大堆抽屉。
需要存储多个元素时,可使用数组或者链表
数组的元素都在一起
链表的元素是分开的,其中每个元素都存储了下一个元素的地址
数组的读取速度很快
链表的插入和删除速度很快
在同一个数组中,所有元素的类型都必须相同(都为int,double等)
代码:
这是我自己写的:
def my_sort(arr): # 首相取选址范围,从大到小取,最大为最大索引,最小为0 for i in range(len(arr) - 1, -1, -1): # 开始循环对数组的数据进行比较 for i in range(i): # 如果前面的数字大于后面的数字,两个数字互相,确保后面的数字大 if arr[i] > arr[i + 1]: arr[i], arr[i + 1] = arr[i + 1], arr[i] return arr if __name__ == '__main__': print(my_sort([9, 3, 33, 3, 2, 1, 5, 6]))
def findSmallest(arr): # 定义初始的最小值 smallest = arr[0] smallest_index = 0 # 循环读取列表,返回列表最小的索引 for i in range(1, len(arr)): if arr[i] < smallest: smallest = arr[i] smallest_index = i return smallest_index def selectionSort(arr): # 在一个新的列表中,每次装入最小的索引 newArr = [] for i in range(len(arr)): smallest = findSmallest(arr) newArr.append(arr.pop(smallest)) return newArr if __name__ == '__main__': print(selectionSort(list('6754345678987654')))
选择排序是一种简单直观的排序算法,无论什么数据进去都是 O(n²) 的时间复杂度。所以用到它的时候,数据规模越小越好。唯一的好处可能就是不占用额外的内存空间了吧。
我写的不用额外占用内存空间,书中的代码还是需要额外新建一个新列表,但书中的代码更加容易理解,而且逻辑也很漂亮
第三章 递归
递归时我最讨厌的主题,希望书中学完,能够让我爱上它一点
实际使用中,使用循环的性能更好。高手在Stark Overflow上说过:如果使用循环,程序的性能可能更高;如果使用递归,程序可能更容易理解。如何选择要看什么对你来说更重要
每个递归函数都有两部分组成:基线条件(base case)和递归条件(recursive case)。递归条件指的是函数调用自己,而基线条件则指的是函数不再调用自己,从而避免形成无限循环。
书中举例了一个好简单的例子,真的很基础,但讲的不错。
def fact(x): if x == 1: return 1 else: return x * fact(x-1)
注意每个fact调用都有自己的x变量。在一个函数中不能访问另一个x变量
书p39页,结合盒子的例子。这个栈包含未完成的函数调用,每个函数调用都包含未检查完的盒子。使用栈很方便,因为你无需自己跟踪盒子堆-栈替你这样做了。
原来Python确实有递归次数限制,默认最大次数为998
小结:
递归指的是调用自己的函数
每个递归函数都有两个条件:基线条件和递归条件
栈有两种操作:压入和弹出
所有函数调用都进入调用栈
调用栈可能很长,这将占用大量的内存
第4章 快速排序
学习分而治之和快速排序。分而治之是本书学习的第一种通用的解决方法。
学习快速排序---一种常用的优雅的排序算法。快速排序使用分而治之的策略。
分而治之D&G(divide and cpnquer)
工作原理:
找出基线条件,这个条件必须尽可能的简单
不断将问题分解(或者说缩小规模),直到符合基线条件
涉及数组的递归函数时,基线条件通常是数组为空或只包含一个元素。陷入困境时,请检查基线条件是不是这样的。
书中的3道编程题,没能写出来,只能抄答案了。
请编写书中要求sum函数的代码
def sum(arr): if len(arr) == 0: return 0 # 把第一个值取出来,后面的进行递归,当只有一个元素的arr会满足基线条件 return arr[0] + sum(arr[1:]) if __name__ == '__main__': print(sum(list(range(997))))
编写一个递归函数来计算列表包含的元素数:
def count(arr): if arr == []: return 0 return 1 + count(arr[1:]) if __name__ == '__main__': print(count(list(range(100))))
跟第一个原理差不多
找出列表中最大的数字
def find_max_num(arr): # 当两个元素的时候,进行比较,返回最大值,基线条件 if len(arr) == 2: return arr[0] if arr[0] > arr[1] else arr[1] # 递归条件,拆分后面索引1的元素到最后的元素进行递归条件。 sun_max = find_max_num(arr[1:]) return arr[0] if arr[0] > sun_max else sun_max # 这个我自己真心写不出来,看的我都有点绕了 if __name__ == '__main__': print(find_max_num([1, 2, 3, 4, 99, 5]))
正式进入快速排序:
来源:https://www.cnblogs.com/sidianok/p/12446291.html