撒地方大神

https://pythonav.com/index/

1.对列表进行过滤
li = [{"id":3}, {"id":1}, {"id":5}]
filter_list = filter(lambda item: item.get("id")> 3, li)
for item in filter_list:
    print(item.get("id"))
	
	
2.写一个类似于字段用法的类
class RepeatDic(object):
    def __init__(self):
        self.__li = []

    def __getitem__(self, item):
        filter_items = filter(lambda x: x[0] == item, self.__li)
        return [i[1] for i in filter_items]

    def __setitem__(self, key, value):
        self.__li.append((key, value))

    def __delitem__(self, key):
        self.__li = [item for item in self.__li if item[0] != key]


obj = RepeatDic()

obj["haha"] = "heihei"
print(obj["haha"])
obj["haha"] = "hehe"
print(obj["haha"])
del obj["haha"]
print(obj["haha"])

3.二分法查找
li = range(0, 30,  2)
def mid_find(li, left, right, val):
    if right >= left:
        mid = (left + right) // 2
        if li[mid] > val:
            right = mid - 1
            return mid_find(li, left, right, val)
        elif li[mid] < val:
            left = mid + 1
            return mid_find(li, left, right, val)
        else:
            return mid
    else:
        return None

要点:左右边界移动,碰头时终止循环
def mid_find2(li, val):
    left = 0
    right = len(li) - 1
    while left <= right:
        mid = (left + right) // 2
        if li[mid] > val:
            right = mid - 1
        elif li[mid] < val:
            left = mid + 1
        else:
            return mid

ret = mid_find2(li, 120)
print(ret)



4.一个大小为100G的文件etl_log.txt, 要读取文件中的内容, 写出具体过程代码?
要点:指定字节数读,弄成一个生成器
def readFile(file_path):
    with open(file_path, "r", encoding="utf-8") as f:
        while 1:
            block = f.read(1024)
            if block:
                yield block
            else:
                return

it = readFile("run.py")
for item in it:
    print(item)

5.写一个上下文管理类
class NewFile(object):
    def __init__(self, path):
        self.path = path
        self.__file_handler  = None

    def __exit__(self, exc_type, exc_val, exc_tb):
        if self.__file_handler:
            print("close")
            self.__file_handler.close()

    def __enter__(self):
        self.__file_handler = open(self.path, 'r', encoding="utf-8")
        print("open")
        return self.__file_handler

with NewFile("run.py") as f:
    print(f.read())
	
6.冒泡排序
要点:内层循环时时比较,时时交换
def bubble_sort(li):
    for i in range(len(li)-1):  #第i趟
        exchange = False
        for j in range(len(li)-i-1):
            if li[j] > li[j+1]:
                li[j], li[j+1] = li[j+1], li[j]
                exchange = True
        if not exchange:  #这里属于优化,也就是如果这次循环中没有发生一次交换,那么此时已经是有序的
            return
 
 
li = list(range(10000))
random.shuffle(li)
 
bubble_sort(li)

7.后序遍历,我称之为深度优先遍历
要点:深度优先 一般都可以用栈来实现,需要确定pop的条件,以及做好pop后的标识
# 非递归方式
def find(root_node):
    li = []
    stack = []
    stack.append([root_node,[root_node.lchild, root_node.rchild]])
    while len(stack) > 0:
        currNodeItem = stack[-1]
        if currNodeItem[1][0] is None and currNodeItem[1][1] is None:
            #当前是叶子节点,输出到列表
            li.append(currNodeItem[0].data)
            # 标志父亲节点这个儿子已经输出
            if len(stack) > 1: #考虑如果root节点就是叶子节点
                parentNodeItem = stack[-2]
                childs = parentNodeItem[1]
                for index,child in enumerate(childs):
                    if currNodeItem[0] == child:
                        childs[index] = None
                        break
            # 把栈里的数据移除掉
            stack.pop()
        elif currNodeItem[1][0] is not None: #有左儿子,添加左儿子
            lchild = currNodeItem[1][0]
            stack.append([lchild, [lchild.lchild, lchild.rchild]])
        else: # 没有左儿子情况下,有右儿子,添加右儿子
            rchild = currNodeItem[1][1]
            stack.append([rchild, [rchild.lchild, rchild.rchild]])

    return ','.join(li)

