Skip to content

Commit

Permalink
add: add resource
Browse files Browse the repository at this point in the history
  • Loading branch information
Your Name committed Dec 28, 2023
0 parents commit 08380dd
Show file tree
Hide file tree
Showing 140 changed files with 9,066 additions and 0 deletions.
4 changes: 4 additions & 0 deletions .github/FUNDING.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# These are supported funding model platforms

github: [PegasusWang]
custom: ["https://www.paypal.me/pegasuswang"]
34 changes: 34 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# media
*.mp4
*.mov
*.avi


# python
__pycache__/
*.py[cod]
*.pyc
*$py.class
.python-version


# vim
.vimrc
.lvimrc
.*.sw[a-z]
*.un~
Session.vim

# cache
.cache
.tmp
.idea

# mkdocs
site/

# vscode
.vscode/

# pytest
.pytest_cache/
21 changes: 21 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2019 PegasusWang

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
9 changes: 9 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
push:
git push origin master

serve:
mkdocs serve

publish:
git push origin master
mkdocs gh-deploy
347 changes: 347 additions & 0 deletions README.md

Large diffs are not rendered by default.

11 changes: 11 additions & 0 deletions docs/00_课程简介之笨方法学算法/why_and_how_to_learn.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# 什么是算法和数据结构?

程序 = 算法 + 数据结构

算法(Algorithm):是指解题方案的准确而完整的描述,是一系列解决问题的清晰指令,算法代表着用系统的方法描述解决问题的策略机制。也就是说,能够对一定规范的输入,在有限时间内获得所要求的输出。

数据结构(Data Structures):是计算机存储和组织数据的一种方式,可以用来高效地处理数据。

