【搬运自用】Python重要知识点+数据结构和算法- 100天从新手到大师Day16(GitHub)

Python语言进阶

原作者:骆昊
https://github.com/jackfrued/Python-100-Days
更多内容欢迎查看并订阅专栏’Python从新手到大师’

重要知识点

  • 生成式(推导式)的用法

    prices = {
        'AAPL': 191.88,
        'GOOG': 1186.96,
        'IBM': 149.24,
        'ORCL': 48.44,
        'ACN': 166.89,
        'FB': 208.09,
        'SYMC': 21.29
    }
    # 用股票价格大于100元的股票构造一个新的字典
    prices2 = {key: value for key, value in prices.items() if value > 100}
    print(prices2)
    

    说明:生成式(推导式)可以用来生成列表、集合和字典。

  • 嵌套的列表的坑

    names = ['关羽', '张飞', '赵云', '马超', '黄忠']
    courses = ['语文', '数学', '英语']
    # 录入五个学生三门课程的成绩
    # 错误 - 参考http://pythontutor.com/visualize.html#mode=edit
    # scores = [[None] * len(courses)] * len(names)
    scores = [[None] * len(courses) for _ in range(len(names))]
    for row, name in enumerate(names):
        for col, course in enumerate(courses):
            scores[row][col] = float(input(f'请输入{name}的{course}成绩: '))
            print(scores)
    

    Python Tutor - VISUALIZE CODE AND GET LIVE HELP

  • heapq模块(堆排序)

    """
    从列表中找出最大的或最小的N个元素
    堆结构(大根堆/小根堆)
    """
    import heapq
    
    list1 = [34, 25, 12, 99, 87, 63, 58, 78, 88, 92]
    list2 = [
        {'name': 'IBM', 'shares': 100, 'price': 91.1},
        {'name': 'AAPL', 'shares': 50, 'price': 543.22},
        {'name': 'FB', 'shares': 200, 'price': 21.09},
        {'name': 'HPQ', 'shares': 35, 'price': 31.75},
        {'name': 'YHOO', 'shares': 45, 'price': 16.35},
        {'name': 'ACME', 'shares': 75, 'price': 115.65}
    ]
    print(heapq.nlargest(3, list1))
    print(heapq.nsmallest(3, list1))
    print(heapq.nlargest(2, list2, key=lambda x: x['price']))
    print(heapq.nlargest(2, list2, key=lambda x: x['shares']))
    
  • itertools模块

    """
    迭代工具模块
    """
    import itertools
    
    # 产生ABCD的全排列
    itertools.permutations('ABCD')
    # 产生ABCDE的五选三组合
    itertools.combinations('ABCDE', 3)
    # 产生ABCD和123的笛卡尔积
    itertools.product('ABCD', '123')
    # 产生ABC的无限循环序列
    itertools.cycle(('A', 'B', 'C'))
    
  • collections模块

    常用的工具类:

    • namedtuple:命令元组,它是一个类工厂,接受类型的名称和属性列表来创建一个类。
    • deque:双端队列,是列表的替代实现。Python中的列表底层是基于数组来实现的,而deque底层是双向链表,因此当你需要在头尾添加和删除元素时,deque会表现出更好的性能,渐近时间复杂度为 O ( 1 ) O(1) O(1)
    • Counterdict的子类,键是元素,值是元素的计数,它的most_common()方法可以帮助我们获取出现频率最高的元素。Counterdict的继承关系我认为是值得商榷的,按照CARP原则,Counterdict的关系应该设计为关联关系更为合理。
    • OrderedDictdict的子类,它记录了键值对插入的顺序,看起来既有字典的行为,也有链表的行为。
    • defaultdict:类似于字典类型,但是可以通过默认的工厂函数来获得键对应的默认值,相比字典中的setdefault()方法,这种做法更加高效。
    """
    找出序列中出现次数最多的元素
    """
    from collections import Counter
    
    words = [
        'look', 'into', 'my', 'eyes', 'look', 'into', 'my', 'eyes',
        'the', 'eyes', 'the', 'eyes', 'the', 'eyes', 'not', 'around',
        'the', 'eyes', "don't", 'look', 'around', 'the', 'eyes',
        'look', 'into', 'my', 'eyes', "you're", 'under'
    ]
    counter = Counter(words)
    print(counter.most_common(3))
    