print(find(root)) #B,D,C,A,F,G,E  对应后序遍历

# 递归遍历
def post_order(root):
    if root:
        post_order(root.lchild)
        post_order(root.rchild)
        print(root.data, end=',')

8.广度优先,层级遍历
要点:使用 队列,一般出队,一边把儿子入队
def find(root):
    from collections import deque
    que = deque()
    que.append(root)
    while len(que) > 0:
        currentNode = que.popleft()
        print(currentNode.data, end=',')
        if currentNode.lchild:
            que.append(currentNode.lchild)
        if currentNode.rchild:
            que.append(currentNode.rchild)

find(root)

9.选择排序
# 选择排序,你就记成 选择性最小下标来排序
# 每次循环取最开始那个值的索引 作为最小值索引
def select_sort(li):
    for i in range(len(li)-1): #第几趟
        min_loc = i
        change_flag = False
        for j in range(i+1, len(li)):
            if li[j] < li[min_loc]:
                change_flag = True
                min_loc = j
        if not change_flag:
            return
        li[i], li[min_loc] = li[min_loc], li[i]

li = [2,5,4,58,1,175,1,4]
select_sort(li)
print(li)


10.插入排序
#记录手里牌的下标和抓到牌的值,利用冒泡的方式去把抓到牌往左冒
#刚开始手里有一张牌,
def insert_sort(li):
    for i in range(1, len(li)): #摸到牌的下标
        tmp = li[i]
        j = i - 1 #手牌下标
        while j >= 0 and tmp < li[j]:
            li[j+1] = li[j]
            j-=1
        li[j+1] = tmp

11.快速排序
#快排是分而治之的思想
# 要点:递归终止条件就是上下边界碰头, 分区时维护两个指标指针,一个追整个数据,一个追踪小于的标志的值的个数
# 标志值可以去最后的一位数, 两指针出现不相等时,要把大的值交换到 靠右边,  在最后要把标志值交换一下
def quickSort(li, p, r):
    if p>=r:return
    q = partition(li,p,r)
    quickSort(li,p,q-1)
    quickSort(li,q+1,r)

def partition(li, p, r):
    '''

    :param li: 数据列表
    :param p: 数据区间的上标
    :param r: 数据区间的下标
    :return:
    '''
    pivot = li[r] #定义分区点为最后位置的元素
    i = p # 定义 i 指针, 用于追踪小于标志值
    j = p
    while j <= r: # 循环遍历 j 指针
        if li[j] < pivot:
            if j != i: # 如果出现i j不相等,说明比标志值的大的值出现,开始实时把这个值交换到右边
                swap(li, i, j)
            i+=1
        j+=1
    swap(li, i, r) #当p和r碰头时,循环终止,把标志值交换到  小于标志值的分界处
    return i

def swap(li, i, j):
    li[i], li[j] = li[j],li[i]

li = [42,4,4651,47,785,2,7,2,485]
quickSort(li,0, len(li)-1)
print(li)


12.Python 一个数如果恰好等于它的因子之和,这个数就称为"完数"。例如6=1+2+3.编程找出1000以内的所有完数
for i in range(2, 1001):
    m = []
    for j in range(1,i):
        if i % j == 0:
            m.append(j)
    if sum(m) == i:
        print(i)

13.单例模式
class singleton(object):
    __instance = None

    def __new__(cls, *args, **kwargs):
        if cls.__instance is None:
            cls.__instance = object.__new__(cls, *args, **kwargs)
        return cls.__instance