- # QA
- 你了解 redis 吗,你知道它有哪几个常用的数据结构吗?你知道它的底层实现方式吗?
- [redis](https://www.cnblogs.com/powertoolsteam/p/redis.html)
44 changes: 44 additions & 0 deletions docs/01_抽象数据类型和面向对象编程/ADT_OOP.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# Python 一切皆对象(面向对象编程)


在后面实现新的数据类型时,使用 python 的 class 实现,它包含属性和方法。
属性一般是使用某种特定的数据类型,而方法一般是对属性的操作。后续实例不会使用继承等特性。


# 什么是抽象数据类型 ADT

实际上 python 内置的 list 就可以看成一种抽象数据类型。

ADT: Abstract Data Type,抽象数据类型,我们在组合已有的数据结构来实现一种新的数据类型, ADT 定义了类型的数据和操作。

我们以抽象一个背包(Bag) 数据类型来说明,背包是一种容器类型,我们可以给它添加东西,也可以移除东西,并且我们想知道背包里
有多少东西。于是我们可以定义一个新的数据类型叫做 Bag(data;method).

```py
class Bag:
""" 背包类型 """
pass
```


# 实现一个 Bag ADT
视频中我们将使用 python 的 class 来实现一个新的容器类型叫做 Bag。


# 实现 ADT 我们应该注意什么?
- 如何选用恰当的数据结构作为存储?
- 选取的数据结构能否满足 ADT 的功能需求
- 实现效率如何?


# QA:
- python 的魔术方法`__len__` ,方法
- 使用 pytest 运行单元测试,保证我们实现的数据结构和算法是正确的。

# 延伸阅读:

[数据结构与算法--ADT](http://www.atjiang.com/data-structures-using-python-ADT/)

[http://www.nhu.edu.tw/~chun/CS-ch12-Abstract%20Data%20Types.pdf](http://www.nhu.edu.tw/~chun/CS-ch12-Abstract%20Data%20Types.pdf)

[pytest](https://www.jianshu.com/p/932a4d9f78f8)
43 changes: 43 additions & 0 deletions docs/01_抽象数据类型和面向对象编程/bag_adt.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# coding: utf8


class Bag(object):

def __init__(self, maxsize=10):
self.maxsize = maxsize
self._items = list()

def add(self, item):
if len(self) >= self.maxsize:
raise Exception('Full')
self._items.append(item)

def remove(self, item):
self._items.remove(item)

def __len__(self):
return len(self._items)

def __iter__(self):
for item in self._items:
yield item


def test_bag():
bag = Bag()

bag.add(1)
bag.add(2)
bag.add(3)

assert len(bag) == 3

bag.remove(3)
assert len(bag) == 2

for i in bag:
print(i)


if __name__ == '__main__':
test_bag()
55 changes: 55 additions & 0 deletions docs/02_数组和列表/array_and_list.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# 线性结构
本节我们从最简单和常用的线性结构开始,并结合 Python 语言本身内置的数据结构和其底层实现方式来讲解。
虽然本质上数据结构的思想是语言无关的,但是了解 Python 的实现方式有助于你避免一些坑。

我们会在代码中注释出操作的时间复杂度。


# 数组 array

数组是最常用到的一种线性结构,其实 python 内置了一个 array 模块,但是大部人甚至从来没用过它。
Python 的 array 是<span style="border-bottom:2px solid black;">内存连续</span>、存储的都是<span style="border-bottom:2px solid black;">同一数据类型</span>的结构,而且只能存数值和字符。

array 的文档:https://docs.python.org/2/library/array.html

推荐用 numpy.array)

本章最后用 list 来实现一个固定长度、并且支持所有 Python 数据类型的数组 Array.


# 列表 list
如果你学过 C++,list 其实和 C++ STL(标准模板库)中的 vector 很类似,它可能是你的 Python 学习中使用最频繁的数据结构之一。
这里我们不再去自己实现 list,因为这是个 Python 提供的非常基础的数据类型,我会在视频中讲解它的工作方式和内存分配策略,
避免使用过程中碰到一些坑。当然如果你有毅力或者兴趣的了解底层是如何实现的,可以看看 cpython 解释器的具体实现。
[O(1)](http://gongsichuang.com/news/post/TygxKeS7gOS5iOaEj+aAnQ==.html#:~:text=O%20%281%29%E6%98%AF%E6%9C%80%E4%BD%8E%E7%9A%84%E6%97%B6%E7%A9%BA%E5%A4%8D%E6%9D%82%E5%BA%A6%EF%BC%8C%E4%B9%9F%E5%B0%B1%E6%98%AF%E8%80%97%E6%97%B6%2F%E8%80%97%E7%A9%BA%E9%97%B4%E4%B8%8E%E8%BE%93%E5%85%A5%E6%95%B0%E6%8D%AE%E5%A4%A7%E5%B0%8F%E6%97%A0%E5%85%B3%EF%BC%8C%E6%97%A0%E8%AE%BA%E8%BE%93%E5%85%A5%E6%95%B0%E6%8D%AE%E5%A2%9E%E5%A4%A7%E5%A4%9A%E5%B0%91%E5%80%8D%EF%BC%8C%E8%80%97%E6%97%B6%2F%E8%80%97%E7%A9%BA%E9%97%B4%E9%83%BD%E4%B8%8D%E5%8F%98%E3%80%82,%E5%93%88%E5%B8%8C%E7%AE%97%E6%B3%95%E5%B0%B1%E6%98%AF%E5%85%B8%E5%9E%8B%E7%9A%84O%20%281%29%E6%97%B6%E9%97%B4%E5%A4%8D%E6%9D%82%E5%BA%A6%EF%BC%8C%E6%97%A0%E8%AE%BA%E6%95%B0%E6%8D%AE%E8%A7%84%E6%A8%A1%E5%A4%9A%E5%A4%A7%EF%BC%8C%E9%83%BD%E5%8F%AF%E4%BB%A5%E5%9C%A8%E4%B8%80%E6%AC%A1%E8%AE%A1%E7%AE%97%E5%90%8E%E6%89%BE%E5%88%B0%E7%9B%AE%E6%A0%87%EF%BC%88%E4%B8%8D%E8%80%83%E8%99%91%E5%86%B2%E7%AA%81%E7%9A%84%E8%AF%9D%EF%BC%89%E3%80%82)

操作 | 平均时间复杂度 |
--------------------------------------|----------------|
list[index] | O(1) |
list.append | O(1) |
list.insert | O(n) |
list.pop(index), default last element | O(1) |
list.remove | O(n) |

![](./list.png)

# 用 list 实现 Array ADT
讲完了 list 让我们来实现一个定长的数组 Array ADT,在其他一些语言中,内置的数组结构就是定长的。
这里我们会使用 list 作为 Array 的一个成员(代理)。具体请参考视频讲解和代码示例,后边我们会使用到这个 Array 类。


# 小问题
- 线性结构的查找,删除,访问一个元素的平均时间复杂度(简单地理解为一个操作需要的平均步骤)
- list 内存重新分配的时候为什么要有冗余?不会浪费空间吗?
- 当你频繁的pop list 的第一个元素的时候,会发生什么?如果需要频繁在两头增添元素,你知道更高效的数据结构吗?后边我们会讲到


# 延伸阅读

[Python list implementation](https://www.laurentluce.com/posts/python-list-implementation/)

[https://github.com/python/cpython/blob/master/Objects/listobject.c](https://github.com/python/cpython/blob/master/Objects/listobject.c)


# 勘误
视频里的 Array.clear 方法有误。应该是 `for i in range(len(self._items))`,已经在后续所有使用到 Array 的代码里修正
45 changes: 45 additions & 0 deletions docs/02_数组和列表/array_and_list.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# -*- coding: utf-8 -*-

# https://docs.python.org/2/library/array.html
from array import array # python 提供的比较原始的 array 类


arr = array('u', 'asdf')

print(arr[0], arr[1], arr[2], arr[3])


# 实现定长的 Array ADT,省略了边界检查等

class Array(object):

def __init__(self, size=32):
self._size = size
self._items = [None] * size

def __getitem__(self, index):
return self._items[index]

def __setitem__(self, index, value):
self._items[index] = value

def __len__(self):
return self._size

def clear(self, value=None):
for i in range(len(self._items)):
self._items[i] = value

def __iter__(self):
for item in self._items:
yield item


def test_array():
size = 10
a = Array(size)
a[0] = 1
assert a[0] == 1
assert len(a) == 10

# py.test array_and_list.py
Binary file added docs/02_数组和列表/list.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
121 changes: 121 additions & 0 deletions docs/03_链表/double_link_list.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
# -*- coding: utf-8 -*-


class Node(object):
__slots__ = ('value', 'prev', 'next') # save memory

def __init__(self, value=None, prev=None, next=None):
self.value, self.prev, self.next = value, prev, next


class CircularDoubleLinkedList(object):
"""循环双端链表 ADT
多了个循环其实就是把 root 的 prev 指向 tail 节点,串起来
"""

def __init__(self, maxsize=None):
self.maxsize = maxsize
node = Node()
node.next, node.prev = node, node
self.root = node
self.length = 0

def __len__(self):
return self.length

def headnode(self):
return self.root.next

def tailnode(self):
return self.root.prev

def append(self, value): # O(1), 你发现一般不用 for 循环的就是 O(1),有限个步骤
if self.maxsize is not None and len(self) >= self.maxsize:
raise Exception('LinkedList is Full')
node = Node(value=value)
tailnode = self.tailnode() or self.root

tailnode.next = node
node.prev = tailnode
node.next = self.root
self.root.prev = node
self.length += 1

def appendleft(self, value):
if self.maxsize is not None and len(self) >= self.maxsize:
raise Exception('LinkedList is Full')
node = Node(value=value)
if self.root.next is self.root: # empty
node.next = self.root
node.prev = self.root
self.root.next = node
self.root.prev = node
else:
node.prev = self.root
headnode = self.root.next
node.next = headnode
headnode.prev = node
self.root.next = node
self.length += 1

def remove(self, node): # O(1),传入node 而不是 value 我们就能实现 O(1) 删除
"""remove
:param node # 在 lru_cache 里实际上根据key 保存了整个node:
"""
if node is self.root:
return
else: #
node.prev.next = node.next
node.next.prev = node.prev
self.length -= 1
return node

def iter_node(self):
if self.root.next is self.root:
return
curnode = self.root.next
while curnode.next is not self.root:
yield curnode
curnode = curnode.next
yield curnode

def __iter__(self):
for node in self.iter_node():
yield node.value

def iter_node_reverse(self):
"""相比单链表独有的反序遍历"""
if self.root.prev is self.root:
return
curnode = self.root.prev
while curnode.prev is not self.root:
yield curnode
curnode = curnode.prev
yield curnode


def test_double_link_list():
dll = CircularDoubleLinkedList()
assert len(dll) == 0

dll.append(0)
dll.append(1)
dll.append(2)

assert list(dll) == [0, 1, 2]

assert [node.value for node in dll.iter_node()] == [0, 1, 2]
assert [node.value for node in dll.iter_node_reverse()] == [2, 1, 0]

headnode = dll.headnode()
assert headnode.value == 0
dll.remove(headnode)
assert len(dll) == 2
assert [node.value for node in dll.iter_node()] == [1, 2]

dll.appendleft(0)
assert [node.value for node in dll.iter_node()] == [0, 1, 2]


if __name__ == '__main__':
test_double_link_list()
Loading

0 comments on commit 08380dd

Please sign in to comment.