数据结构和算法

  • 算法:解决问题的方法和步骤

  • 评价算法的好坏:渐近时间复杂度和渐近空间复杂度。

  • 渐近时间复杂度的大O标记:

    • O ( c ) O(c) O(c) 常量时间复杂度 - 布隆过滤器 / 哈希存储
    • O ( l o g 2 n ) O(log_{2}n) O(log2n) 对数时间复杂度 - 折半查找(二分查找)
    • O ( n ) O(n) O(n) 线性时间复杂度 - 顺序查找 / 计数排序
    • O ( n ∗ l o g 2 n ) O(n*log_{2}n) O(nlog2n) 对数线性时间复杂度 - 高级排序算法(归并排序、快速排序)
    • O ( n 2 ) O(n^2) O(n2) 平方时间复杂度 - 简单排序算法(选择排序、插入排序、冒泡排序)
    • O ( n 3 ) O(n^3) O(n3) 立方时间复杂度 - Floyd算法 / 矩阵乘法运算
    • O ( 2 n ) O(2^n) O(2n) 几何级数时间复杂度 - 汉诺塔
    • O ( n ! ) O(n!) O(n!) 阶乘时间复杂度 - 旅行经销商问题 - NPC

    在这里插入图片描述

    在这里插入图片描述

  • 排序算法(选择、冒泡和归并)和查找算法(顺序和折半)

    def select_sort(items, comp=lambda x, y: x < y):
        """简单选择排序"""
        items = items[:]
        for i in range(len(items) - 1):
            min_index = i
            for j in range(i + 1, len(items)):
                if comp(items[j], items[min_index]):
                    min_index = j
            items[i], items[min_index] = items[min_index], items[i]
        return items
    
    def bubble_sort(items, comp=lambda x, y: x > y):
        """冒泡排序"""
        items = items[:]
        for i in range(len(items) - 1):
            swapped = False
            for j in range(len(items) - 1 - i):
                if comp(items[j], items[j + 1]):
                    items[j], items[j + 1] = items[j + 1], items[j]
                    swapped = True
            if not swapped:
                break
        return items
    
    def bubble_sort(items, comp=lambda x, y: x > y):
        """搅拌排序(冒泡排序升级版)"""
        items = items[:]
        for i in range(len(items) - 1):
            swapped = False
            for j in range(len(items) - 1 - i):
                if comp(items[j], items[j + 1]):
                    items[j], items[j + 1] = items[j + 1], items[j]
                    swapped = True
            if swapped:
                swapped = False
                for j in range(len(items) - 2 - i, i, -1):
                    if comp(items[j - 1], items[j]):
                        items[j], items[j - 1] = items[j - 1], items[j]
                        swapped = True
            if not swapped:
                break
        return items
    
    def merge(items1, items2, comp=lambda x, y: x < y):
        """合并(将两个有序的列表合并成一个有序的列表)"""
        items = []
        index1, index2 = 0, 0
        while index1 < len(items1) and index2 < len(items2):
            if comp(items1[index1], items2[index2]):
                items.append(items1[index1])
                index1 += 1
            else:
                items.append(items2[index2])
                index2 += 1
        items += items1[index1:]
        items += items2[index2:]
        return items
    
    
    def merge_sort(items, comp=lambda x, y: x < y):
        return _merge_sort(list(items), comp)
    
    
    def _merge_sort(items, comp):
        """归并排序"""
        if len(items) < 2:
            return items
        mid = len(items) // 2
        left = _merge_sort(items[:mid], comp)
        right = _merge_sort(items[mid:], comp)
        return merge(left, right, comp)
    
    def seq_search(items, key):
        """顺序查找"""
        for index, item in enumerate(items):
            if item == key:
                return index
        return -1
    
    def bin_search(items, key):
        """折半查找"""
        start, end = 0, len(items) - 1
        while start <= end:
            mid = (start + end) // 2
            if key > items[mid]:
                start = mid + 1
            elif key < items[mid]:
                end = mid - 1
            else:
                return mid
        return -1
    
  • 常用算法:

    • 穷举法 - 又称为暴力破解法,对所有的可能性进行验证,直到找到正确答案。
    • 贪婪法 - 在对问题求解时,总是做出在当前看来
    • 最好的选择,不追求最优解,快速找到满意解。
    • 分治法 - 把一个复杂的问题分成两个或更多的相同或相似的子问题,再把子问题分成更小的子问题,直到可以直接求解的程度,最后将子问题的解进行合并得到原问题的解。
    • 回溯法 - 回溯法又称为试探法,按选优条件向前搜索,当搜索到某一步发现原先选择并不优或达不到目标时,就退回一步重新选择。
    • 动态规划 - 基本思想也是将待求解问题分解成若干个子问题,先求解并保存这些子问题的解,避免产生大量的重复运算。

    穷举法例子:百钱百鸡和五人分鱼。

    # 公鸡5元一只 母鸡3元一只 小鸡1元三只
    # 用100元买100只鸡 问公鸡/母鸡/小鸡各多少只
    for x in range(20):
        for y in range(33):
            z = 100 - x - y
            if 5 * x + 3 * y + z // 3 == 100 and z % 3 == 0:
                print(x, y, z)
    
    # A、B、C、D、E五人在某天夜里合伙捕鱼 最后疲惫不堪各自睡觉
    # 第二天A第一个醒来 他将鱼分为5份 扔掉多余的1条 拿走自己的一份
    # B第二个醒来 也将鱼分为5份 扔掉多余的1条 拿走自己的一份
    # 然后C、D、E依次醒来也按同样的方式分鱼 问他们至少捕了多少条鱼
    fish = 6
    while True:
        total = fish
        enough = True
        for _ in range(5):
            if (total - 1) % 5 == 0:
                total = (total - 1) // 5 * 4
            else:
                enough = False
                break
        if enough:
            print(fish)
            break
        fish += 5
    

    贪婪法例子:假设小偷有一个背包,最多能装20公斤赃物,他闯入一户人家,发现如下表所示的物品。很显然,他不能把所有物品都装进背包,所以必须确定拿走哪些物品,留下哪些物品。

    名称价格(美元)重量(kg)
    电脑20020
    收音机204
    17510
    花瓶502
    101
    油画909
    """
    贪婪法:在对问题求解时,总是做出在当前看来是最好的选择,不追求最优解,快速找到满意解。
    输入:
    20 6
    电脑 200 20
    收音机 20 4
    钟 175 10
    花瓶 50 2
    书 10 1
    油画 90 9
    """
    class Thing(object):
        """物品"""
    
        def __init__(self, name, price, weight):
            self.name = name
            self.price = price
            self.weight = weight
    
        @property
        def value(self):
            """价格重量比"""
            return self.price / self.weight
    
    
    def input_thing():
        """输入物品信息"""
        name_str, price_str, weight_str = input().split()
        return name_str, int(price_str), int(weight_str)
    
    
    def main():
        """主函数"""
        max_weight, num_of_things = map(int, input().split())
        all_things = []
        for _ in range(num_of_things):
            all_things.append(Thing(*input_thing()))
        all_things.sort(key=lambda x: x.value, reverse=True)
        total_weight = 0
        total_price = 0
        for thing in all_things:
            if total_weight + thing.weight <= max_weight:
                print(f'小偷拿走了{thing.name}')
                total_weight += thing.weight
                total_price += thing.price
        print(f'总价值: {total_price}美元')
    
    
    if __name__ == '__main__':
        main()
    

    分治法例子:快速排序

    """
    快速排序 - 选择枢轴对元素进行划分,左边都比枢轴小右边都比枢轴大
    """
    def quick_sort(items, comp=lambda x, y: x <= y):
        items = list(items)[:]
        _quick_sort(items, 0, len(items) - 1, comp)
        return items
    
    
    def _quick_sort(items, start, end, comp):
        if start < end:
            pos = _partition(items, start, end, comp)
            _quick_sort(items, start, pos - 1, comp)
            _quick_sort(items, pos + 1, end, comp)
    
    
    def _partition(items, start, end, comp):
        pivot = items[end]
        i = start - 1
        for j in range(start, end):
            if comp(items[j], pivot):
                i += 1
                items[i], items[j] = items[j], items[i]
        items[i + 1], items[end] = items[end], items[i + 1]
        return i + 1
    

    回溯法例子:骑士巡逻

    """
    递归回溯法:叫称为试探法,按选优条件向前搜索,当搜索到某一步,发现原先选择并不优或达不到目标时,就退回一步重新选择,比较经典的问题包括骑士巡逻、八皇后和迷宫寻路等。
    """
    import sys
    import time
    
    SIZE = 5
    total = 0
    
    
    def print_board(board):
        for row in board:
            for col in row:
                print(str(col).center(4), end='')
            print()
    
    
    def patrol(board, row, col, step=1):
        if row >= 0 and row < SIZE and \
            col >= 0 and col < SIZE and \
            board[row][col] == 0:
            board[row][col] = step
            if step == SIZE * SIZE:
                global total
                total += 1
                print(f'第{total}种走法: ')
                print_board(board)
            patrol(board, row - 2, col - 1, step + 1)
            patrol(board, row - 1, col - 2, step + 1)
            patrol(board, row + 1, col - 2, step + 1)
            patrol(board, row + 2, col - 1, step + 1)
            patrol(board, row + 2, col + 1, step + 1)
            patrol(board, row + 1, col + 2, step + 1)
            patrol(board, row - 1, col + 2, step + 1)
            patrol(board, row - 2, col + 1, step + 1)
            board[row][col] = 0
    
    
    def main():
        board = [[0] * SIZE for _ in range(SIZE)]
        patrol(board, SIZE - 1, SIZE - 1)
    
    
    if __name__ == '__main__':
        main()
    

    动态规划例子:子列表元素之和的最大值。

    说明:子列表指的是列表中索引(下标)连续的元素构成的列表;列表中的元素是int类型,可能包含正整数、0、负整数;程序输入列表中的元素,输出子列表元素求和的最大值,例如:

    输入:1 -2 3 5 -3 2

    输出:8

    输入:0 -2 3 5 -1 2

    输出:9

    输入:-9 -2 -3 -5 -3

    输出:-2

    def main():
        items = list(map(int, input().split()))
        overall = partial = items[0]
        for i in range(1, len(items)):
            partial = max(items[i], partial + items[i])
            overall = max(partial, overall)
        print(overall)
    
    
    if __name__ == '__main__':
        main()
    

    说明:这个题目最容易想到的解法是使用二重循环,但是代码的时间性能将会变得非常的糟糕。使用动态规划的思想,仅仅是多用了两个变量,就将原来 O ( N 2 ) O(N^2) O(N2)复杂度的问题变成了 O ( N ) O(N) O(N)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值