a = singleton()
b  = singleton()
print(id(a), id(b))

14.99乘法表
for row in range(1,10):
    for col in range(1, row+1):
        print("%s * %s = %s"%(col, row, col*row), end="  ")
    print()

15.看代码 写结果
https://zhuanlan.zhihu.com/p/23582996?refer=passer

16.类继承查找循环
要点:python2的经典类和新式类(继承了object) 都是按照深度优先查找类,只不过在查过中,有个细节不一样就是
遇到 重复类的保留情况,经典类是去掉后面的保留第一个,而新式类则是去掉前面的保留最后一个
python3的C3 MRO算法有点复杂,这个我没怎么去了解
一般5到6个类的继承关系我直接去看,一旦比这复杂,我会直接调用__mro__属性来查看
class A(object):
    def __init__(self):
        print("enter A")
        super(A, self).__init__()
        print("leave A")

class B(object):
    def __init__(self):
        print("enter B")
        super(B, self).__init__()
        print("leave B")

class C(A):
    def __init__(self):
        print("enter C")
        super(C, self).__init__()
        print("leave C")

class D(A):
    def __init__(self):
        print("enter D")
        super(D, self).__init__()
        print("leave D")

class E(B,C):
    def __init__(self):
        print("enter E")
        super(E, self).__init__()
        print("leave E")

class F(E,D):
    def __init__(self):
        print("enter F")
        super(F, self).__init__()
        print("leave F")

# print(F.__mro__)

16.二叉树的最大深度
from collections import deque
class Solution(object):
    def preorderTraversal(self, root):
        """
        :type root: TreeNode
        :rtype: List[int]
        """
        if not root: return 0

        queue = deque()
        queue.append(root)
        depth = 0

        while queue:
            size = len(queue)
            for i in range(size):
                curr = queue.popleft()
                if curr.left:
                    queue.append(curr.left)
                if curr.right:
                    queue.append(curr.right)
            depth += 1
        return depth

obj = Solution()
ret = obj.preorderTraversal(e)
print(ret)

