读书笔记:算法图解 using Python
读书笔记:算法图解 using Python
阅读书目:[美] Aditya Bhargava. “算法图解”
写在前面:
作者称自己是“视觉型学习者,对图解式写作风格钟爱有加”。这一点和我学东西的思维方式很想,对作者很有好感。事实上在阅读过程中也感到很愉快,有些类似近藤麻理惠《怦然心动的人生整理魔法》的图解和讲解方式,深入浅出,常看常新。
阅读这本书主要是想帮自己回忆上课学过的数据结构和算法的相关内容,希望帮自己建立起形象的体系和深刻的理解。此外,当时上课是基于C/C++,这次学习数据结构和算法希望能基于Python实现,为之后的刷题道路铺路。
目录
- 读书笔记:算法图解 using Python
- ch1 算法简介
- 二分查找
- 大O表示法
- ch2 选择排序
- 数组和链表
- 选择排序
- ch3 递归
- 递归
- 调用栈 call stack
- ch4 快速排序
- 分治
- 快速排序
- ch5 散列表
- 散列函数
- 应用案例
- 冲突和性能
- ch6 广度优先搜索
- 图简介
- 广度优先搜索 BFS
- 图的实现
- 广度优先算法的实现
- ch7 狄克斯特拉算法
- 狄克斯特拉算法的理解
- 狄克斯特拉算法的实现
- ch8 贪婪算法
- 教室调度问题
- 背包问题
- 集合覆盖问题
- NP完全问题
- 旅行商问题
- ch9 动态规划
- 背包问题
- 背包问题FAQ
- 最长公共子串
- 最长公共子串
- 最长公共子序列
- 动态规划都有哪些实际应用呢?
- 小结
- ch10 K最近邻算法(机器学习Mini Intro)
- 推荐系统
- OCR
- 小结
- ch11 其他
- 二叉查找树
- 反向索引 Inverted Index
- 傅立叶变换
- 并行算法
- MapReduce
- 布隆过滤器和HyperLogLog
- SHA算法
- Diffie-Hellman密钥交换
- 线性规划
ch1 算法简介
二分查找
# 二分查找
# lst为有序数组,若item存在lst中则返回下标,否则返回-1
def binary_search(lst, item):# 查找的下标范围low = 0high = len(lst) - 1while low <= high:# 每次查找中间的元素mid = (low + high) // 2 # 向下取整guess = lst[mid]if guess == item:return midelif guess < item: # 如果猜的数字小了,修改lowlow = mid + 1else: # 如果猜的数字大了,修改highhigh = mid - 1return -1
>>> lst = [1,4,5,6,8,13,15,61,543]
>>> binary_search(lst, 8)
4
>>> binary_search(lst, 9)
-1
二分查找的时间复杂度为O(logn)O(\log n)O(logn)。
大O表示法
介绍了大O表示法的含义和目的,结合图像和实际问题说明了不同时间复杂度之间的差异。
ch2 选择排序
数组和链表
- 内存上的差异:计算机内存犹如一大堆抽屉。
- 数组的元素都在一起。
- 链表的元素是分开的,其中每个元素都存储了下一个元素的地址。
- 常见操作的运行时间:读取/插入/删除,要根据实例的操作需求选择数据结构。
- 数组的读取速度很快。/ 数组擅长随机访问。
- 链表的插入和删除速度很快。/ 链表擅长插入和删除。
- 练习:Facebook如何存储用户名?
选择排序
# 找出数组中最小元素
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 = ireturn smallest_index
# 选择排序
def selectionSort(arr):newArr = []for i in range(len(arr)):# 找出数组中最小的元素,并将其加入到新数组中 smallest_index = findSmallest(arr)newArr.append(arr.pop(smallest_index))return newArr
>>> selectionSort([5, 3, 6, 2, 10])
[2, 3, 5, 6, 10]
选择排序的运行时间为O(n2)O(n^2)O(n2)。
ch3 递归
递归
递归
是指函数调用自己。
递归只是让解决方案更清晰,并没有性能上的优势。实际上,在有些情况下,使用循环的性能更好。
编写递归函数时,必须告诉它何时停止递归。
递归条件 recursive case
:函数调用自己基线条件 base case
:函数不再调用自己
从而避免形成无限循环。
调用栈 call stack
栈有两种操作:压入和弹出。
计算机在内部使用被称为调用栈
的栈。这个栈用于存储多个函数的变量。所有函数调用都进入调用栈。
栈在递归中扮演着重要角色。使用栈虽然很方便,但是也要付出代价:存储详尽的信息可能占用大量的内存。调用栈可能很长,这将占用大量的内存。在这种情况下,你有两种选择。
重新编写代码,转而使用循环。
使用
尾递归
。这是一个高级递归主题,不在本书的讨论范围内。另外,并非所有的语言都支持尾递归。
ch4 快速排序
快速排序采用了分而治之的策略。
我们将探索分而治之 (divide and conquer,D&C)
——一种著名的递归式问题解决方法。
分治
D&C将问题逐步分解。使用D&C处理列表时,基线条件很可能是空数组或只包含一个元素的数组。
- 一个直观的示例:农场主的土地划分
- 一个代码的示例:函数
sum(lst)
的实现原理 - 快速排序
# 练习4.1: 请编写前述sum 函数的代码。
def my_sum(lst):if not lst: return 0return lst[0] + my_sum(lst[1:])
>>> my_sum([1,2,3,4,5])
15
# 练习4.2: 编写一个递归函数来计算列表包含的元素数。
def my_count(lst):if not lst: return 0return 1 + my_count(lst[1:])
>>> my_count([1,2,3,4,5])
5
# 练习4.3: 找出列表中最大的数字。
def my_max(lst):if len(lst) == 0: return Noneelif len(lst) == 1: return lst[0]else:return max(lst[0], my_max(lst[1:]))
>>> my_max([1,2,3,4,6])
6
>>> my_max([])
>>> my_max([1])
1
# 练习4: 二分查找也是一种分而治之算法,可以写成递归形式
def binary_search(lst, item):def helper(lst, item, low, high):if low > high:return Falseelse:mid = (low + high) // 2if item == lst[mid]return Trueelif item < lst[mid]:return helper(lst, item, low, mid - 1)else:return helper(lst, item, mid + 1, high)low = 0high = len(lst) - 1return helper(lst, item, low, high)
快速排序
归纳证明
:
在基线条件中,我证明这种算法对空数组或包含一个元素的数组管用。
在归纳条件中,我证明如果快速排序对包含一个元素的数组管用,对包含两个元素的数组也将管用;如果它对包含两个元素的数组管用,对包含三个元素的数组也将管用,以此类推。因此,我可以说,快速排序对任何长度的数组都管用。这里不再深入讨论归纳证明,但它很有趣,并与D&C协同发挥作用。
步骤:
- 选择
基准值
。 - 将数组分成两个子数组:小于基准值的元素和大于基准值的元素。
- 对这两个子数组进行快速排序。
# 快速排序
def quicksort(array):if len(array) < 2: # 基线条件:为空或只包含一个元素的数组是“有序”的return array else: # 递归条件pivot = array[0] less = [i for i in array[1:] if i <= pivot] # 由所有小于基准值的元素组成的子数组greater = [i for i in array[1:] if i > pivot] # 由所有大于基准值的元素组成的子数组return quicksort(less) + [pivot] + quicksort(greater)
>>> quicksort([10, 5, 2, 3])
[2, 3, 5, 10]
快速排序的性能高度依赖于你选择的基准值。快速排序的平均运行时间为O(nlogn)O(n\log n)O(nlogn),最糟糕运行时间为O(n2)O(n^2)O(n2)。
大O表示法中的常量有时候事关重大,对快速查找和合并查找来说就是如此。快速查找的常量比合并查找小,因此如果它们的运行时间都为O(nlogn)O(n\log n)O(nlogn),快速查找的速度将更快。实际上,快速查找的速度确实更快,因为相对于遇上最糟情况,它遇上平均情况的可能性要大得多。
比较简单查找和二分查找时,常量几乎无关紧要,因为列表很长时,O(logn)O(\log n)O(logn)的速度比O(n)O(n)O(n)快得多。
ch5 散列表
散列函数
散列表用途广泛。散列表
,也被称为散列映射
、映射
、字典
和关联数组
。
散列函数
将输入映射到数字。
它必须是一致的。将同样的输入映射到相同的索引。
理想情况下,它应将不同的输入映射到不同的索引。
你可能根本不需要自己去实现散列表,任一优秀的语言都提供了散列表实现。Python提供的散列表实现为字典
。
散列表由键
和值
组成。在下面的散列表book
中,键为商品名,值为商品价格。散列表将键映射到值。散列表可以立即获取对应键的值。
>>> book = dict()
>>> book["apple"] = 0.67 # 一个苹果的价格为67美分
>>> book["milk"] = 1.49 # 牛奶的价格为1.49美元
>>> book["avocado"] = 1.49
>>> print book
{'avocado': 1.49, 'apple': 0.67, 'milk': 1.49}
>>> print book["avocado"] # 鳄梨的价格
1.49
应用案例
- 模拟映射关系,用于查找
- 电话簿:联系人-电话号码
- DNS解析:网站-IP地址
- 防止重复
- 投票
别忘了,如果你将已投票者的姓名存储在列表中,这个函数的速度终将变得非常慢,因为它必须使用简单查找搜索整个列表。但这里将它们存储在了散列表中,而散列表让你能够迅速知道来投票的人是否投过票。使用散列表来检查是否重复,速度非常快。
- 缓存/记住数据,以免服务器再通过处理来生成它们。
缓存是一种常用的加速方式,所有大型网站都使用缓存,而缓存的数据则存储在散列表中!
Facebook不仅缓存主页,还缓存About页面、Contact页面、Terms and Conditions页面等众多其他的页面。因此,它需要将页面URL映射到页面数据。
当你访问Facebook的页面时,它首先检查散列表中是否存储了该页面。若是,则返回缓存中的数据,否则让服务器做些处理
cache = {}def get_page(url):if cache.get(url):return cache[url] # 返回缓存的数据else:data = get_data_from_server(url)cache[url] = data # 先将数据保存到缓存中return data
冲突和性能
良好的散列函数让数组中的值呈均匀分布。糟糕的散列函数让值扎堆,导致大量的冲突。
散列表的查找、插入和删除速度都非常快。平均情况为O(1)O(1)O(1),而最糟情况为O(n)O(n)O(n)。
一旦填装因子超过0.70.70.7,就该调整散列表的长度。
但是几乎根本不用自己去实现散列表,因为可使用Python提供的散列表,并假定能够获得平均情况下的性能:常量时间。
ch6 广度优先搜索
图简介
最短路径问题 shortest-path problem
。注意,本节中是指非加权图的最短路径问题 ,比如社交网络中一度关系、二度关系…
图模拟一组连接。图用于模拟不同的东西是如何相连的。
图由节点(node)
和边(edge)
组成。
广度优先搜索 BFS
广度优先搜索(breadth-first search,BFS)
是一种用于图的查找算法
,可帮助回答两类问题。
第一类问题:从节点A出发,
有
前往节点B的路径吗
?第二类问题:从节点A出发,前往节点B的
哪条
路径最短
?
需要按添加顺序进行检查。有一个可实现这种目的的数据结构,那就是队列(queue)
。队列仅支持两种操作:
- 入队
- 出队
队列是一种先进先出 (First In First Out,FIFO)
的数据结构,而栈是一种后进先出 (Last In First Out,LIFO)
的数据结构。
图的实现
用散列表(Python的字典)表示图。将节点映射到其所有邻居。注意,散列表是无序的。
# 以下是一个有向图,dict的每个value是key的list
graph = {}
graph["you"] = ["alice", "bob", "claire"]
graph["bob"] = ["anuj", "peggy"]
graph["alice"] = ["peggy"]
graph["claire"] = ["thom", "jonny"]
graph["anuj"] = []
graph["peggy"] = []
graph["thom"] = []
graph["jonny"] = []
广度优先算法的实现
检查一个人之前,要确认之前没检查过他,这很重要,否则可能形成无限循环。为此,你可使用一个列表来记录检查过的人。
from collections import deque def person_is_seller(name):return name[-1] == 'm' # 检查人的姓名是否以m结尾:如果是,他就是芒果销售商。def search(name):search_queue = deque() # 创建一个队列search_queue += graph[name] # 将name的邻居都加入到这个搜索队列中searched = [] # 这个数组用于记录检查过的人while search_queue: # 只要队列不为空person = search_queue.popleft() # 取出其中的第一个人if person not in searched: # 仅当这个人没检查过时才检查if person_is_seller(person): # 检查这个人是否是芒果销售商print(person + " is a mango seller!") # 是芒果销售商return Trueelse:search_queue += graph[person] # 不是芒果销售商。将这个人的朋友都加入搜索队列searched.append(person) # 将这个人标记为检查过return False # 如果到达了这里,就说明队列中没人是芒果销售商
>>> search("you")
thom is a mango seller!
True
运行时间:
如果你在你的整个人际关系网中搜索芒果销售商,就意味着你将沿每条边前行(记住,边是从一个人到另一个人的箭头或连接),因此运行时间至少为O(边数)O(边数)O(边数)。
你还使用了一个队列,其中包含要检查的每个人。将一个人添加到队列需要的时间是固定的,即为O(1)O(1)O(1),因此对每个人都这样做需要的总时间为O(人数)O(人数)O(人数)。所以,广度优先搜索的运行时间为O(人数+边数)O(人数+边数)O(人数+边数),这通常写作O(V+E)O(V+E)O(V+E),其中VVV为顶点数,EEE为边数。
注意,树是一种特殊的图。树是连通无环无向图。
ch7 狄克斯特拉算法
要计算加权图
中的最短路径,可使用狄克斯特拉算法 (Dijkstra's algorithm)
。狄克斯特拉算法找出的是总权重最小的路径。
注意它的适用情况:
- 计算非加权图中的最短路径,可使用广度优先搜索。要计算加权图中的最短路径,可使用狄克斯特拉算法。
- 狄克斯特拉算法只适用于
有向无环图 (directed acyclic graph,DAG)
。 - 狄克斯特拉算法不能用于
含有负权边的图
。这种情况可以用贝尔曼-福德算法(Bellman-Ford Algorithm)
。
狄克斯特拉算法的理解
狄克斯特拉算法包含4个步骤。
- 找出最便宜的节点,即可在最短时间内前往的节点。
- 对于该节点的邻居,检查是否有前往它们的更短路径,如果有,就更新其开销。
- 重复这个过程,直到对图中的每个节点都这样做了。
- 计算最终路径。
以想要以乐谱换钢琴为例:
同时维护一个开销表和父节点表。算法过程如下:
最终获得了最短路径:
狄克斯特拉算法的实现
以下图为例:
需要用到3个散列表+1个数组:
# 创建图
graph = {}
graph['start'] = {'a': 6, 'b': 2}
graph['a'] = {'fin': 1}
graph['b'] = {'a': 3, 'fin': 5}
graph['fin'] = {} # 终点没有任何邻居# 创造开销表
costs = {}
costs['a'] = 6
costs['b'] = 2
costs['fin'] = inf# 创建储存父节点的散列表
parents = {}
parents['a'] = 'start'
parents['b'] = 'start'
parents['fin'] = None
>>> graph
{'start': {'a': 6, 'b': 2},'a': {'fin': 1},'b': {'a': 3, 'fin': 5},'fin': {}}
>>> costs
{'a': 6, 'b': 2, 'fin': inf}
>>> parents
{'a': 'start', 'b': 'start', 'fin': None}
# 用一个数组记录处理过的节点
processed = []
def find_lowest_cost_node(costs):lowest_cost = inflowest_cost_node = Nonefor node, cost in costs.items(): # 遍历所有的节点if cost < lowest_cost and node not in processed: # 如果当前节点的开销更低且未处理过,lowest_cost = cost # 就将其视为开销最低的节点lowest_cost_node = nodereturn lowest_cost_nodenode = find_lowest_cost_node(costs) # 在未处理的节点中找出开销最小的节点
while node is not None: # 这个while循环在所有节点都被处理过后结束cost = costs[node]neighbors = graph[node]for n in neighbors.keys(): # 遍历当前节点的所有邻居new_cost = cost + neighbors[n]if costs[n] > new_cost: # 如果经当前节点前往该邻居更近,costs[n] = new_cost # 就更新该邻居的开销parents[n] = node # 同时将该邻居的父节点设置为当前节点processed.append(node) # 将当前节点标记为处理过node = find_lowest_cost_node(costs) # 找出接下来要处理的节点,并循环
>>> costs
{'a': 5, 'b': 2, 'fin': 6}
>>> parents
{'a': 'b', 'b': 'start', 'fin': 'a'}
ch8 贪婪算法
对刷题似乎没太大帮助,详细内容先略。
学习如何处理不可能完成的任务:没有快速算法的问题(NP完全问题)。
学习识别
NP完全问题
,以免浪费时间去寻找解决它们的快速算法。学习
近似算法
,使用它们可快速找到NP完全问题的近似解。学习
贪婪
策略——一种非常简单的问题解决策略。
教室调度问题
贪婪算法很简单:每步都采取最优的做法。在这个示例中,你每次都选择结束最早的课。用专业术语说,就是你每步都选择局部最优解 ,最终得到的就是全局最优解。信不信由你,对于这个调度问题,上述简单算法找到的就是最优解!
显然,贪婪算法并非在任何情况下都行之有效,但它易于实现!下面再来看一个例子。
背包问题
看性价比。
集合覆盖问题
NP完全问题
旅行商问题
ch9 动态规划
背包问题
背包问题的简单算法是尝试所有排列组合,运算时间为O(2n)O(2^n)O(2n)
动态规划
先解决子问题,在逐步解决大问题。
每个动态规划算法都从一个网格开始,背包问题的网格如下。
网格的各行为商品,各列为不同容量(1~4磅)的背包。所有这些列你都需要,因为它们将帮助你计算子背包的价值。
网格最初是空的。你将填充其中的每个单元格,网格填满后,就找到了问题的答案!你一定要跟着做。请你创建网格,我们一起来填满它。
正在填充的表格:
填充的公式:
背包问题FAQ
Q1:假设你发现还有第四件商品可偷——一个iPhone!此时需要重新执行前面所做的计算吗?
A1:不需要。追加一行继续计算即可,
Q2:沿着一列往下走时,最大价值有可能降低吗?
A2:不可能。每次迭代时,你都存储当前的最大价值。最大价值不可能比以前低!
Q3:行的排列顺序发生变化时结果将如何?
A3:答案不会随之变化。
Q4:可以逐列而不是逐行填充网格吗?
A4:就这个问题而言,这没有任何影响,但对于其他问题,可能有影响。
Q5:增加一件更小的商品将如何呢?
A5:由于项链的加入,你需要考虑的粒度更细,因此必须调整网格。
Q6:可以偷商品的一部分吗?
A6:没法用动态规划来处理这种情形。但使用贪婪算法可轻松地处理这种情况!首先,尽可能多地拿价值最高的商品;如果拿光了,再尽可能多地拿价值次高的商品,以此类推。
Q7:相互依赖的情况?
A7:动态规划没法处理。动态规划功能强大,它能够解决子问题并使用这些答案来解决大问题。但仅当每个子问题都是离散的,即不依赖于其他子问题时,动态规划才管用。
Q8:最优解可能导致背包没装满吗
A8:完全可能。
最长公共子串
最长公共子串
费曼算法 (Feynman algorithm)
if word_a[i] == word_b[j]: # 两个字母相同cell[i][j] = cell[i-1][j-1] + 1
else: # 两个字母不同cell[i][j] = 0
最长公共子序列
if word_a[i] == word_b[j]: # 两个字母相同cell[i][j] = cell[i-1][j-1] + 1
else: # 两个字母不同cell[i][j] = max(cell[i-1][j], cell[i][j-1])
动态规划都有哪些实际应用呢?
生物学家根据最长公共序列来确定DNA链的相似性,进而判断度两种动物或疾病有多相似。最长公共序列还被用来寻找多发性硬化症治疗方案。
你使用过诸如git diff 等命令吗?它们指出两个文件的差异,也是使用动态规划实现的。
前面讨论了字符串的相似程度。编辑距离 (levenshtein distance)指出了两个字符串的相似程度,也是使用动态规划计算得到的。编辑距离算法的用途很多,从拼写检查到判断用户上传的资料是否是盗版,都在其中。
你使用过诸如Microsoft Word等具有断字功能的应用程序吗?它们如何确定在什么地方断字以确保行长一致呢?使用动态规划!
小结
动态规划可帮助你在给定约束条件下找到最优解。在背包问题中,你必须在背包容量给定的情况下,偷到价值最高的商品。
在问题可分解为彼此独立且离散的子问题时,就可使用动态规划来解决。
每种动态规划解决方案都涉及网格。
单元格中的值通常就是你要优化的值。在前面的背包问题中,单元格的值为商品的价值。在后面的最长公共子串问题中,单元格的值为子传长度。
每个单元格都是一个子问题,因此你应考虑如何将问题分成子问题,这有助于你找出网格的坐标轴。
没有放之四海皆准的计算动态规划解决方案的公式。
ch10 K最近邻算法(机器学习Mini Intro)
但愿通过阅读本章,你对KNN和机器学习的各种用途能有大致的认识!机器学习是个很有趣的领域,只要下定决心,你就能很深入地了解它。
推荐系统
推荐:特征抽取
读书笔记:算法图解 using Python相关推荐
- 剑指offer(第二版)读书笔记以及编程题目python版答案(二)
剑指offer(第二版)读书笔记以及编程题目python版答案(二) 题目五:青蛙跳台阶 github地址: https://github.com/ciecus/leetcode_answers/tr ...
- 读书笔记 -- 算法入门
14天阅读挑战赛 努力是为了不平庸~ 算法学习有些时候是枯燥的,这一次,让我们先人一步,趣学算法!欢迎记录下你的那些努力时刻(算法学习知识点/算法题解/遇到的算法bug/等等),在分享的同时加深对于算 ...
- 读书笔记:《流畅的Python》第五章 一等函数
# 一等对象/一等函数 ''' 1.在运行时创建 2.能赋值给变量或数据结构中的元素 3.能作为函数的参数传给函数 4.能作为函数的返回值返回结果 '''# 函数对象本身时function对象的实例d ...
- 读书笔记——《图解TCP/IP》【上】
<图解TCP/IP>第5版 -- [日]竹下隆史 村山公保 荒井透 著 乌尼日其其格 译 前半本读书笔记 图解TCP/IP Ch1 网络基础知识 传输方式的分类 地址 网络的构成要素 Ch ...
- python源码剖析读书笔记总结_《Python源码剖析》读书笔记:内存垃圾回收
Python内存回收的基石是引用计数,"当一个对象的引用被创建或复制时,对象的引用技术加1:当一个对象的引用被销毁时,对象的引用技术减1",如果对象的引用计数减少为0,将对象的所占 ...
- 读书笔记_图解HTTP
图解HTTP 上野宣2014年 阅读顺序应为:<图解HTTP>-><HTTP权威指南>-><TCP/IP详解> <图解HTTP>下载地址: ...
- 读书笔记:《流畅的Python》第4章 文本和字节序列
# 第四章 文本和字节序列"""内容提要:1.Unicode字符串2.二进制序列3.在二者之间转换使用的编码4.字符/码位/字节表述5.bytes/bytearray/m ...
- 读书笔记(十)——python简单爬取企查查网企业信息,并以excel格式存储
2019独角兽企业重金招聘Python工程师标准>>> 今天这个小爬虫是应朋友,帮忙写的一个简单的爬虫,目的是爬取企查查这个网站的企业信息. 编程最终要的就是搭建编程环境,这里我们的 ...
- python读书笔记2000_流畅的Python读书笔记
特殊方法的存在是为了Python解释器调用的,你自己并不需要去调用他们,比如说my_object.len()这种写法是没有的,应该使用len(my_object).在使用len(my_object)的 ...
最新文章
- word顶部有一道线_为什么顶角线不会过时?文章内容告诉你
- centos8.2安装mysql_为CentOS 8操作系统安装MySQL的方法,以安装MySQL 8为例
- js基本函数和基本方法
- Python爬虫自动下载音乐(网易)
- java中this代表什么
- 大数据之Hadoop3.x模板虚拟机配置图解
- uml点餐系统活动图_大学生网上订餐系统--UML建模
- 初识ubuntu 安装steam
- mac系统如何修改默认打开方式
- xp隐藏桌面计算机图标不见了怎么办,XP系统桌面IE图标不见了怎么办?IE图标消失了怎么恢复?...
- win7休眠设置在哪里_win7系统休眠功能如何关闭 win7系统休眠功能关闭步骤【图解】...
- 挖掘有多深,舞弊就有多大,孙德顺敛财10亿,看图计算如何穿透与识别多层嵌套影子公司!...
- Java编程:通过获取当前时间,判断今天星期几
- 推流(RTSP/RTMP)
- 计算机从应用上看向那个方向发展,(总)2019年6月一级Ms Office试题
- (1)简单易学—— 人脸检测 Tensorflow_MTCNN模型训练详细步骤(纯干货,适用于windows和ubuntu系统)
- 计算机网络 - linux - 配置静态ip地址(NAT模式)
- BottomSheetDialog禁止下滑关闭
- 2021威海高考成绩查询电话,2021威海高考成绩查询系统
- C语言程序设计--结构体--计算该日是在本年中是第几天