套路系列:
    刷二叉树

    1.动态规划
        场景:动态规划问题的一般形式就是求最值
        特点:本质就是穷举,存在重叠子问题,可以通过备忘录和DPtable来优化穷举过程
            并且一定存在最优子结构,怎么穷举,就需要列出状态转移方程
        动态规划三要素:重叠子问题,最优子结构,状态转移方程(这步是最难的)
        过程:明确「状态」 -> 定义 dp 数组/函数的含义 -> 明确「选择」-> 明确 base case
            通过DP table实现自底向上, 备忘录、DP table 就是在追求“如何聪明地穷举”。⽤空间换时间的思路
        
        先确定「状态」,也就是原问题和⼦问题中变化的变量。由于硬币数量⽆
            限,所以唯⼀的状态就是⽬标⾦额 amount 。
        然后确定 dp 函数的定义:当前的⽬标⾦额是 n ,⾄少需要 dp(n) 个硬
            币凑出该⾦额
        然后确定「选择」并择优,也就是对于每个状态,可以做出什么选择改变当
            前状态。具体到这个问题,⽆论当的⽬标⾦额是多少,选择就是从⾯额列表
            coins 中选择⼀个硬币,然后⽬标⾦额就会减少
        最后明确 base case,显然⽬标⾦额为 0 时,所需硬币数量为 0;当⽬标⾦额
            ⼩于 0 时,⽆解,返回 -1
        
        # 伪码框架
        def coinChange(coins: List[int], amount: int):
        # 定义:要凑出⾦额 n,⾄少要 dp(n) 个硬币
        def dp(n):
        # 做选择,选择需要硬币最少的那个结果
        for coin in coins:
        res = min(res, 1 + dp(n - coin))
        return res
        # 我们要求的问题是 dp(amount)
        return dp(amount)
        
        
        int coinChange(vector<int>& coins, int amount) {
            // 数组⼤⼩为 amount + 1,初始值也为 amount + 1
            vector<int> dp(amount + 1, amount + 1);
            // base case
            dp[0] = 0;
            for (int i = 0; i < dp.size(); i++) {
                // 内层 for 在求所有⼦问题 + 1 的最⼩值
                for (int coin : coins) {
                    // ⼦问题⽆解,跳过
                    if (i - coin < 0) continue;
                    dp[i] = min(dp[i], 1 + dp[i - coin]);
                }
            }
            return (dp[amount] == amount + 1) ? -1 : dp[amount];
        }
        
    2.回溯算法(DFS)
        解决⼀个回溯问题(一般常见全排列问题,纯暴力穷举),实际上就是⼀个决策树的遍历过程。你只需要思考 3 个问题:
        1、路径:也就是已经做出的选择。
        2、选择列表:也就是你当前可以做的选择。
        3、结束条件:也就是到达决策树底层,⽆法再做选择的条件。
    
        # 多叉树的前序遍历和后序遍历
        result = []
        def backtrack(路径, 选择列表):
            if 满⾜结束条件:
                result.add(路径)
                return
            for 选择 in 选择列表:
                做选择
                backtrack(路径, 选择列表)
                撤销选择
        
        List<List<Integer>> res = new LinkedList<>();
        /* 主函数,输⼊⼀组不重复的数字,返回它们的全排列 */
        List<List<Integer>> permute(int[] nums) {
            // 记录「路径」
            LinkedList<Integer> track = new LinkedList<>();
            backtrack(nums, track);
            return res;
        }
        
        // 路径:记录在 track 中
        // 选择列表:nums 中不存在于 track 的那些元素
        // 结束条件:nums 中的元素全都在 track 中出现
        void backtrack(int[] nums, LinkedList<Integer> track) {
            // 触发结束条件
            if (track.size() == nums.length) {
                res.add(new LinkedList(track));
                return;
            }
            
            for (int i = 0; i < nums.length; i++) {
                // 排除不合法的选择
                if (track.contains(nums[i]))
                    continue;
                // 做选择
                track.add(nums[i]);
                // 进⼊下⼀层决策树
                backtrack(nums, track);
                // 取消选择
                track.removeLast();
            }
        }
    
    3.BFS算法
        场景:抽象成图,比如求最短路径(二叉树的最小高度,打开密码锁的最少步数)
        一般是用队列维护,空间复杂度比DFS复杂,一般还需维护一个数据集,防止走回头路
        
        框架:
        // 计算从起点 start 到终点 target 的最近距离
        int BFS(Node start, Node target) {
            Queue<Node> q; // 核⼼数据结构
            Set<Node> visited; // 避免⾛回头路
            q.offer(start); // 将起点加⼊队列
            visited.add(start);
            int step = 0; // 记录扩散的步数
            while (q not empty) {
                int sz = q.size();
                /* 将当前队列中的所有节点向四周扩散 */
                for (int i = 0; i < sz; i++) {
                    Node cur = q.poll();
                    /* 划重点:这⾥判断是否到达终点 */
                    if (cur is target)
                        return step;
                    /* 将 cur 的相邻节点加⼊队列 */
                    for (Node x : cur.adj())
                        if (x not in visited) {
                            q.offer(x);
                            visited.add(x);
                        }
                }
                /* 划重点:更新步数在这⾥ */
                step++;
            }
        }
    
    4.滑动窗口算法(双指针问题)
        场景:子串,子数组问题
        维护⼀个窗⼝,不断滑动,然后更新答案
        
        框架:
        int left = 0, right = 0;
        while (right < s.size()) {`
            // 增⼤窗⼝
            window.add(s[right]);
            right++;
            while (window needs shrink) {
                // 缩⼩窗⼝
                window.remove(s[left]);
                left++;
            }
        }
        
    5.二分法查找

撒地方大神

撒地方大神

 撒地方大神

哈哈